From 6a03eeb98404c17dcee28ba5bc20b1edc2d9f194 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Pupier?= Date: Wed, 28 Jun 2023 23:08:21 +0200 Subject: [PATCH] Fix Attributes defined in another file which is included (#734) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix attributes completion defined in another file which is included resolves #727 Signed-off-by: Aurélien Pupier Co-authored-by: Guillaume Grossetie --- CHANGELOG.md | 1 + src/asciidocEngine.ts | 3 +- src/asciidocParser.ts | 44 +++++++++++-------- src/features/attributeReferenceProvider.ts | 2 +- src/features/documentLinkProvider.ts | 8 ++-- src/image-paste.ts | 2 +- .../attributeRefCompletionProvider.test.ts | 32 ++++++++++++-- 7 files changed, 63 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0cc3fcd2..3224b374 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ ### Bug fixes - folding of several single line comments is not working for more than 2 lines (#722) +- attribute coming from include files are missing in completion with 3.0.x (#727) ## 3.0.5 "pre-release" (2023-06-03) diff --git a/src/asciidocEngine.ts b/src/asciidocEngine.ts index 4b35ed9e..2d5c74c3 100644 --- a/src/asciidocEngine.ts +++ b/src/asciidocEngine.ts @@ -51,7 +51,6 @@ export class AsciidocEngine { } public static load (textDocument: SkinnyTextDocument): Asciidoctor.Document { - const { document } = AsciidocParser.load(textDocument) - return document + return AsciidocParser.load(textDocument) } } diff --git a/src/asciidocParser.ts b/src/asciidocParser.ts index c338454a..b9f7aa8e 100644 --- a/src/asciidocParser.ts +++ b/src/asciidocParser.ts @@ -76,17 +76,13 @@ export class AsciidocParser { highlightjsBuiltInSyntaxHighlighter.$register_for('highlight.js', 'highlightjs') const baseDir = AsciidocTextDocument.fromTextDocument(textDocument).getBaseDir() const templateDirs = this.getTemplateDirs() - const options: { [key: string]: any } = { - attributes: { - 'env-vscode': '', - env: 'vscode', - ...asciidoctorAttributes, - }, - backend, - extension_registry: registry, - header_footer: true, - safe: 'unsafe', - ...(baseDir && { base_dir: baseDir }), + const options: { [key: string]: any } = AsciidocParser.getDefaultAsciidoctorOptions(baseDir) + options.extension_registry = registry + options.header_footer = true + options.attributes = { + 'env-vscode': '', + env: 'vscode', + ...asciidoctorAttributes, } if (templateDirs.length !== 0) { options.template_dirs = templateDirs @@ -110,27 +106,37 @@ export class AsciidocParser { // Load - public static load (textDocument: SkinnyTextDocument): { document: Asciidoctor.Document, baseDocumentIncludeItems: IncludeItems } { + public static getBaseDocumentIncludeItems (textDocument: SkinnyTextDocument): IncludeItems { const memoryLogger = processor.MemoryLogger.create() processor.LoggerManager.setLogger(memoryLogger) const registry = processor.Extensions.create() asciidoctorFindIncludeProcessor.register(registry) asciidoctorFindIncludeProcessor.resetIncludes() const baseDir = AsciidocTextDocument.fromTextDocument(textDocument).getBaseDir() - const document = processor.load(textDocument.getText(), { + const options = this.getDefaultAsciidoctorOptions(baseDir) + options.extension_registry = registry + processor.load(textDocument.getText(), options) + // QUESTION: should we report error? + return asciidoctorFindIncludeProcessor.getBaseDocIncludes() + } + + public static load (textDocument: SkinnyTextDocument): Asciidoctor.Document { + const memoryLogger = processor.MemoryLogger.create() + processor.LoggerManager.setLogger(memoryLogger) + const baseDir = AsciidocTextDocument.fromTextDocument(textDocument).getBaseDir() + return processor.load(textDocument.getText(), AsciidocParser.getDefaultAsciidoctorOptions(baseDir)) + } + + private static getDefaultAsciidoctorOptions (baseDir: string): any { + return { attributes: { 'env-vscode': '', env: 'vscode', }, - extension_registry: registry, sourcemap: true, safe: 'unsafe', + parse: true, ...(baseDir && { base_dir: baseDir }), - }) - // QUESTION: should we report error? - return { - document, - baseDocumentIncludeItems: asciidoctorFindIncludeProcessor.getBaseDocIncludes(), } } diff --git a/src/features/attributeReferenceProvider.ts b/src/features/attributeReferenceProvider.ts index ca703a07..3a532f44 100644 --- a/src/features/attributeReferenceProvider.ts +++ b/src/features/attributeReferenceProvider.ts @@ -4,7 +4,7 @@ import { AsciidocParser } from '../asciidocParser' export class AttributeReferenceProvider { provideCompletionItems (textDocument: vscode.TextDocument, position: vscode.Position): vscode.CompletionItem[] { - const { document } = AsciidocParser.load(textDocument) + const document = AsciidocParser.load(textDocument) const attributes = document.getAttributes() const lineText = textDocument.lineAt(position).text const prefix = lineText.substring(position.character - 1, position.character) diff --git a/src/features/documentLinkProvider.ts b/src/features/documentLinkProvider.ts index 78d322d2..87f9749d 100644 --- a/src/features/documentLinkProvider.ts +++ b/src/features/documentLinkProvider.ts @@ -45,20 +45,22 @@ function normalizeLink ( export default class LinkProvider implements vscode.DocumentLinkProvider { public provideDocumentLinks (textDocument: vscode.TextDocument, _token: vscode.CancellationToken): vscode.DocumentLink[] { - const { document, baseDocumentIncludeItems } = AsciidocParser.load(textDocument) + const baseDocumentIncludeItems = AsciidocParser.getBaseDocumentIncludeItems(textDocument) // includes from the reader are resolved correctly but the line numbers may be offset and not exactly match the document let baseDocumentProcessorIncludes = baseDocumentIncludeItems const includeDirective = /^(\\)?include::([^[][^[]*)\[([^\n]+)?\]$/ // get includes from document text. These may be inside ifeval or ifdef but the line numbers are correct. const baseDocumentRegexIncludes = new Map() - document.getSourceLines().forEach((line, index) => { + const splittedLines = textDocument.getText().split('\n') + for (let index = 0; index < splittedLines.length; index++) { + const line = splittedLines[index] const match = includeDirective.exec(line) if (match) { // match[2] is the include reference baseDocumentRegexIncludes.set(index, match[2].length) } - }) + } // find a corrected mapping for line numbers const betterIncludeMatching = similarArrayMatch( diff --git a/src/image-paste.ts b/src/image-paste.ts index e9a102b6..d56b4e17 100644 --- a/src/image-paste.ts +++ b/src/image-paste.ts @@ -334,7 +334,7 @@ export namespace Import { return dir } - const { document } = AsciidocParser.load(textDocument) + const document = AsciidocParser.load(textDocument) return document.getAttribute('imagesdir', '') } diff --git a/src/test/attributeRefCompletionProvider.test.ts b/src/test/attributeRefCompletionProvider.test.ts index 3114c6d8..d4364871 100644 --- a/src/test/attributeRefCompletionProvider.test.ts +++ b/src/test/attributeRefCompletionProvider.test.ts @@ -18,7 +18,7 @@ suite('Attribute ref CompletionsProvider', () => { createdFiles = [] }) test('Should return attribute key defined in same file', async () => { - const fileToAutoComplete = vscode.Uri.file(`${root}/fileToAutoComplete-attributeRef.adoc`) + const fileToAutoComplete = vscode.Uri.file(`${root}/fileToAutoComplete-attributeRef-samefile.adoc`) await vscode.workspace.fs.writeFile(fileToAutoComplete, Buffer.from(`:my-attribute-to-find-in-completion: dummy value `)) createdFiles.push(fileToAutoComplete) @@ -49,8 +49,8 @@ dumm`)) assert.deepStrictEqual((completionItem.label as vscode.CompletionItemLabel).description, 'dummy value') assert.deepStrictEqual(completionItem.insertText, '{my-attribute-to-find-in-completion}') }) - test('Should return no completion when nothign corresponds', async () => { - const fileToAutoComplete = vscode.Uri.file(`${root}/fileToAutoComplete-attributeRef.adoc`) + test('Should return no completion when nothing corresponds', async () => { + const fileToAutoComplete = vscode.Uri.file(`${root}/fileToAutoComplete-attributeRef-samefile-basedOnValue.adoc`) await vscode.workspace.fs.writeFile(fileToAutoComplete, Buffer.from(`:my-attribute-to-find-in-completion: dummy value somethingVeryDifferent`)) createdFiles.push(fileToAutoComplete) @@ -59,4 +59,30 @@ somethingVeryDifferent`)) const completionsItems = new AttributeReferenceProvider().provideCompletionItems(file, new Position(1, 22)) assert.notStrictEqual(completionsItems.length, 0, 'There are Completion provided although none are expected.') }) + test('Should return attribute key defined in another file', async () => { + const fileToAutoComplete = vscode.Uri.file(`${root}/fileToAutoComplete-attributeRef-differentFile.adoc`) + await vscode.workspace.fs.writeFile(fileToAutoComplete, Buffer.from(`= test +include::file-referenced-with-an-attribute.adoc[] + + + `)) + createdFiles.push(fileToAutoComplete) + + const fileReferencedWithAnAttribute = vscode.Uri.file(`${root}/file-referenced-with-an-attribute.adoc`) + await vscode.workspace.fs.writeFile(fileReferencedWithAnAttribute, Buffer.from(':my-attribute-to-find-in-completion: dummy value')) + createdFiles.push(fileReferencedWithAnAttribute) + + const file = await vscode.workspace.openTextDocument(fileToAutoComplete) + const completionsItems = new AttributeReferenceProvider().provideCompletionItems(file, new Position(3, 0)) + const filteredCompletionItems = completionsItems.filter((completionItem) => { + if ((completionItem.label as vscode.CompletionItemLabel)) { + return (completionItem.label as vscode.CompletionItemLabel).label === 'my-attribute-to-find-in-completion' + } else { + return false + } + }) + const completionItem = filteredCompletionItems[0] + assert.deepStrictEqual((completionItem.label as vscode.CompletionItemLabel).description, 'dummy value') + assert.deepStrictEqual(completionItem.insertText, '{my-attribute-to-find-in-completion}') + }) })