From e3b92fa111ad46a33de0528d6d84b8cf94090524 Mon Sep 17 00:00:00 2001 From: Jerome Lelong Date: Mon, 25 May 2020 16:05:21 +0200 Subject: [PATCH 1/5] Create a dictionary of all bibtex optional fields --- data/bibtex-optional-entries.json | 392 ++++++++++++++++++++++++++++++ 1 file changed, 392 insertions(+) create mode 100644 data/bibtex-optional-entries.json diff --git a/data/bibtex-optional-entries.json b/data/bibtex-optional-entries.json new file mode 100644 index 000000000..e742b6470 --- /dev/null +++ b/data/bibtex-optional-entries.json @@ -0,0 +1,392 @@ +{ + "article": [ + "addendum", + "annotator", + "commentator", + "doi", + "editor", + "eid", + "eprint", + "eprintclass", + "eprinttype", + "issn", + "issue", + "issuesubtitle", + "issuetitle", + "journalsubtitle", + "language", + "month", + "note", + "number", + "origlanguage", + "pages", + "pubstate", + "series", + "subtitle", + "titleaddon", + "translator", + "url", + "urldate", + "version", + "volume" + ], + "book": [ + "addendum", + "afterword", + "annotator", + "chapter", + "commentator", + "doi", + "edition", + "editor", + "eprint", + "eprintclass", + "eprinttype", + "foreword", + "introduction", + "isbn", + "language", + "location", + "mainsubtitle", + "maintitle", + "maintitleaddon", + "month", + "note", + "number", + "origlanguage", + "pages", + "pagetotal", + "part", + "publisher", + "pubstate", + "series", + "subtitle", + "titleaddon", + "translator", + "url", + "urldate", + "volume", + "volumes" + ], + "booklet": [ + "addendum", + "adress", + "chapter", + "doi", + "eprint", + "eprintclass", + "eprinttype", + "howpublished", + "language", + "location", + "month", + "note", + "pages", + "pagetotal", + "pubstate", + "subtitle", + "titleaddon", + "type", + "url", + "urldate" + ], + "conference": [ + "addendum", + "address", + "booksubtitle", + "booktitleaddon", + "chapter", + "doi", + "editor", + "eprint", + "eprintclass", + "eprinttype", + "eventdate", + "eventtitle", + "eventtitleaddon", + "isbn", + "language", + "location", + "mainsubtitle", + "maintitle", + "maintitleaddon", + "month", + "note", + "note.", + "number", + "organization", + "pages", + "part", + "publisher", + "pubstate", + "series", + "subtitle", + "titleaddon", + "url", + "urldate", + "venue", + "volume", + "volumes" + ], + "inbook": [ + "addendum", + "afterword", + "annotator", + "bookauthor", + "booksubtitle", + "booktitleaddon", + "chapter", + "commentator", + "doi", + "edition", + "editor", + "eprint", + "eprintclass", + "eprinttype", + "foreword", + "introduction", + "isbn", + "language", + "location", + "mainsubtitle", + "maintitle", + "maintitleaddon", + "month", + "note", + "number", + "origlanguage", + "pages", + "part", + "publisher", + "pubstate", + "series", + "subtitle", + "titleaddon", + "translator", + "type", + "url", + "urldate", + "volume", + "volumes" + ], + "incollection": [ + "addendum", + "address", + "afterword", + "annotator", + "booksubtitle", + "booktitleaddon", + "chapter", + "commentator", + "doi", + "edition", + "editor", + "eprint", + "eprintclass", + "eprinttype", + "foreword", + "introduction", + "isbn", + "language", + "location", + "mainsubtitle", + "maintitle", + "maintitleaddon", + "month", + "note", + "number", + "origlanguage", + "pages", + "part", + "publisher", + "pubstate", + "series", + "subtitle", + "titleaddon", + "translator", + "type", + "url", + "urldate", + "volume", + "volumes" + ], + "inproceedings": [ + "addendum", + "address", + "booksubtitle", + "booktitleaddon", + "chapter", + "doi", + "editor", + "eprint", + "eprintclass", + "eprinttype", + "eventdate", + "eventtitle", + "eventtitleaddon", + "isbn", + "language", + "location", + "mainsubtitle", + "maintitle", + "maintitleaddon", + "month", + "note", + "number", + "organization", + "pages", + "part", + "publisher", + "pubstate", + "series", + "subtitle", + "titleaddon", + "url", + "urldate", + "venue", + "volume", + "volumes" + ], + "manual": [ + "addendum", + "address", + "chapter", + "doi", + "edition", + "eprint", + "eprintclass", + "eprinttype", + "isbn", + "language", + "location", + "month", + "note", + "number", + "organization", + "pages", + "pagetotal", + "publisher", + "pubstate", + "series", + "subtitle", + "titleaddon", + "type", + "url", + "urldate", + "version", + "year" + ], + "masterthesis": [ + "address", + "month", + "note", + "type" + ], + "misc": [ + "addendum", + "doi", + "eprint", + "eprintclass", + "eprinttype", + "howpublished", + "language", + "location", + "month", + "note", + "organization", + "pubstate", + "subtitle", + "titleaddon", + "type", + "url", + "urldate", + "version", + "year" + ], + "phdthesis": [ + "addendum", + "address", + "chapter", + "doi", + "eprint", + "eprintclass", + "eprinttype", + "isbn", + "language", + "location", + "month", + "note", + "pages", + "pagetotal", + "pubstate", + "subtitle", + "titleaddon", + "type", + "url", + "urldate" + ], + "proceedings": [ + "addendum", + "address", + "chapter", + "doi", + "editor", + "eprint", + "eprintclass", + "eprinttype", + "eventdate", + "eventtitle", + "eventtitleaddon", + "isbn", + "language", + "location", + "mainsubtitle", + "maintitle", + "maintitleaddon", + "month", + "note", + "number", + "organization", + "pages", + "pagetotal", + "part", + "publisher", + "pubstate", + "series", + "subtitle", + "titleaddon", + "url", + "urldate", + "venue", + "volume", + "volumes" + ], + "techreport": [ + "addendum", + "address", + "chapter", + "doi", + "eprint", + "eprintclass", + "eprinttype", + "isrn", + "language", + "location", + "month", + "note", + "number", + "pages", + "pagetotal", + "pubstate", + "subtitle", + "titleaddon", + "type", + "url", + "urldate", + "version" + ], + "unpublished": [ + "month", + "year" + ] +} \ No newline at end of file From 749d78b1fdf026f6895a7268b8394c310ba3b50a Mon Sep 17 00:00:00 2001 From: Jerome Lelong Date: Mon, 25 May 2020 16:05:47 +0200 Subject: [PATCH 2/5] Add bibtex 'conference' type --- data/bibtex-entries.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/data/bibtex-entries.json b/data/bibtex-entries.json index 2d6959ad3..68f1c6cc7 100644 --- a/data/bibtex-entries.json +++ b/data/bibtex-entries.json @@ -18,6 +18,13 @@ "title", "author" ], + "conference": [ + "author", + "title", + "booktitle", + "editor", + "year" + ], "inbook": [ "author", "editor", From b448a98683d1f20eb8f174c8acc3810994b9868c Mon Sep 17 00:00:00 2001 From: Jerome Lelong Date: Tue, 26 May 2020 11:33:53 +0200 Subject: [PATCH 3/5] Add intellisense for optional bibtex fields Intellisense has to triggered manually by ctrl+space --- src/providers/bibtexcompletion.ts | 47 +++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/src/providers/bibtexcompletion.ts b/src/providers/bibtexcompletion.ts index 4e8736897..3843d75e0 100644 --- a/src/providers/bibtexcompletion.ts +++ b/src/providers/bibtexcompletion.ts @@ -6,7 +6,8 @@ import {Extension} from '../main' export class BibtexCompleter implements vscode.CompletionItemProvider { extension: Extension - private entries: vscode.CompletionItem[] = [] + private entryItems: vscode.CompletionItem[] = [] + private optFieldItems: {[key: string]: vscode.CompletionItem[]} = {} constructor(extension: Extension) { this.extension = extension @@ -19,7 +20,8 @@ export class BibtexCompleter implements vscode.CompletionItemProvider { } loadDefaultItems() { - const defaultEntries = fs.readFileSync(`${this.extension.extensionRoot}/data/bibtex-entries.json`, {encoding: 'utf8'}) + const entries = JSON.parse(fs.readFileSync(`${this.extension.extensionRoot}/data/bibtex-entries.json`, {encoding: 'utf8'})) + const optFields = JSON.parse(fs.readFileSync(`${this.extension.extensionRoot}/data/bibtex-optional-entries.json`, {encoding: 'utf8'})) const entriesReplacements = vscode.workspace.getConfiguration('latex-workshop').get('intellisense.bibtexJSON.replace') as {[key: string]: string[]} const config = vscode.workspace.getConfiguration('latex-workshop') const leftright = config.get('bibtex-format.surround') === 'Curly braces' ? [ '{', '}' ] : [ '"', '"'] @@ -32,19 +34,21 @@ export class BibtexCompleter implements vscode.CompletionItemProvider { sort: config.get('bibtex-format.sortby') as string[] } - const entries = JSON.parse(defaultEntries) const entriesList: string[] = [] Object.keys(entries).forEach(entry => { if (entry in entriesList) { return } if (entry in entriesReplacements) { - this.entries.push(this.entryToCompletion(entry, entriesReplacements[entry], bibtexFormat)) + this.entryItems.push(this.entryToCompletion(entry, entriesReplacements[entry], bibtexFormat)) } else { - this.entries.push(this.entryToCompletion(entry, entries[entry], bibtexFormat)) + this.entryItems.push(this.entryToCompletion(entry, entries[entry], bibtexFormat)) } entriesList.push(entry) }) + Object.keys(optFields).forEach(entry => { + this.optFieldItems[entry] = this.fieldsToCompletion(optFields[entry]) + }) } entryToCompletion(itemName: string, itemFields: string[], config: bibtexUtils.BibtexFormatConfig): vscode.CompletionItem { @@ -72,15 +76,46 @@ export class BibtexCompleter implements vscode.CompletionItemProvider { return suggestion } + fieldsToCompletion(fields: string[]): vscode.CompletionItem[] { + const suggestions: vscode.CompletionItem[] = [] + fields.forEach(field => { + const suggestion: vscode.CompletionItem = new vscode.CompletionItem(field, vscode.CompletionItemKind.Snippet) + suggestion.detail = field + suggestion.documentation = `Add ${field} = {}` + suggestion.insertText = new vscode.SnippetString(`${field} = {$1}`) + suggestions.push(suggestion) + }) + return suggestions + } + provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, _token: vscode.CancellationToken, _context: vscode.CompletionContext): Promise { return new Promise((resolve, _reject) => { const currentLine = document.lineAt(position.line).text + const prevLine = document.lineAt(position.line - 1).text if (currentLine.match(/@[a-zA-Z]*$/)) { - resolve(this.entries) + // Complete an entry name + resolve(this.entryItems) return + } else if (currentLine.match(/\s*[a-zA-z]*/) && prevLine.match(/(?:@[a-zA-Z]{)|(?:["}0-9],\s*$)/)) { + // Add optional fields + const optFields = this.provideOptFields(document, position) + resolve(optFields) } resolve() }) } + provideOptFields(document: vscode.TextDocument, position: vscode.Position): vscode.CompletionItem[] { + const pattern = /^\s*@([a-zA-Z]+)\{(?:[^,]*,)?\s$/m + const content = document.getText(new vscode.Range(new vscode.Position(0, 0), position)) + const reversedContent = content.replace(/(\r\n)|\r/g, '\n').split('\n').reverse().join('\n') + const match = reversedContent.match(pattern) + if (match) { + const entryType = match[1].toLowerCase() + if (entryType in this.optFieldItems) { + return this.optFieldItems[entryType] + } + } + return [] + } } From c19d0760d65546ad177c2dd49d9b28cf5b306b19 Mon Sep 17 00:00:00 2001 From: Jerome Lelong Date: Tue, 26 May 2020 11:38:49 +0200 Subject: [PATCH 4/5] Fix regex typo --- src/providers/bibtexcompletion.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/bibtexcompletion.ts b/src/providers/bibtexcompletion.ts index 3843d75e0..6aaf4fa72 100644 --- a/src/providers/bibtexcompletion.ts +++ b/src/providers/bibtexcompletion.ts @@ -96,7 +96,7 @@ export class BibtexCompleter implements vscode.CompletionItemProvider { // Complete an entry name resolve(this.entryItems) return - } else if (currentLine.match(/\s*[a-zA-z]*/) && prevLine.match(/(?:@[a-zA-Z]{)|(?:["}0-9],\s*$)/)) { + } else if (currentLine.match(/^\s*[a-zA-Z]*/) && prevLine.match(/(?:@[a-zA-Z]{)|(?:["}0-9],\s*$)/)) { // Add optional fields const optFields = this.provideOptFields(document, position) resolve(optFields) From 743065a365e0d6e78d0fd1dbb9ff399d76fb6ef4 Mon Sep 17 00:00:00 2001 From: Jerome Lelong Date: Wed, 27 May 2020 10:34:52 +0200 Subject: [PATCH 5/5] Compute alignment based on all possible fields --- src/providers/bibtexcompletion.ts | 39 ++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/src/providers/bibtexcompletion.ts b/src/providers/bibtexcompletion.ts index 6aaf4fa72..03c6371fd 100644 --- a/src/providers/bibtexcompletion.ts +++ b/src/providers/bibtexcompletion.ts @@ -34,24 +34,42 @@ export class BibtexCompleter implements vscode.CompletionItemProvider { sort: config.get('bibtex-format.sortby') as string[] } + const maxLengths: {[key: string]: number} = this.computeMaxLengths(entries, optFields) const entriesList: string[] = [] Object.keys(entries).forEach(entry => { if (entry in entriesList) { return } if (entry in entriesReplacements) { - this.entryItems.push(this.entryToCompletion(entry, entriesReplacements[entry], bibtexFormat)) + this.entryItems.push(this.entryToCompletion(entry, entriesReplacements[entry], bibtexFormat, maxLengths)) } else { - this.entryItems.push(this.entryToCompletion(entry, entries[entry], bibtexFormat)) + this.entryItems.push(this.entryToCompletion(entry, entries[entry], bibtexFormat, maxLengths)) } entriesList.push(entry) }) Object.keys(optFields).forEach(entry => { - this.optFieldItems[entry] = this.fieldsToCompletion(optFields[entry]) + this.optFieldItems[entry] = this.fieldsToCompletion(entry, optFields[entry], bibtexFormat, maxLengths) }) } - entryToCompletion(itemName: string, itemFields: string[], config: bibtexUtils.BibtexFormatConfig): vscode.CompletionItem { + computeMaxLengths(entries: {[key: string]: string[]}, optFields: {[key: string]: string[]}): {[key: string]: number} { + const maxLengths: {[key: string]: number} = {} + Object.keys(entries).forEach(key => { + let maxFieldLength = 0 + entries[key].forEach(field => { + maxFieldLength = Math.max(maxFieldLength, field.length) + }) + if (key in optFields) { + optFields[key].forEach(field => { + maxFieldLength = Math.max(maxFieldLength, field.length) + }) + } + maxLengths[key] = maxFieldLength + }) + return maxLengths + } + + entryToCompletion(itemName: string, itemFields: string[], config: bibtexUtils.BibtexFormatConfig, maxLengths: {[key: string]: number}): vscode.CompletionItem { const suggestion: vscode.CompletionItem = new vscode.CompletionItem(itemName, vscode.CompletionItemKind.Snippet) suggestion.detail = itemName suggestion.documentation = `Add a @${itemName} entry` @@ -59,15 +77,10 @@ export class BibtexCompleter implements vscode.CompletionItemProvider { // The following code is copied from bibtexutils.ts:bibtexFormat // Find the longest field name in entry - let maxFieldLength = 0 - itemFields.forEach(field => { - maxFieldLength = Math.max(maxFieldLength, field.length) - }) - let s: string = itemName + '{${0:key}' itemFields.forEach(field => { s += ',\n' + config.tab + (config.case === 'lowercase' ? field : field.toUpperCase()) - s += ' '.repeat(maxFieldLength - field.length) + ' = ' + s += ' '.repeat(maxLengths[itemName] - field.length) + ' = ' s += config.left + `$${count}` + config.right count++ }) @@ -76,13 +89,13 @@ export class BibtexCompleter implements vscode.CompletionItemProvider { return suggestion } - fieldsToCompletion(fields: string[]): vscode.CompletionItem[] { + fieldsToCompletion(itemName: string, fields: string[], config: bibtexUtils.BibtexFormatConfig, maxLengths: {[key: string]: number}): vscode.CompletionItem[] { const suggestions: vscode.CompletionItem[] = [] fields.forEach(field => { const suggestion: vscode.CompletionItem = new vscode.CompletionItem(field, vscode.CompletionItemKind.Snippet) suggestion.detail = field - suggestion.documentation = `Add ${field} = {}` - suggestion.insertText = new vscode.SnippetString(`${field} = {$1}`) + suggestion.documentation = `Add ${field} = ${config.left}${config.right}` + suggestion.insertText = new vscode.SnippetString(`${field}` + ' '.repeat(maxLengths[itemName] - field.length) + ` = ${config.left}$1${config.right},`) suggestions.push(suggestion) }) return suggestions