From 8812c3e4bc46e9f4f2a572bfbe3b29e0e1293538 Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Sun, 25 Jun 2017 21:34:04 -0700 Subject: [PATCH 01/31] Use the right regex for validating abbreviations --- package.json | 2 +- src/emmetHelper.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 706bfe0..f7bbefc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vscode-emmet-helper", - "version": "0.0.10", + "version": "0.0.11", "description": "Helper to use emmet modules in Visual Studio Code", "main": "./out/emmetHelper.js", "author": "Microsoft Corporation", diff --git a/src/emmetHelper.ts b/src/emmetHelper.ts index 0f5b164..bd16169 100644 --- a/src/emmetHelper.ts +++ b/src/emmetHelper.ts @@ -153,7 +153,7 @@ export function extractAbbreviation(document: vscode.TextDocument, position: vsc * @param abbreviation string */ export function isAbbreviationValid(syntax: string, abbreviation: string): boolean { - return isStyleSheet(syntax) ? htmlAbbreviationRegex.test(abbreviation) : cssAbbreviationRegex.test(abbreviation); + return !isStyleSheet(syntax) ? htmlAbbreviationRegex.test(abbreviation) : cssAbbreviationRegex.test(abbreviation); } /** From 3a7aec63a8952270cb5fd454815b13ebddcda5c2 Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Wed, 28 Jun 2017 11:45:11 -0700 Subject: [PATCH 02/31] Use new value for emmet.showExpandedAbbreviation setting --- package.json | 2 +- src/emmetHelper.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index f7bbefc..4e85bf1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vscode-emmet-helper", - "version": "0.0.11", + "version": "0.0.12", "description": "Helper to use emmet modules in Visual Studio Code", "main": "./out/emmetHelper.js", "author": "Microsoft Corporation", diff --git a/src/emmetHelper.ts b/src/emmetHelper.ts index bd16169..44bd17e 100644 --- a/src/emmetHelper.ts +++ b/src/emmetHelper.ts @@ -26,7 +26,7 @@ export class EmmetCompletionItemProvider implements vscode.CompletionItemProvide public provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Thenable { let emmetConfig = vscode.workspace.getConfiguration('emmet'); - if (!emmetConfig['useNewEmmet'] || !emmetConfig['showExpandedAbbreviation']) { + if (!emmetConfig['useNewEmmet'] || emmetConfig['showExpandedAbbreviation'] !== false || emmetConfig['showExpandedAbbreviation'] !== 'never') { return Promise.resolve(null); } From 35fb32247f37157d4e6dd9a86106ef0ffc841921 Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Wed, 28 Jun 2017 11:56:36 -0700 Subject: [PATCH 03/31] Silly me, used the wrong conditions --- package.json | 2 +- src/emmetHelper.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 4e85bf1..a46c562 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vscode-emmet-helper", - "version": "0.0.12", + "version": "0.0.13", "description": "Helper to use emmet modules in Visual Studio Code", "main": "./out/emmetHelper.js", "author": "Microsoft Corporation", diff --git a/src/emmetHelper.ts b/src/emmetHelper.ts index 44bd17e..937f1d7 100644 --- a/src/emmetHelper.ts +++ b/src/emmetHelper.ts @@ -26,7 +26,7 @@ export class EmmetCompletionItemProvider implements vscode.CompletionItemProvide public provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Thenable { let emmetConfig = vscode.workspace.getConfiguration('emmet'); - if (!emmetConfig['useNewEmmet'] || emmetConfig['showExpandedAbbreviation'] !== false || emmetConfig['showExpandedAbbreviation'] !== 'never') { + if (!emmetConfig['useNewEmmet'] || emmetConfig['showExpandedAbbreviation'] === false || emmetConfig['showExpandedAbbreviation'] === 'never') { return Promise.resolve(null); } From 227500ae034ddd52f6ddce85805ecaa0150c62a6 Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Wed, 28 Jun 2017 13:55:48 -0700 Subject: [PATCH 04/31] Move getEmmetMode from extension to emmet-helper module --- package.json | 2 +- src/emmetHelper.ts | 35 ++++++++++++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index a46c562..e22761b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vscode-emmet-helper", - "version": "0.0.13", + "version": "0.0.14", "description": "Helper to use emmet modules in Visual Studio Code", "main": "./out/emmetHelper.js", "author": "Microsoft Corporation", diff --git a/src/emmetHelper.ts b/src/emmetHelper.ts index 937f1d7..1b9d4d9 100644 --- a/src/emmetHelper.ts +++ b/src/emmetHelper.ts @@ -13,6 +13,7 @@ import * as fs from 'fs'; const snippetKeyCache = new Map(); const htmlAbbreviationRegex = /^[a-z,A-Z,!,(,[,#,\.]/; const cssAbbreviationRegex = /^[a-z,A-Z,!,@,#]/; +const emmetModes = ['html','pug','slim','haml','xml','xsl', 'jsx', 'css','scss','sass','less','stylus']; export class EmmetCompletionItemProvider implements vscode.CompletionItemProvider { private _syntax: string; @@ -26,7 +27,10 @@ export class EmmetCompletionItemProvider implements vscode.CompletionItemProvide public provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Thenable { let emmetConfig = vscode.workspace.getConfiguration('emmet'); - if (!emmetConfig['useNewEmmet'] || emmetConfig['showExpandedAbbreviation'] === false || emmetConfig['showExpandedAbbreviation'] === 'never') { + if (!emmetConfig['useNewEmmet'] + || emmetConfig['showExpandedAbbreviation'] === false + || emmetConfig['showExpandedAbbreviation'] === 'never' + || emmetModes.indexOf(this._syntax) === -1) { return Promise.resolve(null); } @@ -283,6 +287,35 @@ function dirExists(dirPath: string): boolean { } } +/** +* Get the corresponding emmet mode for given vscode language mode +* Eg: jsx for typescriptreact/javascriptreact or pug for jade +* If the language is not supported by emmet or has been exlcuded via `exlcudeLanguages` setting, +* then nothing is returned +* +* @param language +*/ +export function getEmmetMode(language: string): string { +    const excludedConfig = vscode.workspace.getConfiguration('emmet')['excludeLanguages']; +    const excludedLanguages = Array.isArray(excludedConfig) ? excludedConfig : []; +     +    if (!language || excludedLanguages.indexOf(language) > -1) { +        return; +    } +    if (/\b(typescriptreact|javascriptreact|jsx-tags)\b/.test(language)) { // treat tsx like jsx +        return 'jsx'; +    } +    if (language === 'sass-indented') { // map sass-indented to sass +        return 'sass'; +    } +    if (language === 'jade') { +        return 'pug'; +    } +    if (emmetModes.indexOf(language) > -1) { +        return language; +    } +} + From 5875048b62a907eb4e7ebcbab7811ce878cb3f68 Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Thu, 29 Jun 2017 10:59:51 -0700 Subject: [PATCH 05/31] Enable html suggestions when typing after : --- package.json | 2 +- src/emmetHelper.ts | 100 ++++++++++++++++++++++----------------------- 2 files changed, 51 insertions(+), 51 deletions(-) diff --git a/package.json b/package.json index e22761b..285e896 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vscode-emmet-helper", - "version": "0.0.14", + "version": "0.0.15", "description": "Helper to use emmet modules in Visual Studio Code", "main": "./out/emmetHelper.js", "author": "Microsoft Corporation", diff --git a/src/emmetHelper.ts b/src/emmetHelper.ts index 1b9d4d9..f0cb39a 100644 --- a/src/emmetHelper.ts +++ b/src/emmetHelper.ts @@ -11,13 +11,14 @@ import * as path from 'path'; import * as fs from 'fs'; const snippetKeyCache = new Map(); -const htmlAbbreviationRegex = /^[a-z,A-Z,!,(,[,#,\.]/; +const htmlAbbreviationStartRegex = /^[a-z,A-Z,!,(,[,#,\.]/; +const htmlAbbreviationEndRegex = /[a-z,A-Z,!,),\],#,\.,},\d]$/; const cssAbbreviationRegex = /^[a-z,A-Z,!,@,#]/; -const emmetModes = ['html','pug','slim','haml','xml','xsl', 'jsx', 'css','scss','sass','less','stylus']; +const emmetModes = ['html', 'pug', 'slim', 'haml', 'xml', 'xsl', 'jsx', 'css', 'scss', 'sass', 'less', 'stylus']; export class EmmetCompletionItemProvider implements vscode.CompletionItemProvider { private _syntax: string; - + constructor(syntax: string) { if (syntax) { this._syntax = syntax; @@ -27,42 +28,44 @@ export class EmmetCompletionItemProvider implements vscode.CompletionItemProvide public provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Thenable { let emmetConfig = vscode.workspace.getConfiguration('emmet'); - if (!emmetConfig['useNewEmmet'] - || emmetConfig['showExpandedAbbreviation'] === false + if (!emmetConfig['useNewEmmet'] + || emmetConfig['showExpandedAbbreviation'] === false || emmetConfig['showExpandedAbbreviation'] === 'never' || emmetModes.indexOf(this._syntax) === -1) { return Promise.resolve(null); } + let expandedAbbr: vscode.CompletionItem; let [abbreviationRange, abbreviation] = extractAbbreviation(document, position); - if (!isAbbreviationValid(this._syntax, abbreviation)) { - return; - } - let expandedText = expand(abbreviation, getExpandOptions(this._syntax)); - if (!expandedText) { - return; + if (isAbbreviationValid(this._syntax, abbreviation)) { + let expandedText = expand(abbreviation, getExpandOptions(this._syntax)); + if (expandedText) { + expandedAbbr = new vscode.CompletionItem(abbreviation); + expandedAbbr.insertText = new vscode.SnippetString(expandedText); + expandedAbbr.documentation = this.removeTabStops(expandedText); + expandedAbbr.range = abbreviationRange; + expandedAbbr.detail = 'Emmet Abbreviation'; + if (isStyleSheet(this._syntax)) { + // Temporary fix for https://github.com/Microsoft/vscode/issues/28933 + expandedAbbr.filterText = abbreviation; + expandedAbbr.sortText = expandedAbbr.documentation; + expandedAbbr.label = expandedAbbr.documentation; + return Promise.resolve(new vscode.CompletionList([expandedAbbr], true)); + } + } } - let expandedAbbr = new vscode.CompletionItem(abbreviation); - expandedAbbr.insertText = new vscode.SnippetString(expandedText); - expandedAbbr.documentation = this.removeTabStops(expandedText); - expandedAbbr.range = abbreviationRange; - expandedAbbr.detail = 'Emmet Abbreviation'; - let completionItems: vscode.CompletionItem[] = expandedAbbr ? [expandedAbbr] : []; if (!isStyleSheet(this._syntax)) { - // Workaround for the main expanded abbr not appearing before the snippet suggestions - expandedAbbr.sortText = '0' + expandedAbbr.label; + if (expandedAbbr) { + // Workaround for the main expanded abbr not appearing before the snippet suggestions + expandedAbbr.sortText = '0' + expandedAbbr.label; + } let currentWord = this.getCurrentWord(document, position); let abbreviationSuggestions = this.getAbbreviationSuggestions(this._syntax, currentWord, abbreviation, abbreviationRange); completionItems = completionItems.concat(abbreviationSuggestions); - } else { - // Temporary fix for https://github.com/Microsoft/vscode/issues/28933 - expandedAbbr.filterText = abbreviation; - expandedAbbr.sortText = expandedAbbr.documentation; - expandedAbbr.label = expandedAbbr.documentation; } return Promise.resolve(new vscode.CompletionList(completionItems, true)); } @@ -109,14 +112,11 @@ export class EmmetCompletionItemProvider implements vscode.CompletionItemProvide } private getCurrentWord(document: vscode.TextDocument, position: vscode.Position): string { - let wordAtPosition = document.getWordRangeAtPosition(position); - let currentWord = ''; - if (wordAtPosition && wordAtPosition.start.character < position.character) { - let word = document.getText(wordAtPosition); - currentWord = word.substr(0, position.character - wordAtPosition.start.character); + let currentLine = document.lineAt(position.line).text; + let matches = currentLine.match(/[\w,:]*$/); + if (matches) { + return matches[0]; } - - return currentWord; } private removeTabStops(expandedWord: string): string { @@ -157,7 +157,7 @@ export function extractAbbreviation(document: vscode.TextDocument, position: vsc * @param abbreviation string */ export function isAbbreviationValid(syntax: string, abbreviation: string): boolean { - return !isStyleSheet(syntax) ? htmlAbbreviationRegex.test(abbreviation) : cssAbbreviationRegex.test(abbreviation); + return !isStyleSheet(syntax) ? (htmlAbbreviationStartRegex.test(abbreviation) && htmlAbbreviationEndRegex.test(abbreviation)) : cssAbbreviationRegex.test(abbreviation); } /** @@ -296,24 +296,24 @@ function dirExists(dirPath: string): boolean { * @param language */ export function getEmmetMode(language: string): string { -    const excludedConfig = vscode.workspace.getConfiguration('emmet')['excludeLanguages']; -    const excludedLanguages = Array.isArray(excludedConfig) ? excludedConfig : []; -     -    if (!language || excludedLanguages.indexOf(language) > -1) { -        return; -    } -    if (/\b(typescriptreact|javascriptreact|jsx-tags)\b/.test(language)) { // treat tsx like jsx -        return 'jsx'; -    } -    if (language === 'sass-indented') { // map sass-indented to sass -        return 'sass'; -    } -    if (language === 'jade') { -        return 'pug'; -    } -    if (emmetModes.indexOf(language) > -1) { -        return language; -    } +     const excludedConfig = vscode.workspace.getConfiguration('emmet')['excludeLanguages']; +     const excludedLanguages = Array.isArray(excludedConfig) ? excludedConfig : []; + +     if (!language || excludedLanguages.indexOf(language) > -1) { +         return; +     } +     if (/\b(typescriptreact|javascriptreact|jsx-tags)\b/.test(language)) { // treat tsx like jsx +         return 'jsx'; +     } +     if (language === 'sass-indented') { // map sass-indented to sass +         return 'sass'; +     } +     if (language === 'jade') { +         return 'pug'; +     } +     if (emmetModes.indexOf(language) > -1) { +         return language; +     } } From 6b39053441cc47a44daf24d232ce06d83b9b09cc Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Thu, 29 Jun 2017 17:15:46 -0700 Subject: [PATCH 06/31] Updating html regex for valid abbr --- package.json | 2 +- src/emmetHelper.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 285e896..0b31fd0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vscode-emmet-helper", - "version": "0.0.15", + "version": "0.0.16", "description": "Helper to use emmet modules in Visual Studio Code", "main": "./out/emmetHelper.js", "author": "Microsoft Corporation", diff --git a/src/emmetHelper.ts b/src/emmetHelper.ts index f0cb39a..7eca21d 100644 --- a/src/emmetHelper.ts +++ b/src/emmetHelper.ts @@ -12,7 +12,7 @@ import * as fs from 'fs'; const snippetKeyCache = new Map(); const htmlAbbreviationStartRegex = /^[a-z,A-Z,!,(,[,#,\.]/; -const htmlAbbreviationEndRegex = /[a-z,A-Z,!,),\],#,\.,},\d]$/; +const htmlAbbreviationEndRegex = /[a-z,A-Z,!,),\],#,\.,},\d,*,$]$/; const cssAbbreviationRegex = /^[a-z,A-Z,!,@,#]/; const emmetModes = ['html', 'pug', 'slim', 'haml', 'xml', 'xsl', 'jsx', 'css', 'scss', 'sass', 'less', 'stylus']; From e7dd852adc03e506065240b5d9b0fa89a2cf5fd9 Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Thu, 29 Jun 2017 18:18:18 -0700 Subject: [PATCH 07/31] Try/Catch --- package.json | 2 +- src/emmetHelper.ts | 56 +++++++++++++++++++++++++++++----------------- 2 files changed, 37 insertions(+), 21 deletions(-) diff --git a/package.json b/package.json index 0b31fd0..41acbf0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vscode-emmet-helper", - "version": "0.0.16", + "version": "0.0.17", "description": "Helper to use emmet modules in Visual Studio Code", "main": "./out/emmetHelper.js", "author": "Microsoft Corporation", diff --git a/src/emmetHelper.ts b/src/emmetHelper.ts index 7eca21d..20fe9fd 100644 --- a/src/emmetHelper.ts +++ b/src/emmetHelper.ts @@ -39,7 +39,13 @@ export class EmmetCompletionItemProvider implements vscode.CompletionItemProvide let [abbreviationRange, abbreviation] = extractAbbreviation(document, position); if (isAbbreviationValid(this._syntax, abbreviation)) { - let expandedText = expand(abbreviation, getExpandOptions(this._syntax)); + let expandedText; + try { + expandedText = expand(abbreviation, getExpandOptions(this._syntax)); + } catch (e) { + + } + if (expandedText) { expandedAbbr = new vscode.CompletionItem(abbreviation); expandedAbbr.insertText = new vscode.SnippetString(expandedText); @@ -91,7 +97,12 @@ export class EmmetCompletionItemProvider implements vscode.CompletionItemProvide } let currentAbbr = abbreviation + snippetKey.substr(prefix.length); - let expandedAbbr = expand(currentAbbr, getExpandOptions(syntax)); + let expandedAbbr; + try { + expandedAbbr = expand(currentAbbr, getExpandOptions(syntax)); + } catch (e) { + + } let item = new vscode.CompletionItem(snippetKey); item.documentation = this.removeTabStops(expandedAbbr); @@ -141,7 +152,12 @@ export function isStyleSheet(syntax): boolean { */ export function extractAbbreviation(document: vscode.TextDocument, position: vscode.Position): [vscode.Range, string] { let currentLine = document.lineAt(position.line).text; - let result = extract(currentLine, position.character, true); + let result; + try { + result = extract(currentLine, position.character, true); + } catch (e) { + + } if (!result) { return [null, '']; } @@ -296,24 +312,24 @@ function dirExists(dirPath: string): boolean { * @param language */ export function getEmmetMode(language: string): string { -     const excludedConfig = vscode.workspace.getConfiguration('emmet')['excludeLanguages']; -     const excludedLanguages = Array.isArray(excludedConfig) ? excludedConfig : []; + const excludedConfig = vscode.workspace.getConfiguration('emmet')['excludeLanguages']; + const excludedLanguages = Array.isArray(excludedConfig) ? excludedConfig : []; -     if (!language || excludedLanguages.indexOf(language) > -1) { -         return; -     } -     if (/\b(typescriptreact|javascriptreact|jsx-tags)\b/.test(language)) { // treat tsx like jsx -         return 'jsx'; -     } -     if (language === 'sass-indented') { // map sass-indented to sass -         return 'sass'; -     } -     if (language === 'jade') { -         return 'pug'; -     } -     if (emmetModes.indexOf(language) > -1) { -         return language; -     } + if (!language || excludedLanguages.indexOf(language) > -1) { + return; + } + if (/\b(typescriptreact|javascriptreact|jsx-tags)\b/.test(language)) { // treat tsx like jsx + return 'jsx'; + } + if (language === 'sass-indented') { // map sass-indented to sass + return 'sass'; + } + if (language === 'jade') { + return 'pug'; + } + if (emmetModes.indexOf(language) > -1) { + return language; + } } From 543c06c55e2cd8ff26575811b0c0c0bba6d823e1 Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Thu, 29 Jun 2017 19:08:04 -0700 Subject: [PATCH 08/31] Updating urls --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 41acbf0..bb11655 100644 --- a/package.json +++ b/package.json @@ -1,16 +1,16 @@ { "name": "vscode-emmet-helper", - "version": "0.0.17", + "version": "0.0.18", "description": "Helper to use emmet modules in Visual Studio Code", "main": "./out/emmetHelper.js", "author": "Microsoft Corporation", "repository": { "type": "git", - "url": "https://github.com/Microsoft/vscode-emmet-helper" + "url": "https://github.com/ramya-rao-a/vscode-emmet-helper" }, "license": "MIT", "bugs": { - "url": "https://github.com/Microsoft/vscode-emmet-helper" + "url": "https://github.com/ramya-rao-a/vscode-emmet-helper" }, "devDependencies": { "@types/node": "^6.0.46", From 6016dcf52fda8b544a02d9563dc259d47a717c59 Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Mon, 17 Jul 2017 18:54:14 -0700 Subject: [PATCH 09/31] Use languageservertypes instead of vscode so that LS can use this helper --- package.json | 5 +- src/emmetHelper.ts | 255 +- src/typings/vscode.d.ts | 5061 --------------------------------------- 3 files changed, 130 insertions(+), 5191 deletions(-) delete mode 100644 src/typings/vscode.d.ts diff --git a/package.json b/package.json index bb11655..feb1842 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vscode-emmet-helper", - "version": "0.0.18", + "version": "0.0.19", "description": "Helper to use emmet modules in Visual Studio Code", "main": "./out/emmetHelper.js", "author": "Microsoft Corporation", @@ -18,7 +18,8 @@ }, "dependencies": { "@emmetio/expand-abbreviation": "^0.5.8", - "@emmetio/extract-abbreviation": "^0.1.1" + "@emmetio/extract-abbreviation": "^0.1.1", + "vscode-languageserver-types": "^3.0.3" }, "scripts": { "prepublish": "tsc -p ./", diff --git a/src/emmetHelper.ts b/src/emmetHelper.ts index 20fe9fd..94350dd 100644 --- a/src/emmetHelper.ts +++ b/src/emmetHelper.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ -import * as vscode from 'vscode'; +import { TextDocument, Position, Range, CompletionItem, CompletionList, TextEdit, InsertTextFormat } from 'vscode-languageserver-types' import { expand, createSnippetsRegistry } from '@emmetio/expand-abbreviation'; import * as extract from '@emmetio/extract-abbreviation'; import * as path from 'path'; @@ -16,126 +16,130 @@ const htmlAbbreviationEndRegex = /[a-z,A-Z,!,),\],#,\.,},\d,*,$]$/; const cssAbbreviationRegex = /^[a-z,A-Z,!,@,#]/; const emmetModes = ['html', 'pug', 'slim', 'haml', 'xml', 'xsl', 'jsx', 'css', 'scss', 'sass', 'less', 'stylus']; -export class EmmetCompletionItemProvider implements vscode.CompletionItemProvider { - private _syntax: string; +export interface EmmetConfiguration { + useNewEmmet: string; + showExpandedAbbreviation: string; + showAbbreviationSuggestions: boolean; + syntaxProfiles: object; + variables: object; +} - constructor(syntax: string) { - if (syntax) { - this._syntax = syntax; +export function doComplete(document: TextDocument, position: Position, syntax: string, emmetConfig: EmmetConfiguration): CompletionList { + + if (!emmetConfig.useNewEmmet || emmetConfig.showExpandedAbbreviation === 'never' || emmetModes.indexOf(syntax) === -1) { + return; + } + + let expandedAbbr: CompletionItem; + let [abbreviationRange, abbreviation] = extractAbbreviation(document, position); + let expandOptions = getExpandOptions(emmetConfig.syntaxProfiles, emmetConfig.variables, syntax); + + if (isAbbreviationValid(syntax, abbreviation)) { + let expandedText; + try { + expandedText = expand(abbreviation, expandOptions); + } catch (e) { + + } + + if (expandedText) { + expandedAbbr = CompletionItem.create(abbreviation); + expandedAbbr.textEdit = TextEdit.replace(abbreviationRange, expandedText); + expandedAbbr.documentation = removeTabStops(expandedText); + expandedAbbr.insertTextFormat = InsertTextFormat.Snippet; + expandedAbbr.detail = 'Emmet Abbreviation'; + if (isStyleSheet(syntax)) { + // Temporary fix for https://github.com/Microsoft/vscode/issues/28933 + expandedAbbr.filterText = abbreviation; + expandedAbbr.sortText = expandedAbbr.documentation; + expandedAbbr.label = expandedAbbr.documentation; + return CompletionList.create([expandedAbbr], true); + } } } - public provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Thenable { - - let emmetConfig = vscode.workspace.getConfiguration('emmet'); - if (!emmetConfig['useNewEmmet'] - || emmetConfig['showExpandedAbbreviation'] === false - || emmetConfig['showExpandedAbbreviation'] === 'never' - || emmetModes.indexOf(this._syntax) === -1) { - return Promise.resolve(null); + let completionItems: CompletionItem[] = expandedAbbr ? [expandedAbbr] : []; + if (!isStyleSheet(syntax)) { + if (expandedAbbr) { + // Workaround for the main expanded abbr not appearing before the snippet suggestions + expandedAbbr.sortText = '0' + expandedAbbr.label; } - let expandedAbbr: vscode.CompletionItem; - let [abbreviationRange, abbreviation] = extractAbbreviation(document, position); - - if (isAbbreviationValid(this._syntax, abbreviation)) { - let expandedText; - try { - expandedText = expand(abbreviation, getExpandOptions(this._syntax)); - } catch (e) { - - } - - if (expandedText) { - expandedAbbr = new vscode.CompletionItem(abbreviation); - expandedAbbr.insertText = new vscode.SnippetString(expandedText); - expandedAbbr.documentation = this.removeTabStops(expandedText); - expandedAbbr.range = abbreviationRange; - expandedAbbr.detail = 'Emmet Abbreviation'; - if (isStyleSheet(this._syntax)) { - // Temporary fix for https://github.com/Microsoft/vscode/issues/28933 - expandedAbbr.filterText = abbreviation; - expandedAbbr.sortText = expandedAbbr.documentation; - expandedAbbr.label = expandedAbbr.documentation; - return Promise.resolve(new vscode.CompletionList([expandedAbbr], true)); - } - } - } - - let completionItems: vscode.CompletionItem[] = expandedAbbr ? [expandedAbbr] : []; - if (!isStyleSheet(this._syntax)) { - if (expandedAbbr) { - // Workaround for the main expanded abbr not appearing before the snippet suggestions - expandedAbbr.sortText = '0' + expandedAbbr.label; - } - - let currentWord = this.getCurrentWord(document, position); - let abbreviationSuggestions = this.getAbbreviationSuggestions(this._syntax, currentWord, abbreviation, abbreviationRange); + if (emmetConfig.showAbbreviationSuggestions) { + let currentWord = getCurrentWord(document, position); + let abbreviationSuggestions = getAbbreviationSuggestions(syntax, currentWord, abbreviation, abbreviationRange, expandOptions); completionItems = completionItems.concat(abbreviationSuggestions); } - return Promise.resolve(new vscode.CompletionList(completionItems, true)); + } - - getAbbreviationSuggestions(syntax: string, prefix: string, abbreviation: string, abbreviationRange: vscode.Range): vscode.CompletionItem[] { - if (!vscode.workspace.getConfiguration('emmet')['showAbbreviationSuggestions'] || !prefix || !abbreviation) { - return []; - } - - if (!snippetKeyCache.has(syntax)) { - let registry = createSnippetsRegistry(syntax); - let snippetKeys: string[] = registry.all({ type: 'string' }).map(snippet => { - return snippet.key; - }); - snippetKeyCache.set(syntax, snippetKeys); - } - - let snippetKeys = snippetKeyCache.get(syntax); - let snippetCompletions = []; - snippetKeys.forEach(snippetKey => { - if (!snippetKey.startsWith(prefix) || snippetKey === prefix) { - return; - } - - let currentAbbr = abbreviation + snippetKey.substr(prefix.length); - let expandedAbbr; - try { - expandedAbbr = expand(currentAbbr, getExpandOptions(syntax)); - } catch (e) { - - } - - let item = new vscode.CompletionItem(snippetKey); - item.documentation = this.removeTabStops(expandedAbbr); - item.detail = 'Emmet Abbreviation'; - item.insertText = new vscode.SnippetString(expandedAbbr); - item.range = abbreviationRange; - - // Workaround for snippet suggestions items getting filtered out as the complete abbr does not start with snippetKey - item.filterText = abbreviation; - - // Workaround for the main expanded abbr not appearing before the snippet suggestions - item.sortText = '9' + abbreviation; - - snippetCompletions.push(item); - }); - - return snippetCompletions; - } - - private getCurrentWord(document: vscode.TextDocument, position: vscode.Position): string { - let currentLine = document.lineAt(position.line).text; - let matches = currentLine.match(/[\w,:]*$/); - if (matches) { - return matches[0]; - } - } - - private removeTabStops(expandedWord: string): string { - return expandedWord.replace(/\$\{\d+\}/g, '').replace(/\$\{\d+:([^\}]+)\}/g, '$1'); - } - + return CompletionList.create(completionItems, true); } +function getAbbreviationSuggestions(syntax: string, prefix: string, abbreviation: string, abbreviationRange: Range, expandOptions: object): CompletionItem[] { + if (!snippetKeyCache.has(syntax)) { + let registry = createSnippetsRegistry(syntax); + let snippetKeys: string[] = registry.all({ type: 'string' }).map(snippet => { + return snippet.key; + }); + snippetKeyCache.set(syntax, snippetKeys); + } + + let snippetKeys = snippetKeyCache.get(syntax); + let snippetCompletions = []; + snippetKeys.forEach(snippetKey => { + if (!snippetKey.startsWith(prefix) || snippetKey === prefix) { + return; + } + + let currentAbbr = abbreviation + snippetKey.substr(prefix.length); + let expandedAbbr; + try { + expandedAbbr = expand(currentAbbr, expandOptions); + } catch (e) { + + } + + let item = CompletionItem.create(snippetKey); + item.documentation = removeTabStops(expandedAbbr); + item.detail = 'Emmet Abbreviation'; + item.textEdit = TextEdit.replace(abbreviationRange, expandedAbbr); + item.insertTextFormat = InsertTextFormat.Snippet; + + // Workaround for snippet suggestions items getting filtered out as the complete abbr does not start with snippetKey + item.filterText = abbreviation; + + // Workaround for the main expanded abbr not appearing before the snippet suggestions + item.sortText = '9' + abbreviation; + + snippetCompletions.push(item); + }); + + return snippetCompletions; +} + +function getCurrentWord(document: TextDocument, position: Position): string { + let currentLine = getCurrentLine(document, position); + let matches = currentLine.match(/[\w,:]*$/); + if (matches) { + return matches[0]; + } +} + +function removeTabStops(expandedWord: string): string { + return expandedWord.replace(/\$\{\d+\}/g, '').replace(/\$\{\d+:([^\}]+)\}/g, '$1'); +} + +function getCurrentLine(document: TextDocument, position: Position): string { + let offset = document.offsetAt(position); + let text = document.getText(); + for (let i = offset - 1; i >= 0; i--) { + if (text[i] === '\n') { + return text.substring(i + 1, offset); + } + } +} + + let variablesFromFile = {}; let profilesFromFile = {}; let emmetExtensionsPath = ''; @@ -150,8 +154,8 @@ export function isStyleSheet(syntax): boolean { /** * Extracts abbreviation from the given position in the given document */ -export function extractAbbreviation(document: vscode.TextDocument, position: vscode.Position): [vscode.Range, string] { - let currentLine = document.lineAt(position.line).text; +export function extractAbbreviation(document: TextDocument, position: Position): [Range, string] { + let currentLine = getCurrentLine(document, position); let result; try { result = extract(currentLine, position.character, true); @@ -162,7 +166,7 @@ export function extractAbbreviation(document: vscode.TextDocument, position: vsc return [null, '']; } - let rangeToReplace = new vscode.Range(position.line, result.location, position.line, result.location + result.abbreviation.length); + let rangeToReplace = Range.create(position.line, result.location, position.line, result.location + result.abbreviation.length); return [rangeToReplace, result.abbreviation]; } @@ -181,13 +185,13 @@ export function isAbbreviationValid(syntax: string, abbreviation: string): boole * @param syntax * @param textToReplace */ -export function getExpandOptions(syntax: string, textToReplace?: string) { +export function getExpandOptions(syntaxProfiles: object, variables: object, syntax: string, textToReplace?: string) { return { field: field, syntax: syntax, - profile: getProfile(syntax), + profile: getProfile(syntax, syntaxProfiles), addons: syntax === 'jsx' ? { 'jsx': true } : null, - variables: getVariables(), + variables: getVariables(variables), text: textToReplace ? textToReplace : null }; } @@ -196,8 +200,7 @@ export function getExpandOptions(syntax: string, textToReplace?: string) { * Maps and returns syntaxProfiles of previous format to ones compatible with new emmet modules * @param syntax */ -export function getProfile(syntax: string): any { - let profilesFromSettings = vscode.workspace.getConfiguration('emmet')['syntaxProfiles'] || {}; +export function getProfile(syntax: string, profilesFromSettings: object): any { let profilesConfig = Object.assign({}, profilesFromFile, profilesFromSettings); let options = profilesConfig[syntax]; @@ -250,21 +253,19 @@ export function getProfile(syntax: string): any { /** * Returns variables to be used while expanding snippets */ -export function getVariables(): any { - let variablesFromSettings = vscode.workspace.getConfiguration('emmet')['variables']; +export function getVariables(variablesFromSettings: object): any { return Object.assign({}, variablesFromFile, variablesFromSettings); } /** * Updates customizations from snippets.json and syntaxProfiles.json files in the directory configured in emmet.extensionsPath setting */ -export function updateExtensionsPath() { - let currentEmmetExtensionsPath = vscode.workspace.getConfiguration('emmet')['extensionsPath']; +export function updateExtensionsPath(currentEmmetExtensionsPath: string) { if (emmetExtensionsPath !== currentEmmetExtensionsPath) { emmetExtensionsPath = currentEmmetExtensionsPath; - if (emmetExtensionsPath && emmetExtensionsPath.trim()) { - let dirPath = path.isAbsolute(emmetExtensionsPath) ? emmetExtensionsPath : path.join(vscode.workspace.rootPath, emmetExtensionsPath); + if (emmetExtensionsPath && emmetExtensionsPath.trim() && path.isAbsolute(emmetExtensionsPath.trim())) { + let dirPath = emmetExtensionsPath.trim(); let snippetsPath = path.join(dirPath, 'snippets.json'); let profilesPath = path.join(dirPath, 'syntaxProfiles.json'); if (dirExists(dirPath)) { @@ -310,11 +311,9 @@ function dirExists(dirPath: string): boolean { * then nothing is returned * * @param language +* @param exlcudedLanguages Array of language ids that user has chosen to exlcude for emmet */ -export function getEmmetMode(language: string): string { - const excludedConfig = vscode.workspace.getConfiguration('emmet')['excludeLanguages']; - const excludedLanguages = Array.isArray(excludedConfig) ? excludedConfig : []; - +export function getEmmetMode(language: string, excludedLanguages: string[]): string { if (!language || excludedLanguages.indexOf(language) > -1) { return; } diff --git a/src/typings/vscode.d.ts b/src/typings/vscode.d.ts deleted file mode 100644 index b0670a9..0000000 --- a/src/typings/vscode.d.ts +++ /dev/null @@ -1,5061 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -declare module 'vscode' { - - /** - * The version of the editor. - */ - export const version: string; - - /** - * Represents a reference to a command. Provides a title which - * will be used to represent a command in the UI and, optionally, - * an array of arguments which will be passed to the command handler - * function when invoked. - */ - export interface Command { - /** - * Title of the command, like `save`. - */ - title: string; - - /** - * The identifier of the actual command handler. - * @see [commands.registerCommand](#commands.registerCommand). - */ - command: string; - - /** - * A tooltip for for command, when represented in the UI. - */ - tooltip?: string; - - /** - * Arguments that the command handler should be - * invoked with. - */ - arguments?: any[]; - } - - /** - * Represents a line of text, such as a line of source code. - * - * TextLine objects are __immutable__. When a [document](#TextDocument) changes, - * previously retrieved lines will not represent the latest state. - */ - export interface TextLine { - - /** - * The zero-based line number. - */ - readonly lineNumber: number; - - /** - * The text of this line without the line separator characters. - */ - readonly text: string; - - /** - * The range this line covers without the line separator characters. - */ - readonly range: Range; - - /** - * The range this line covers with the line separator characters. - */ - readonly rangeIncludingLineBreak: Range; - - /** - * The offset of the first character which is not a whitespace character as defined - * by `/\s/`. **Note** that if a line is all whitespaces the length of the line is returned. - */ - readonly firstNonWhitespaceCharacterIndex: number; - - /** - * Whether this line is whitespace only, shorthand - * for [TextLine.firstNonWhitespaceCharacterIndex](#TextLine.firstNonWhitespaceCharacterIndex) === [TextLine.text.length](#TextLine.text). - */ - readonly isEmptyOrWhitespace: boolean; - } - - /** - * Represents a text document, such as a source file. Text documents have - * [lines](#TextLine) and knowledge about an underlying resource like a file. - */ - export interface TextDocument { - - /** - * The associated URI for this document. Most documents have the __file__-scheme, indicating that they - * represent files on disk. However, some documents may have other schemes indicating that they are not - * available on disk. - */ - readonly uri: Uri; - - /** - * The file system path of the associated resource. Shorthand - * notation for [TextDocument.uri.fsPath](#TextDocument.uri). Independent of the uri scheme. - */ - readonly fileName: string; - - /** - * Is this document representing an untitled file. - */ - readonly isUntitled: boolean; - - /** - * The identifier of the language associated with this document. - */ - readonly languageId: string; - - /** - * The version number of this document (it will strictly increase after each - * change, including undo/redo). - */ - readonly version: number; - - /** - * `true` if there are unpersisted changes. - */ - readonly isDirty: boolean; - - /** - * `true` if the document have been closed. A closed document isn't synchronized anymore - * and won't be re-used when the same resource is opened again. - */ - readonly isClosed: boolean; - - /** - * Save the underlying file. - * - * @return A promise that will resolve to true when the file - * has been saved. If the file was not dirty or the save failed, - * will return false. - */ - save(): Thenable; - - /** - * The [end of line](#EndOfLine) sequence that is predominately - * used in this document. - */ - readonly eol: EndOfLine; - - /** - * The number of lines in this document. - */ - readonly lineCount: number; - - /** - * Returns a text line denoted by the line number. Note - * that the returned object is *not* live and changes to the - * document are not reflected. - * - * @param line A line number in [0, lineCount). - * @return A [line](#TextLine). - */ - lineAt(line: number): TextLine; - - /** - * Returns a text line denoted by the position. Note - * that the returned object is *not* live and changes to the - * document are not reflected. - * - * The position will be [adjusted](#TextDocument.validatePosition). - * - * @see [TextDocument.lineAt](#TextDocument.lineAt) - * @param position A position. - * @return A [line](#TextLine). - */ - lineAt(position: Position): TextLine; - - /** - * Converts the position to a zero-based offset. - * - * The position will be [adjusted](#TextDocument.validatePosition). - * - * @param position A position. - * @return A valid zero-based offset. - */ - offsetAt(position: Position): number; - - /** - * Converts a zero-based offset to a position. - * - * @param offset A zero-based offset. - * @return A valid [position](#Position). - */ - positionAt(offset: number): Position; - - /** - * Get the text of this document. A substring can be retrieved by providing - * a range. The range will be [adjusted](#TextDocument.validateRange). - * - * @param range Include only the text included by the range. - * @return The text inside the provided range or the entire text. - */ - getText(range?: Range): string; - - /** - * Get a word-range at the given position. By default words are defined by - * common separators, like space, -, _, etc. In addition, per languge custom - * [word definitions](#LanguageConfiguration.wordPattern) can be defined. It - * is also possible to provide a custom regular expression. *Note* that a - * custom regular expression must not match the empty string and that it will - * be ignored if it does. - * - * The position will be [adjusted](#TextDocument.validatePosition). - * - * @param position A position. - * @param regex Optional regular expression that describes what a word is. - * @return A range spanning a word, or `undefined`. - */ - getWordRangeAtPosition(position: Position, regex?: RegExp): Range | undefined; - - /** - * Ensure a range is completely contained in this document. - * - * @param range A range. - * @return The given range or a new, adjusted range. - */ - validateRange(range: Range): Range; - - /** - * Ensure a position is contained in the range of this document. - * - * @param position A position. - * @return The given position or a new, adjusted position. - */ - validatePosition(position: Position): Position; - } - - /** - * Represents a line and character position, such as - * the position of the cursor. - * - * Position objects are __immutable__. Use the [with](#Position.with) or - * [translate](#Position.translate) methods to derive new positions - * from an existing position. - */ - export class Position { - - /** - * The zero-based line value. - */ - readonly line: number; - - /** - * The zero-based character value. - */ - readonly character: number; - - /** - * @param line A zero-based line value. - * @param character A zero-based character value. - */ - constructor(line: number, character: number); - - /** - * Check if `other` is before this position. - * - * @param other A position. - * @return `true` if position is on a smaller line - * or on the same line on a smaller character. - */ - isBefore(other: Position): boolean; - - /** - * Check if `other` is before or equal to this position. - * - * @param other A position. - * @return `true` if position is on a smaller line - * or on the same line on a smaller or equal character. - */ - isBeforeOrEqual(other: Position): boolean; - - /** - * Check if `other` is after this position. - * - * @param other A position. - * @return `true` if position is on a greater line - * or on the same line on a greater character. - */ - isAfter(other: Position): boolean; - - /** - * Check if `other` is after or equal to this position. - * - * @param other A position. - * @return `true` if position is on a greater line - * or on the same line on a greater or equal character. - */ - isAfterOrEqual(other: Position): boolean; - - /** - * Check if `other` equals this position. - * - * @param other A position. - * @return `true` if the line and character of the given position are equal to - * the line and character of this position. - */ - isEqual(other: Position): boolean; - - /** - * Compare this to `other`. - * - * @param other A position. - * @return A number smaller than zero if this position is before the given position, - * a number greater than zero if this position is after the given position, or zero when - * this and the given position are equal. - */ - compareTo(other: Position): number; - - /** - * Create a new position relative to this position. - * - * @param lineDelta Delta value for the line value, default is `0`. - * @param characterDelta Delta value for the character value, default is `0`. - * @return A position which line and character is the sum of the current line and - * character and the corresponding deltas. - */ - translate(lineDelta?: number, characterDelta?: number): Position; - - /** - * Derived a new position relative to this position. - * - * @param change An object that describes a delta to this position. - * @return A position that reflects the given delta. Will return `this` position if the change - * is not changing anything. - */ - translate(change: { lineDelta?: number; characterDelta?: number; }): Position; - - /** - * Create a new position derived from this position. - * - * @param line Value that should be used as line value, default is the [existing value](#Position.line) - * @param character Value that should be used as character value, default is the [existing value](#Position.character) - * @return A position where line and character are replaced by the given values. - */ - with(line?: number, character?: number): Position; - - /** - * Derived a new position from this position. - * - * @param change An object that describes a change to this position. - * @return A position that reflects the given change. Will return `this` position if the change - * is not changing anything. - */ - with(change: { line?: number; character?: number; }): Position; - } - - /** - * A range represents an ordered pair of two positions. - * It is guaranteed that [start](#Range.start).isBeforeOrEqual([end](#Range.end)) - * - * Range objects are __immutable__. Use the [with](#Range.with), - * [intersection](#Range.intersection), or [union](#Range.union) methods - * to derive new ranges from an existing range. - */ - export class Range { - - /** - * The start position. It is before or equal to [end](#Range.end). - */ - readonly start: Position; - - /** - * The end position. It is after or equal to [start](#Range.start). - */ - readonly end: Position; - - /** - * Create a new range from two positions. If `start` is not - * before or equal to `end`, the values will be swapped. - * - * @param start A position. - * @param end A position. - */ - constructor(start: Position, end: Position); - - /** - * Create a new range from number coordinates. It is a shorter equivalent of - * using `new Range(new Position(startLine, startCharacter), new Position(endLine, endCharacter))` - * - * @param startLine A zero-based line value. - * @param startCharacter A zero-based character value. - * @param endLine A zero-based line value. - * @param endCharacter A zero-based character value. - */ - constructor(startLine: number, startCharacter: number, endLine: number, endCharacter: number); - - /** - * `true` if `start` and `end` are equal. - */ - isEmpty: boolean; - - /** - * `true` if `start.line` and `end.line` are equal. - */ - isSingleLine: boolean; - - /** - * Check if a position or a range is contained in this range. - * - * @param positionOrRange A position or a range. - * @return `true` if the position or range is inside or equal - * to this range. - */ - contains(positionOrRange: Position | Range): boolean; - - /** - * Check if `other` equals this range. - * - * @param other A range. - * @return `true` when start and end are [equal](#Position.isEqual) to - * start and end of this range. - */ - isEqual(other: Range): boolean; - - /** - * Intersect `range` with this range and returns a new range or `undefined` - * if the ranges have no overlap. - * - * @param range A range. - * @return A range of the greater start and smaller end positions. Will - * return undefined when there is no overlap. - */ - intersection(range: Range): Range | undefined; - - /** - * Compute the union of `other` with this range. - * - * @param other A range. - * @return A range of smaller start position and the greater end position. - */ - union(other: Range): Range; - - /** - * Derived a new range from this range. - * - * @param start A position that should be used as start. The default value is the [current start](#Range.start). - * @param end A position that should be used as end. The default value is the [current end](#Range.end). - * @return A range derived from this range with the given start and end position. - * If start and end are not different `this` range will be returned. - */ - with(start?: Position, end?: Position): Range; - - /** - * Derived a new range from this range. - * - * @param change An object that describes a change to this range. - * @return A range that reflects the given change. Will return `this` range if the change - * is not changing anything. - */ - with(change: { start?: Position, end?: Position }): Range; - } - - /** - * Represents a text selection in an editor. - */ - export class Selection extends Range { - - /** - * The position at which the selection starts. - * This position might be before or after [active](#Selection.active). - */ - anchor: Position; - - /** - * The position of the cursor. - * This position might be before or after [anchor](#Selection.anchor). - */ - active: Position; - - /** - * Create a selection from two postions. - * - * @param anchor A position. - * @param active A position. - */ - constructor(anchor: Position, active: Position); - - /** - * Create a selection from four coordinates. - * - * @param anchorLine A zero-based line value. - * @param anchorCharacter A zero-based character value. - * @param activeLine A zero-based line value. - * @param activeCharacter A zero-based character value. - */ - constructor(anchorLine: number, anchorCharacter: number, activeLine: number, activeCharacter: number); - - /** - * A selection is reversed if [active](#Selection.active).isBefore([anchor](#Selection.anchor)). - */ - isReversed: boolean; - } - - /** - * Represents sources that can cause [selection change events](#window.onDidChangeTextEditorSelection). - */ - export enum TextEditorSelectionChangeKind { - /** - * Selection changed due to typing in the editor. - */ - Keyboard = 1, - /** - * Selection change due to clicking in the editor. - */ - Mouse = 2, - /** - * Selection changed because a command ran. - */ - Command = 3 - } - - /** - * Represents an event describing the change in a [text editor's selections](#TextEditor.selections). - */ - export interface TextEditorSelectionChangeEvent { - /** - * The [text editor](#TextEditor) for which the selections have changed. - */ - textEditor: TextEditor; - /** - * The new value for the [text editor's selections](#TextEditor.selections). - */ - selections: Selection[]; - /** - * The [change kind](#TextEditorSelectionChangeKind) which has triggered this - * event. Can be `undefined`. - */ - kind?: TextEditorSelectionChangeKind; - } - - /** - * Represents an event describing the change in a [text editor's options](#TextEditor.options). - */ - export interface TextEditorOptionsChangeEvent { - /** - * The [text editor](#TextEditor) for which the options have changed. - */ - textEditor: TextEditor; - /** - * The new value for the [text editor's options](#TextEditor.options). - */ - options: TextEditorOptions; - } - - /** - * Represents an event describing the change of a [text editor's view column](#TextEditor.viewColumn). - */ - export interface TextEditorViewColumnChangeEvent { - /** - * The [text editor](#TextEditor) for which the options have changed. - */ - textEditor: TextEditor; - /** - * The new value for the [text editor's view column](#TextEditor.viewColumn). - */ - viewColumn: ViewColumn; - } - - /** - * Rendering style of the cursor. - */ - export enum TextEditorCursorStyle { - /** - * Render the cursor as a vertical thick line. - */ - Line = 1, - /** - * Render the cursor as a block filled. - */ - Block = 2, - /** - * Render the cursor as a thick horizontal line. - */ - Underline = 3, - /** - * Render the cursor as a vertical thin line. - */ - LineThin = 4, - /** - * Render the cursor as a block outlined. - */ - BlockOutline = 5, - /** - * Render the cursor as a thin horizontal line. - */ - UnderlineThin = 6 - } - - /** - * Rendering style of the line numbers. - */ - export enum TextEditorLineNumbersStyle { - /** - * Do not render the line numbers. - */ - Off = 0, - /** - * Render the line numbers. - */ - On = 1, - /** - * Render the line numbers with values relative to the primary cursor location. - */ - Relative = 2 - } - - /** - * Represents a [text editor](#TextEditor)'s [options](#TextEditor.options). - */ - export interface TextEditorOptions { - - /** - * The size in spaces a tab takes. This is used for two purposes: - * - the rendering width of a tab character; - * - the number of spaces to insert when [insertSpaces](#TextEditorOptions.insertSpaces) is true. - * - * When getting a text editor's options, this property will always be a number (resolved). - * When setting a text editor's options, this property is optional and it can be a number or `"auto"`. - */ - tabSize?: number | string; - - /** - * When pressing Tab insert [n](#TextEditorOptions.tabSize) spaces. - * When getting a text editor's options, this property will always be a boolean (resolved). - * When setting a text editor's options, this property is optional and it can be a boolean or `"auto"`. - */ - insertSpaces?: boolean | string; - - /** - * The rendering style of the cursor in this editor. - * When getting a text editor's options, this property will always be present. - * When setting a text editor's options, this property is optional. - */ - cursorStyle?: TextEditorCursorStyle; - - /** - * Render relative line numbers w.r.t. the current line number. - * When getting a text editor's options, this property will always be present. - * When setting a text editor's options, this property is optional. - */ - lineNumbers?: TextEditorLineNumbersStyle; - } - - /** - * Represents a handle to a set of decorations - * sharing the same [styling options](#DecorationRenderOptions) in a [text editor](#TextEditor). - * - * To get an instance of a `TextEditorDecorationType` use - * [createTextEditorDecorationType](#window.createTextEditorDecorationType). - */ - export interface TextEditorDecorationType { - - /** - * Internal representation of the handle. - */ - readonly key: string; - - /** - * Remove this decoration type and all decorations on all text editors using it. - */ - dispose(): void; - } - - /** - * Represents different [reveal](#TextEditor.revealRange) strategies in a text editor. - */ - export enum TextEditorRevealType { - /** - * The range will be revealed with as little scrolling as possible. - */ - Default = 0, - /** - * The range will always be revealed in the center of the viewport. - */ - InCenter = 1, - /** - * If the range is outside the viewport, it will be revealed in the center of the viewport. - * Otherwise, it will be revealed with as little scrolling as possible. - */ - InCenterIfOutsideViewport = 2, - /** - * The range will always be revealed at the top of the viewport. - */ - AtTop = 3 - } - - /** - * Represents different positions for rendering a decoration in an [overview ruler](#DecorationRenderOptions.overviewRulerLane). - * The overview ruler supports three lanes. - */ - export enum OverviewRulerLane { - Left = 1, - Center = 2, - Right = 4, - Full = 7 - } - - /** - * Describes the behavior of decorations when typing/editing at their edges. - */ - export enum DecorationRangeBehavior { - /** - * The decoration's range will widen when edits occur at the start or end. - */ - OpenOpen = 0, - /** - * The decoration's range will not widen when edits occur at the start of end. - */ - ClosedClosed = 1, - /** - * The decoration's range will widen when edits occur at the start, but not at the end. - */ - OpenClosed = 2, - /** - * The decoration's range will widen when edits occur at the end, but not at the start. - */ - ClosedOpen = 3 - } - - /** - * Represents options to configure the behavior of showing a [document](#TextDocument) in an [editor](#TextEditor). - */ - export interface TextDocumentShowOptions { - /** - * An optional view column in which the [editor](#TextEditor) should be shown. - * The default is the [one](#ViewColumn.One), other values are adjusted to - * be __Min(column, columnCount + 1)__. - */ - viewColumn?: ViewColumn; - - /** - * An optional flag that when `true` will stop the [editor](#TextEditor) from taking focus. - */ - preserveFocus?: boolean; - - /** - * An optional flag that controls if an [editor](#TextEditor)-tab will be replaced - * with the next editor or if it will be kept. - */ - preview?: boolean; - } - - /** - * A reference to one of the workbench colors as defined in https://code.visualstudio.com/docs/getstarted/theme-color-reference. - * Using a theme color is preferred over a custom color as it gives theme authors and users the possibility to change the color. - */ - export class ThemeColor { - - /** - * Creates a reference to a theme color. - * @param id of the color. The available colors are listed in https://code.visualstudio.com/docs/getstarted/theme-color-reference. - */ - constructor(id: string); - } - - /** - * Represents theme specific rendering styles for a [text editor decoration](#TextEditorDecorationType). - */ - export interface ThemableDecorationRenderOptions { - /** - * Background color of the decoration. Use rgba() and define transparent background colors to play well with other decorations. - * Alternativly a color from the color registry an be [referenced](#ColorIdentifier). - */ - backgroundColor?: string | ThemeColor; - - /** - * CSS styling property that will be applied to text enclosed by a decoration. - */ - outline?: string; - - /** - * CSS styling property that will be applied to text enclosed by a decoration. - * Better use 'outline' for setting one or more of the individual outline properties. - */ - outlineColor?: string | ThemeColor; - - /** - * CSS styling property that will be applied to text enclosed by a decoration. - * Better use 'outline' for setting one or more of the individual outline properties. - */ - outlineStyle?: string; - - /** - * CSS styling property that will be applied to text enclosed by a decoration. - * Better use 'outline' for setting one or more of the individual outline properties. - */ - outlineWidth?: string; - - /** - * CSS styling property that will be applied to text enclosed by a decoration. - */ - border?: string; - - /** - * CSS styling property that will be applied to text enclosed by a decoration. - * Better use 'border' for setting one or more of the individual border properties. - */ - borderColor?: string | ThemeColor; - - /** - * CSS styling property that will be applied to text enclosed by a decoration. - * Better use 'border' for setting one or more of the individual border properties. - */ - borderRadius?: string; - - /** - * CSS styling property that will be applied to text enclosed by a decoration. - * Better use 'border' for setting one or more of the individual border properties. - */ - borderSpacing?: string; - - /** - * CSS styling property that will be applied to text enclosed by a decoration. - * Better use 'border' for setting one or more of the individual border properties. - */ - borderStyle?: string; - - /** - * CSS styling property that will be applied to text enclosed by a decoration. - * Better use 'border' for setting one or more of the individual border properties. - */ - borderWidth?: string; - - /** - * CSS styling property that will be applied to text enclosed by a decoration. - */ - textDecoration?: string; - - /** - * CSS styling property that will be applied to text enclosed by a decoration. - */ - cursor?: string; - - /** - * CSS styling property that will be applied to text enclosed by a decoration. - */ - color?: string | ThemeColor; - - /** - * CSS styling property that will be applied to text enclosed by a decoration. - */ - letterSpacing?: string; - - /** - * An **absolute path** or an URI to an image to be rendered in the gutter. - */ - gutterIconPath?: string | Uri; - - /** - * Specifies the size of the gutter icon. - * Available values are 'auto', 'contain', 'cover' and any percentage value. - * For further information: https://msdn.microsoft.com/en-us/library/jj127316(v=vs.85).aspx - */ - gutterIconSize?: string; - - /** - * The color of the decoration in the overview ruler. Use rgba() and define transparent colors to play well with other decorations. - */ - overviewRulerColor?: string | ThemeColor; - - /** - * Defines the rendering options of the attachment that is inserted before the decorated text - */ - before?: ThemableDecorationAttachmentRenderOptions; - - /** - * Defines the rendering options of the attachment that is inserted after the decorated text - */ - after?: ThemableDecorationAttachmentRenderOptions; - } - - export interface ThemableDecorationAttachmentRenderOptions { - /** - * Defines a text content that is shown in the attachment. Either an icon or a text can be shown, but not both. - */ - contentText?: string; - /** - * An **absolute path** or an URI to an image to be rendered in the attachment. Either an icon - * or a text can be shown, but not both. - */ - contentIconPath?: string | Uri; - /** - * CSS styling property that will be applied to the decoration attachment. - */ - border?: string; - /** - * CSS styling property that will be applied to text enclosed by a decoration. - */ - borderColor?: string | ThemeColor; - /** - * CSS styling property that will be applied to the decoration attachment. - */ - textDecoration?: string; - /** - * CSS styling property that will be applied to the decoration attachment. - */ - color?: string | ThemeColor; - /** - * CSS styling property that will be applied to the decoration attachment. - */ - backgroundColor?: string | ThemeColor; - /** - * CSS styling property that will be applied to the decoration attachment. - */ - margin?: string; - /** - * CSS styling property that will be applied to the decoration attachment. - */ - width?: string; - /** - * CSS styling property that will be applied to the decoration attachment. - */ - height?: string; - } - - /** - * Represents rendering styles for a [text editor decoration](#TextEditorDecorationType). - */ - export interface DecorationRenderOptions extends ThemableDecorationRenderOptions { - /** - * Should the decoration be rendered also on the whitespace after the line text. - * Defaults to `false`. - */ - isWholeLine?: boolean; - - /** - * Customize the growing behavior of the decoration when edits occur at the edges of the decoration's range. - * Defaults to `DecorationRangeBehavior.OpenOpen`. - */ - rangeBehavior?: DecorationRangeBehavior; - - /** - * The position in the overview ruler where the decoration should be rendered. - */ - overviewRulerLane?: OverviewRulerLane; - - /** - * Overwrite options for light themes. - */ - light?: ThemableDecorationRenderOptions; - - /** - * Overwrite options for dark themes. - */ - dark?: ThemableDecorationRenderOptions; - } - - /** - * Represents options for a specific decoration in a [decoration set](#TextEditorDecorationType). - */ - export interface DecorationOptions { - - /** - * Range to which this decoration is applied. The range must not be empty. - */ - range: Range; - - /** - * A message that should be rendered when hovering over the decoration. - */ - hoverMessage?: MarkedString | MarkedString[]; - - /** - * Render options applied to the current decoration. For performance reasons, keep the - * number of decoration specific options small, and use decoration types whereever possible. - */ - renderOptions?: DecorationInstanceRenderOptions; - } - - export interface ThemableDecorationInstanceRenderOptions { - /** - * Defines the rendering options of the attachment that is inserted before the decorated text - */ - before?: ThemableDecorationAttachmentRenderOptions; - - /** - * Defines the rendering options of the attachment that is inserted after the decorated text - */ - after?: ThemableDecorationAttachmentRenderOptions; - } - - export interface DecorationInstanceRenderOptions extends ThemableDecorationInstanceRenderOptions { - /** - * Overwrite options for light themes. - */ - light?: ThemableDecorationInstanceRenderOptions; - - /** - * Overwrite options for dark themes. - */ - dark?: ThemableDecorationInstanceRenderOptions; - } - - /** - * Represents an editor that is attached to a [document](#TextDocument). - */ - export interface TextEditor { - - /** - * The document associated with this text editor. The document will be the same for the entire lifetime of this text editor. - */ - document: TextDocument; - - /** - * The primary selection on this text editor. Shorthand for `TextEditor.selections[0]`. - */ - selection: Selection; - - /** - * The selections in this text editor. The primary selection is always at index 0. - */ - selections: Selection[]; - - /** - * Text editor options. - */ - options: TextEditorOptions; - - /** - * The column in which this editor shows. Will be `undefined` in case this - * isn't one of the three main editors, e.g an embedded editor. - */ - viewColumn?: ViewColumn; - - /** - * Perform an edit on the document associated with this text editor. - * - * The given callback-function is invoked with an [edit-builder](#TextEditorEdit) which must - * be used to make edits. Note that the edit-builder is only valid while the - * callback executes. - * - * @param callback A function which can create edits using an [edit-builder](#TextEditorEdit). - * @param options The undo/redo behavior around this edit. By default, undo stops will be created before and after this edit. - * @return A promise that resolves with a value indicating if the edits could be applied. - */ - edit(callback: (editBuilder: TextEditorEdit) => void, options?: { undoStopBefore: boolean; undoStopAfter: boolean; }): Thenable; - - /** - * Insert a [snippet](#SnippetString) and put the editor into snippet mode. "Snippet mode" - * means the editor adds placeholders and additionals cursors so that the user can complete - * or accept the snippet. - * - * @param snippet The snippet to insert in this edit. - * @param location Position or range at which to insert the snippet, defaults to the current editor selection or selections. - * @param options The undo/redo behavior around this edit. By default, undo stops will be created before and after this edit. - * @return A promise that resolves with a value indicating if the snippet could be inserted. Note that the promise does not signal - * that the snippet is completely filled-in or accepted. - */ - insertSnippet(snippet: SnippetString, location?: Position | Range | Position[] | Range[], options?: { undoStopBefore: boolean; undoStopAfter: boolean; }): Thenable; - - /** - * Adds a set of decorations to the text editor. If a set of decorations already exists with - * the given [decoration type](#TextEditorDecorationType), they will be replaced. - * - * @see [createTextEditorDecorationType](#window.createTextEditorDecorationType). - * - * @param decorationType A decoration type. - * @param rangesOrOptions Either [ranges](#Range) or more detailed [options](#DecorationOptions). - */ - setDecorations(decorationType: TextEditorDecorationType, rangesOrOptions: Range[] | DecorationOptions[]): void; - - /** - * Scroll as indicated by `revealType` in order to reveal the given range. - * - * @param range A range. - * @param revealType The scrolling strategy for revealing `range`. - */ - revealRange(range: Range, revealType?: TextEditorRevealType): void; - - /** - * Show the text editor. - * - * @deprecated **This method is deprecated.** Use [window.showTextDocument](#window.showTextDocument) - * instead. This method shows unexpected behavior and will be removed in the next major update. - * - * @param column The [column](#ViewColumn) in which to show this editor. - */ - show(column?: ViewColumn): void; - - /** - * Hide the text editor. - * - * @deprecated **This method is deprecated.** Use the command 'workbench.action.closeActiveEditor' instead. - * This method shows unexpected behavior and will be removed in the next major update. - */ - hide(): void; - } - - /** - * Represents an end of line character sequence in a [document](#TextDocument). - */ - export enum EndOfLine { - /** - * The line feed `\n` character. - */ - LF = 1, - /** - * The carriage return line feed `\r\n` sequence. - */ - CRLF = 2 - } - - /** - * A complex edit that will be applied in one transaction on a TextEditor. - * This holds a description of the edits and if the edits are valid (i.e. no overlapping regions, document was not changed in the meantime, etc.) - * they can be applied on a [document](#TextDocument) associated with a [text editor](#TextEditor). - * - */ - export interface TextEditorEdit { - /** - * Replace a certain text region with a new value. - * You can use \r\n or \n in `value` and they will be normalized to the current [document](#TextDocument). - * - * @param location The range this operation should remove. - * @param value The new text this operation should insert after removing `location`. - */ - replace(location: Position | Range | Selection, value: string): void; - - /** - * Insert text at a location. - * You can use \r\n or \n in `value` and they will be normalized to the current [document](#TextDocument). - * Although the equivalent text edit can be made with [replace](#TextEditorEdit.replace), `insert` will produce a different resulting selection (it will get moved). - * - * @param location The position where the new text should be inserted. - * @param value The new text this operation should insert. - */ - insert(location: Position, value: string): void; - - /** - * Delete a certain text region. - * - * @param location The range this operation should remove. - */ - delete(location: Range | Selection): void; - - /** - * Set the end of line sequence. - * - * @param endOfLine The new end of line for the [document](#TextDocument). - */ - setEndOfLine(endOfLine: EndOfLine): void; - } - - /** - * A universal resource identifier representing either a file on disk - * or another resource, like untitled resources. - */ - export class Uri { - - /** - * Create an URI from a file system path. The [scheme](#Uri.scheme) - * will be `file`. - * - * @param path A file system or UNC path. - * @return A new Uri instance. - */ - static file(path: string): Uri; - - /** - * Create an URI from a string. Will throw if the given value is not - * valid. - * - * @param value The string value of an Uri. - * @return A new Uri instance. - */ - static parse(value: string): Uri; - - /** - * Scheme is the `http` part of `http://www.msft.com/some/path?query#fragment`. - * The part before the first colon. - */ - readonly scheme: string; - - /** - * Authority is the `www.msft.com` part of `http://www.msft.com/some/path?query#fragment`. - * The part between the first double slashes and the next slash. - */ - readonly authority: string; - - /** - * Path is the `/some/path` part of `http://www.msft.com/some/path?query#fragment`. - */ - readonly path: string; - - /** - * Query is the `query` part of `http://www.msft.com/some/path?query#fragment`. - */ - readonly query: string; - - /** - * Fragment is the `fragment` part of `http://www.msft.com/some/path?query#fragment`. - */ - readonly fragment: string; - - /** - * The string representing the corresponding file system path of this Uri. - * - * Will handle UNC paths and normalize windows drive letters to lower-case. Also - * uses the platform specific path separator. Will *not* validate the path for - * invalid characters and semantics. Will *not* look at the scheme of this Uri. - */ - readonly fsPath: string; - - /** - * Derive a new Uri from this Uri. - * - * ```ts - * let file = Uri.parse('before:some/file/path'); - * let other = file.with({ scheme: 'after' }); - * assert.ok(other.toString() === 'after:some/file/path'); - * ``` - * - * @param change An object that describes a change to this Uri. To unset components use `null` or - * the empty string. - * @return A new Uri that reflects the given change. Will return `this` Uri if the change - * is not changing anything. - */ - with(change: { scheme?: string; authority?: string; path?: string; query?: string; fragment?: string }): Uri; - - /** - * Returns a string representation of this Uri. The representation and normalization - * of a URI depends on the scheme. The resulting string can be safely used with - * [Uri.parse](#Uri.parse). - * - * @param skipEncoding Do not percentage-encode the result, defaults to `false`. Note that - * the `#` and `?` characters occuring in the path will always be encoded. - * @returns A string representation of this Uri. - */ - toString(skipEncoding?: boolean): string; - - /** - * Returns a JSON representation of this Uri. - * - * @return An object. - */ - toJSON(): any; - } - - /** - * A cancellation token is passed to an asynchronous or long running - * operation to request cancellation, like cancelling a request - * for completion items because the user continued to type. - * - * To get an instance of a `CancellationToken` use a - * [CancellationTokenSource](#CancellationTokenSource). - */ - export interface CancellationToken { - - /** - * Is `true` when the token has been cancelled, `false` otherwise. - */ - isCancellationRequested: boolean; - - /** - * An [event](#Event) which fires upon cancellation. - */ - onCancellationRequested: Event; - } - - /** - * A cancellation source creates and controls a [cancellation token](#CancellationToken). - */ - export class CancellationTokenSource { - - /** - * The cancellation token of this source. - */ - token: CancellationToken; - - /** - * Signal cancellation on the token. - */ - cancel(): void; - - /** - * Dispose object and free resources. Will call [cancel](#CancellationTokenSource.cancel). - */ - dispose(): void; - } - - /** - * Represents a type which can release resources, such - * as event listening or a timer. - */ - export class Disposable { - - /** - * Combine many disposable-likes into one. Use this method - * when having objects with a dispose function which are not - * instances of Disposable. - * - * @param disposableLikes Objects that have at least a `dispose`-function member. - * @return Returns a new disposable which, upon dispose, will - * dispose all provided disposables. - */ - static from(...disposableLikes: { dispose: () => any }[]): Disposable; - - /** - * Creates a new Disposable calling the provided function - * on dispose. - * @param callOnDispose Function that disposes something. - */ - constructor(callOnDispose: Function); - - /** - * Dispose this object. - */ - dispose(): any; - } - - /** - * Represents a typed event. - * - * A function that represents an event to which you subscribe by calling it with - * a listener function as argument. - * - * @sample `item.onDidChange(function(event) { console.log("Event happened: " + event); });` - */ - export interface Event { - - /** - * A function that represents an event to which you subscribe by calling it with - * a listener function as argument. - * - * @param listener The listener function will be called when the event happens. - * @param thisArgs The `this`-argument which will be used when calling the event listener. - * @param disposables An array to which a [disposable](#Disposable) will be added. - * @return A disposable which unsubscribes the event listener. - */ - (listener: (e: T) => any, thisArgs?: any, disposables?: Disposable[]): Disposable; - } - - /** - * An event emitter can be used to create and manage an [event](#Event) for others - * to subscribe to. One emitter always owns one event. - * - * Use this class if you want to provide event from within your extension, for instance - * inside a [TextDocumentContentProvider](#TextDocumentContentProvider) or when providing - * API to other extensions. - */ - export class EventEmitter { - - /** - * The event listeners can subscribe to. - */ - event: Event; - - /** - * Notify all subscribers of the [event](EventEmitter#event). Failure - * of one or more listener will not fail this function call. - * - * @param data The event object. - */ - fire(data?: T): void; - - /** - * Dispose this object and free resources. - */ - dispose(): void; - } - - /** - * A file system watcher notifies about changes to files and folders - * on disk. - * - * To get an instance of a `FileSystemWatcher` use - * [createFileSystemWatcher](#workspace.createFileSystemWatcher). - */ - export interface FileSystemWatcher extends Disposable { - - /** - * true if this file system watcher has been created such that - * it ignores creation file system events. - */ - ignoreCreateEvents: boolean; - - /** - * true if this file system watcher has been created such that - * it ignores change file system events. - */ - ignoreChangeEvents: boolean; - - /** - * true if this file system watcher has been created such that - * it ignores delete file system events. - */ - ignoreDeleteEvents: boolean; - - /** - * An event which fires on file/folder creation. - */ - onDidCreate: Event; - - /** - * An event which fires on file/folder change. - */ - onDidChange: Event; - - /** - * An event which fires on file/folder deletion. - */ - onDidDelete: Event; - } - - /** - * A text document content provider allows to add readonly documents - * to the editor, such as source from a dll or generated html from md. - * - * Content providers are [registered](#workspace.registerTextDocumentContentProvider) - * for a [uri-scheme](#Uri.scheme). When a uri with that scheme is to - * be [loaded](#workspace.openTextDocument) the content provider is - * asked. - */ - export interface TextDocumentContentProvider { - - /** - * An event to signal a resource has changed. - */ - onDidChange?: Event; - - /** - * Provide textual content for a given uri. - * - * The editor will use the returned string-content to create a readonly - * [document](#TextDocument). Resources allocated should be released when - * the corresponding document has been [closed](#workspace.onDidCloseTextDocument). - * - * @param uri An uri which scheme matches the scheme this provider was [registered](#workspace.registerTextDocumentContentProvider) for. - * @param token A cancellation token. - * @return A string or a thenable that resolves to such. - */ - provideTextDocumentContent(uri: Uri, token: CancellationToken): ProviderResult; - } - - /** - * Represents an item that can be selected from - * a list of items. - */ - export interface QuickPickItem { - - /** - * A human readable string which is rendered prominent. - */ - label: string; - - /** - * A human readable string which is rendered less prominent. - */ - description: string; - - /** - * A human readable string which is rendered less prominent. - */ - detail?: string; - } - - /** - * Options to configure the behavior of the quick pick UI. - */ - export interface QuickPickOptions { - /** - * An optional flag to include the description when filtering the picks. - */ - matchOnDescription?: boolean; - - /** - * An optional flag to include the detail when filtering the picks. - */ - matchOnDetail?: boolean; - - /** - * An optional string to show as place holder in the input box to guide the user what to pick on. - */ - placeHolder?: string; - - /** - * Set to `true` to keep the picker open when focus moves to another part of the editor or to another window. - */ - ignoreFocusOut?: boolean; - - /** - * An optional function that is invoked whenever an item is selected. - */ - onDidSelectItem?(item: T | string): any; - } - - /** - * Represents an action that is shown with an information, warning, or - * error message. - * - * @see [showInformationMessage](#window.showInformationMessage) - * @see [showWarningMessage](#window.showWarningMessage) - * @see [showErrorMessage](#window.showErrorMessage) - */ - export interface MessageItem { - - /** - * A short title like 'Retry', 'Open Log' etc. - */ - title: string; - - /** - * Indicates that this item replaces the default - * 'Close' action. - */ - isCloseAffordance?: boolean; - } - - /** - * Options to configure the behavior of the message. - * - * @see [showInformationMessage](#window.showInformationMessage) - * @see [showWarningMessage](#window.showWarningMessage) - * @see [showErrorMessage](#window.showErrorMessage) - */ - export interface MessageOptions { - - /** - * Indicates that this message should be modal. - */ - modal?: boolean; - } - - /** - * Options to configure the behavior of the input box UI. - */ - export interface InputBoxOptions { - - /** - * The value to prefill in the input box. - */ - value?: string; - - /** - * Selection of the prefilled [`value`](#InputBoxOptions.value). Defined as tuple of two number where the - * first is the inclusive start index and the second the exclusive end index. When `undefined` the whole - * word will be selected, when empty (start equals end) only the cursor will be set, - * otherwise the defined range will be selected. - */ - valueSelection?: [number, number]; - - /** - * The text to display underneath the input box. - */ - prompt?: string; - - /** - * An optional string to show as place holder in the input box to guide the user what to type. - */ - placeHolder?: string; - - /** - * Set to `true` to show a password prompt that will not show the typed value. - */ - password?: boolean; - - /** - * Set to `true` to keep the input box open when focus moves to another part of the editor or to another window. - */ - ignoreFocusOut?: boolean; - - /** - * An optional function that will be called to validate input and to give a hint - * to the user. - * - * @param value The current value of the input box. - * @return A human readable string which is presented as diagnostic message. - * Return `undefined`, `null`, or the empty string when 'value' is valid. - */ - validateInput?(value: string): string | undefined | null; - } - - /** - * A document filter denotes a document by different properties like - * the [language](#TextDocument.languageId), the [scheme](#Uri.scheme) of - * its resource, or a glob-pattern that is applied to the [path](#TextDocument.fileName). - * - * @sample A language filter that applies to typescript files on disk: `{ language: 'typescript', scheme: 'file' }` - * @sample A language filter that applies to all package.json paths: `{ language: 'json', pattern: '**∕package.json' }` - */ - export interface DocumentFilter { - - /** - * A language id, like `typescript`. - */ - language?: string; - - /** - * A Uri [scheme](#Uri.scheme), like `file` or `untitled`. - */ - scheme?: string; - - /** - * A glob pattern, like `*.{ts,js}`. - */ - pattern?: string; - } - - /** - * A language selector is the combination of one or many language identifiers - * and [language filters](#DocumentFilter). - * - * @sample `let sel:DocumentSelector = 'typescript'`; - * @sample `let sel:DocumentSelector = ['typescript', { language: 'json', pattern: '**∕tsconfig.json' }]`; - */ - export type DocumentSelector = string | DocumentFilter | (string | DocumentFilter)[]; - - - /** - * A provider result represents the values a provider, like the [`HoverProvider`](#HoverProvider), - * may return. For once this is the actual result type `T`, like `Hover`, or a thenable that resolves - * to that type `T`. In addition, `null` and `undefined` can be returned - either directly or from a - * thenable. - * - * The snippets below are all valid implementions of the [`HoverProvider`](#HoverProvider): - * - * ```ts - * let a: HoverProvider = { - * provideHover(doc, pos, token): ProviderResult { - * return new Hover('Hello World'); - * } - * } - * - * let b: HoverProvider = { - * provideHover(doc, pos, token): ProviderResult { - * return new Promise(resolve => { - * resolve(new Hover('Hello World')); - * }); - * } - * } - * - * let c: HoverProvider = { - * provideHover(doc, pos, token): ProviderResult { - * return; // undefined - * } - * } - * ``` - */ - export type ProviderResult = T | undefined | null | Thenable; - - /** - * Contains additional diagnostic information about the context in which - * a [code action](#CodeActionProvider.provideCodeActions) is run. - */ - export interface CodeActionContext { - - /** - * An array of diagnostics. - */ - readonly diagnostics: Diagnostic[]; - } - - /** - * The code action interface defines the contract between extensions and - * the [light bulb](https://code.visualstudio.com/docs/editor/editingevolved#_code-action) feature. - * - * A code action can be any command that is [known](#commands.getCommands) to the system. - */ - export interface CodeActionProvider { - - /** - * Provide commands for the given document and range. - * - * @param document The document in which the command was invoked. - * @param range The range for which the command was invoked. - * @param context Context carrying additional information. - * @param token A cancellation token. - * @return An array of commands or a thenable of such. The lack of a result can be - * signaled by returning `undefined`, `null`, or an empty array. - */ - provideCodeActions(document: TextDocument, range: Range, context: CodeActionContext, token: CancellationToken): ProviderResult; - } - - /** - * A code lens represents a [command](#Command) that should be shown along with - * source text, like the number of references, a way to run tests, etc. - * - * A code lens is _unresolved_ when no command is associated to it. For performance - * reasons the creation of a code lens and resolving should be done to two stages. - * - * @see [CodeLensProvider.provideCodeLenses](#CodeLensProvider.provideCodeLenses) - * @see [CodeLensProvider.resolveCodeLens](#CodeLensProvider.resolveCodeLens) - */ - export class CodeLens { - - /** - * The range in which this code lens is valid. Should only span a single line. - */ - range: Range; - - /** - * The command this code lens represents. - */ - command?: Command; - - /** - * `true` when there is a command associated. - */ - readonly isResolved: boolean; - - /** - * Creates a new code lens object. - * - * @param range The range to which this code lens applies. - * @param command The command associated to this code lens. - */ - constructor(range: Range, command?: Command); - } - - /** - * A code lens provider adds [commands](#Command) to source text. The commands will be shown - * as dedicated horizontal lines in between the source text. - */ - export interface CodeLensProvider { - - /** - * An optional event to signal that the code lenses from this provider have changed. - */ - onDidChangeCodeLenses?: Event; - - /** - * Compute a list of [lenses](#CodeLens). This call should return as fast as possible and if - * computing the commands is expensive implementors should only return code lens objects with the - * range set and implement [resolve](#CodeLensProvider.resolveCodeLens). - * - * @param document The document in which the command was invoked. - * @param token A cancellation token. - * @return An array of code lenses or a thenable that resolves to such. The lack of a result can be - * signaled by returning `undefined`, `null`, or an empty array. - */ - provideCodeLenses(document: TextDocument, token: CancellationToken): ProviderResult; - - /** - * This function will be called for each visible code lens, usually when scrolling and after - * calls to [compute](#CodeLensProvider.provideCodeLenses)-lenses. - * - * @param codeLens code lens that must be resolved. - * @param token A cancellation token. - * @return The given, resolved code lens or thenable that resolves to such. - */ - resolveCodeLens?(codeLens: CodeLens, token: CancellationToken): ProviderResult; - } - - /** - * The definition of a symbol represented as one or many [locations](#Location). - * For most programming languages there is only one location at which a symbol is - * defined. - */ - export type Definition = Location | Location[]; - - /** - * The definition provider interface defines the contract between extensions and - * the [go to definition](https://code.visualstudio.com/docs/editor/editingevolved#_go-to-definition) - * and peek definition features. - */ - export interface DefinitionProvider { - - /** - * Provide the definition of the symbol at the given position and document. - * - * @param document The document in which the command was invoked. - * @param position The position at which the command was invoked. - * @param token A cancellation token. - * @return A definition or a thenable that resolves to such. The lack of a result can be - * signaled by returning `undefined` or `null`. - */ - provideDefinition(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; - } - - /** - * The implemenetation provider interface defines the contract between extensions and - * the go to implementation feature. - */ - export interface ImplementationProvider { - - /** - * Provide the implementations of the symbol at the given position and document. - * - * @param document The document in which the command was invoked. - * @param position The position at which the command was invoked. - * @param token A cancellation token. - * @return A definition or a thenable that resolves to such. The lack of a result can be - * signaled by returning `undefined` or `null`. - */ - provideImplementation(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; - } - - /** - * The type definition provider defines the contract between extensions and - * the go to type definition feature. - */ - export interface TypeDefinitionProvider { - - /** - * Provide the type definition of the symbol at the given position and document. - * - * @param document The document in which the command was invoked. - * @param position The position at which the command was invoked. - * @param token A cancellation token. - * @return A definition or a thenable that resolves to such. The lack of a result can be - * signaled by returning `undefined` or `null`. - */ - provideTypeDefinition(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; - } - - /** - * MarkedString can be used to render human readable text. It is either a markdown string - * or a code-block that provides a language and a code snippet. Note that - * markdown strings will be sanitized - that means html will be escaped. - */ - export type MarkedString = string | { language: string; value: string }; - - /** - * A hover represents additional information for a symbol or word. Hovers are - * rendered in a tooltip-like widget. - */ - export class Hover { - - /** - * The contents of this hover. - */ - contents: MarkedString[]; - - /** - * The range to which this hover applies. When missing, the - * editor will use the range at the current position or the - * current position itself. - */ - range?: Range; - - /** - * Creates a new hover object. - * - * @param contents The contents of the hover. - * @param range The range to which the hover applies. - */ - constructor(contents: MarkedString | MarkedString[], range?: Range); - } - - /** - * The hover provider interface defines the contract between extensions and - * the [hover](https://code.visualstudio.com/docs/editor/intellisense)-feature. - */ - export interface HoverProvider { - - /** - * Provide a hover for the given position and document. Multiple hovers at the same - * position will be merged by the editor. A hover can have a range which defaults - * to the word range at the position when omitted. - * - * @param document The document in which the command was invoked. - * @param position The position at which the command was invoked. - * @param token A cancellation token. - * @return A hover or a thenable that resolves to such. The lack of a result can be - * signaled by returning `undefined` or `null`. - */ - provideHover(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; - } - - /** - * A document highlight kind. - */ - export enum DocumentHighlightKind { - - /** - * A textual occurrence. - */ - Text = 0, - - /** - * Read-access of a symbol, like reading a variable. - */ - Read = 1, - - /** - * Write-access of a symbol, like writing to a variable. - */ - Write = 2 - } - - /** - * A document highlight is a range inside a text document which deserves - * special attention. Usually a document highlight is visualized by changing - * the background color of its range. - */ - export class DocumentHighlight { - - /** - * The range this highlight applies to. - */ - range: Range; - - /** - * The highlight kind, default is [text](#DocumentHighlightKind.Text). - */ - kind?: DocumentHighlightKind; - - /** - * Creates a new document highlight object. - * - * @param range The range the highlight applies to. - * @param kind The highlight kind, default is [text](#DocumentHighlightKind.Text). - */ - constructor(range: Range, kind?: DocumentHighlightKind); - } - - /** - * The document highlight provider interface defines the contract between extensions and - * the word-highlight-feature. - */ - export interface DocumentHighlightProvider { - - /** - * Provide a set of document highlights, like all occurrences of a variable or - * all exit-points of a function. - * - * @param document The document in which the command was invoked. - * @param position The position at which the command was invoked. - * @param token A cancellation token. - * @return An array of document highlights or a thenable that resolves to such. The lack of a result can be - * signaled by returning `undefined`, `null`, or an empty array. - */ - provideDocumentHighlights(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; - } - - /** - * A symbol kind. - */ - export enum SymbolKind { - File = 0, - Module = 1, - Namespace = 2, - Package = 3, - Class = 4, - Method = 5, - Property = 6, - Field = 7, - Constructor = 8, - Enum = 9, - Interface = 10, - Function = 11, - Variable = 12, - Constant = 13, - String = 14, - Number = 15, - Boolean = 16, - Array = 17, - Object = 18, - Key = 19, - Null = 20, - EnumMember = 21, - Struct = 22, - Event = 23, - Operator = 24, - TypeParameter = 25 - } - - /** - * Represents information about programming constructs like variables, classes, - * interfaces etc. - */ - export class SymbolInformation { - - /** - * The name of this symbol. - */ - name: string; - - /** - * The name of the symbol containing this symbol. - */ - containerName: string; - - /** - * The kind of this symbol. - */ - kind: SymbolKind; - - /** - * The location of this symbol. - */ - location: Location; - - /** - * Creates a new symbol information object. - * - * @param name The name of the symbol. - * @param kind The kind of the symbol. - * @param containerName The name of the symbol containing the symbol. - * @param location The the location of the symbol. - */ - constructor(name: string, kind: SymbolKind, containerName: string, location: Location); - - /** - * @deprecated Please use the constructor taking a [location](#Location) object. - * - * Creates a new symbol information object. - * - * @param name The name of the symbol. - * @param kind The kind of the symbol. - * @param range The range of the location of the symbol. - * @param uri The resource of the location of symbol, defaults to the current document. - * @param containerName The name of the symbol containing the symbol. - */ - constructor(name: string, kind: SymbolKind, range: Range, uri?: Uri, containerName?: string); - } - - /** - * The document symbol provider interface defines the contract between extensions and - * the [go to symbol](https://code.visualstudio.com/docs/editor/editingevolved#_go-to-symbol)-feature. - */ - export interface DocumentSymbolProvider { - - /** - * Provide symbol information for the given document. - * - * @param document The document in which the command was invoked. - * @param token A cancellation token. - * @return An array of document highlights or a thenable that resolves to such. The lack of a result can be - * signaled by returning `undefined`, `null`, or an empty array. - */ - provideDocumentSymbols(document: TextDocument, token: CancellationToken): ProviderResult; - } - - /** - * The workspace symbol provider interface defines the contract between extensions and - * the [symbol search](https://code.visualstudio.com/docs/editor/editingevolved#_open-symbol-by-name)-feature. - */ - export interface WorkspaceSymbolProvider { - - /** - * Project-wide search for a symbol matching the given query string. It is up to the provider - * how to search given the query string, like substring, indexOf etc. To improve performance implementors can - * skip the [location](#SymbolInformation.location) of symbols and implement `resolveWorkspaceSymbol` to do that - * later. - * - * @param query A non-empty query string. - * @param token A cancellation token. - * @return An array of document highlights or a thenable that resolves to such. The lack of a result can be - * signaled by returning `undefined`, `null`, or an empty array. - */ - provideWorkspaceSymbols(query: string, token: CancellationToken): ProviderResult; - - /** - * Given a symbol fill in its [location](#SymbolInformation.location). This method is called whenever a symbol - * is selected in the UI. Providers can implement this method and return incomplete symbols from - * [`provideWorkspaceSymbols`](#WorkspaceSymbolProvider.provideWorkspaceSymbols) which often helps to improve - * performance. - * - * @param symbol The symbol that is to be resolved. Guaranteed to be an instance of an object returned from an - * earlier call to `provideWorkspaceSymbols`. - * @param token A cancellation token. - * @return The resolved symbol or a thenable that resolves to that. When no result is returned, - * the given `symbol` is used. - */ - resolveWorkspaceSymbol?(symbol: SymbolInformation, token: CancellationToken): ProviderResult; - } - - /** - * Value-object that contains additional information when - * requesting references. - */ - export interface ReferenceContext { - - /** - * Include the declaration of the current symbol. - */ - includeDeclaration: boolean; - } - - /** - * The reference provider interface defines the contract between extensions and - * the [find references](https://code.visualstudio.com/docs/editor/editingevolved#_peek)-feature. - */ - export interface ReferenceProvider { - - /** - * Provide a set of project-wide references for the given position and document. - * - * @param document The document in which the command was invoked. - * @param position The position at which the command was invoked. - * @param context - * @param token A cancellation token. - * @return An array of locations or a thenable that resolves to such. The lack of a result can be - * signaled by returning `undefined`, `null`, or an empty array. - */ - provideReferences(document: TextDocument, position: Position, context: ReferenceContext, token: CancellationToken): ProviderResult; - } - - /** - * A text edit represents edits that should be applied - * to a document. - */ - export class TextEdit { - - /** - * Utility to create a replace edit. - * - * @param range A range. - * @param newText A string. - * @return A new text edit object. - */ - static replace(range: Range, newText: string): TextEdit; - - /** - * Utility to create an insert edit. - * - * @param position A position, will become an empty range. - * @param newText A string. - * @return A new text edit object. - */ - static insert(position: Position, newText: string): TextEdit; - - /** - * Utility to create a delete edit. - * - * @param range A range. - * @return A new text edit object. - */ - static delete(range: Range): TextEdit; - - /** - * Utility to create an eol-edit. - * - * @param eol An eol-sequence - * @return A new text edit object. - */ - static setEndOfLine(eol: EndOfLine): TextEdit; - - /** - * The range this edit applies to. - */ - range: Range; - - /** - * The string this edit will insert. - */ - newText: string; - - /** - * The eol-sequence used in the document. - * - * *Note* that the eol-sequence will be applied to the - * whole document. - */ - newEol: EndOfLine; - - /** - * Create a new TextEdit. - * - * @param range A range. - * @param newText A string. - */ - constructor(range: Range, newText: string); - } - - /** - * A workspace edit represents textual changes for many documents. - */ - export class WorkspaceEdit { - - /** - * The number of affected resources. - */ - readonly size: number; - - /** - * Replace the given range with given text for the given resource. - * - * @param uri A resource identifier. - * @param range A range. - * @param newText A string. - */ - replace(uri: Uri, range: Range, newText: string): void; - - /** - * Insert the given text at the given position. - * - * @param uri A resource identifier. - * @param position A position. - * @param newText A string. - */ - insert(uri: Uri, position: Position, newText: string): void; - - /** - * Delete the text at the given range. - * - * @param uri A resource identifier. - * @param range A range. - */ - delete(uri: Uri, range: Range): void; - - /** - * Check if this edit affects the given resource. - * @param uri A resource identifier. - * @return `true` if the given resource will be touched by this edit. - */ - has(uri: Uri): boolean; - - /** - * Set (and replace) text edits for a resource. - * - * @param uri A resource identifier. - * @param edits An array of text edits. - */ - set(uri: Uri, edits: TextEdit[]): void; - - /** - * Get the text edits for a resource. - * - * @param uri A resource identifier. - * @return An array of text edits. - */ - get(uri: Uri): TextEdit[]; - - /** - * Get all text edits grouped by resource. - * - * @return An array of `[Uri, TextEdit[]]`-tuples. - */ - entries(): [Uri, TextEdit[]][]; - } - - /** - * A snippet string is a template which allows to insert text - * and to control the editor cursor when insertion happens. - * - * A snippet can define tab stops and placeholders with `$1`, `$2` - * and `${3:foo}`. `$0` defines the final tab stop, it defaults to - * the end of the snippet. Variables are defined with `$name` and - * `${name:default value}`. The full snippet syntax is documented - * [here](http://code.visualstudio.com/docs/editor/userdefinedsnippets#_creating-your-own-snippets). - */ - export class SnippetString { - - /** - * The snippet string. - */ - value: string; - - constructor(value?: string); - - /** - * Builder-function that appends the given string to - * the [`value`](#SnippetString.value) of this snippet string. - * - * @param string A value to append 'as given'. The string will be escaped. - * @return This snippet string. - */ - appendText(string: string): SnippetString; - - /** - * Builder-function that appends a tabstop (`$1`, `$2` etc) to - * the [`value`](#SnippetString.value) of this snippet string. - * - * @param number The number of this tabstop, defaults to an auto-incremet - * value starting at 1. - * @return This snippet string. - */ - appendTabstop(number?: number): SnippetString; - - /** - * Builder-function that appends a placeholder (`${1:value}`) to - * the [`value`](#SnippetString.value) of this snippet string. - * - * @param value The value of this placeholder - either a string or a function - * with which a nested snippet can be created. - * @param number The number of this tabstop, defaults to an auto-incremet - * value starting at 1. - * @return This snippet string. - */ - appendPlaceholder(value: string | ((snippet: SnippetString) => any), number?: number): SnippetString; - - /** - * Builder-function that appends a variable (`${VAR}`) to - * the [`value`](#SnippetString.value) of this snippet string. - * - * @param name The name of the variable - excluding the `$`. - * @param defaultValue The default value which is used when the variable name cannot - * be resolved - either a string or a function with which a nested snippet can be created. - * @return This snippet string. - */ - appendVariable(name: string, defaultValue: string | ((snippet: SnippetString) => any)): SnippetString; - } - - /** - * The rename provider interface defines the contract between extensions and - * the [rename](https://code.visualstudio.com/docs/editor/editingevolved#_rename-symbol)-feature. - */ - export interface RenameProvider { - - /** - * Provide an edit that describes changes that have to be made to one - * or many resources to rename a symbol to a different name. - * - * @param document The document in which the command was invoked. - * @param position The position at which the command was invoked. - * @param newName The new name of the symbol. If the given name is not valid, the provider must return a rejected promise. - * @param token A cancellation token. - * @return A workspace edit or a thenable that resolves to such. The lack of a result can be - * signaled by returning `undefined` or `null`. - */ - provideRenameEdits(document: TextDocument, position: Position, newName: string, token: CancellationToken): ProviderResult; - } - - /** - * Value-object describing what options formatting should use. - */ - export interface FormattingOptions { - - /** - * Size of a tab in spaces. - */ - tabSize: number; - - /** - * Prefer spaces over tabs. - */ - insertSpaces: boolean; - - /** - * Signature for further properties. - */ - [key: string]: boolean | number | string; - } - - /** - * The document formatting provider interface defines the contract between extensions and - * the formatting-feature. - */ - export interface DocumentFormattingEditProvider { - - /** - * Provide formatting edits for a whole document. - * - * @param document The document in which the command was invoked. - * @param options Options controlling formatting. - * @param token A cancellation token. - * @return A set of text edits or a thenable that resolves to such. The lack of a result can be - * signaled by returning `undefined`, `null`, or an empty array. - */ - provideDocumentFormattingEdits(document: TextDocument, options: FormattingOptions, token: CancellationToken): ProviderResult; - } - - /** - * The document formatting provider interface defines the contract between extensions and - * the formatting-feature. - */ - export interface DocumentRangeFormattingEditProvider { - - /** - * Provide formatting edits for a range in a document. - * - * The given range is a hint and providers can decide to format a smaller - * or larger range. Often this is done by adjusting the start and end - * of the range to full syntax nodes. - * - * @param document The document in which the command was invoked. - * @param range The range which should be formatted. - * @param options Options controlling formatting. - * @param token A cancellation token. - * @return A set of text edits or a thenable that resolves to such. The lack of a result can be - * signaled by returning `undefined`, `null`, or an empty array. - */ - provideDocumentRangeFormattingEdits(document: TextDocument, range: Range, options: FormattingOptions, token: CancellationToken): ProviderResult; - } - - /** - * The document formatting provider interface defines the contract between extensions and - * the formatting-feature. - */ - export interface OnTypeFormattingEditProvider { - - /** - * Provide formatting edits after a character has been typed. - * - * The given position and character should hint to the provider - * what range the position to expand to, like find the matching `{` - * when `}` has been entered. - * - * @param document The document in which the command was invoked. - * @param position The position at which the command was invoked. - * @param ch The character that has been typed. - * @param options Options controlling formatting. - * @param token A cancellation token. - * @return A set of text edits or a thenable that resolves to such. The lack of a result can be - * signaled by returning `undefined`, `null`, or an empty array. - */ - provideOnTypeFormattingEdits(document: TextDocument, position: Position, ch: string, options: FormattingOptions, token: CancellationToken): ProviderResult; - } - - /** - * Represents a parameter of a callable-signature. A parameter can - * have a label and a doc-comment. - */ - export class ParameterInformation { - - /** - * The label of this signature. Will be shown in - * the UI. - */ - label: string; - - /** - * The human-readable doc-comment of this signature. Will be shown - * in the UI but can be omitted. - */ - documentation?: string; - - /** - * Creates a new parameter information object. - * - * @param label A label string. - * @param documentation A doc string. - */ - constructor(label: string, documentation?: string); - } - - /** - * Represents the signature of something callable. A signature - * can have a label, like a function-name, a doc-comment, and - * a set of parameters. - */ - export class SignatureInformation { - - /** - * The label of this signature. Will be shown in - * the UI. - */ - label: string; - - /** - * The human-readable doc-comment of this signature. Will be shown - * in the UI but can be omitted. - */ - documentation?: string; - - /** - * The parameters of this signature. - */ - parameters: ParameterInformation[]; - - /** - * Creates a new signature information object. - * - * @param label A label string. - * @param documentation A doc string. - */ - constructor(label: string, documentation?: string); - } - - /** - * Signature help represents the signature of something - * callable. There can be multiple signatures but only one - * active and only one active parameter. - */ - export class SignatureHelp { - - /** - * One or more signatures. - */ - signatures: SignatureInformation[]; - - /** - * The active signature. - */ - activeSignature: number; - - /** - * The active parameter of the active signature. - */ - activeParameter: number; - } - - /** - * The signature help provider interface defines the contract between extensions and - * the [parameter hints](https://code.visualstudio.com/docs/editor/intellisense)-feature. - */ - export interface SignatureHelpProvider { - - /** - * Provide help for the signature at the given position and document. - * - * @param document The document in which the command was invoked. - * @param position The position at which the command was invoked. - * @param token A cancellation token. - * @return Signature help or a thenable that resolves to such. The lack of a result can be - * signaled by returning `undefined` or `null`. - */ - provideSignatureHelp(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; - } - - /** - * Completion item kinds. - */ - export enum CompletionItemKind { - Text = 0, - Method = 1, - Function = 2, - Constructor = 3, - Field = 4, - Variable = 5, - Class = 6, - Interface = 7, - Module = 8, - Property = 9, - Unit = 10, - Value = 11, - Enum = 12, - Keyword = 13, - Snippet = 14, - Color = 15, - Reference = 17, - File = 16, - Folder = 18, - EnumMember = 19, - Constant = 20, - Struct = 21, - Event = 22, - Operator = 23, - TypeParameter = 24 - } - - /** - * A completion item represents a text snippet that is proposed to complete text that is being typed. - * - * It is suffient to create a completion item from just a [label](#CompletionItem.label). In that - * case the completion item will replace the [word](#TextDocument.getWordRangeAtPosition) - * until the cursor with the given label or [insertText](#CompletionItem.insertText). Otherwise the - * the given [edit](#CompletionItem.textEdit) is used. - * - * When selecting a completion item in the editor its defined or synthesized text edit will be applied - * to *all* cursors/selections whereas [additionalTextEdits](CompletionItem.additionalTextEdits) will be - * applied as provided. - * - * @see [CompletionItemProvider.provideCompletionItems](#CompletionItemProvider.provideCompletionItems) - * @see [CompletionItemProvider.resolveCompletionItem](#CompletionItemProvider.resolveCompletionItem) - */ - export class CompletionItem { - - /** - * The label of this completion item. By default - * this is also the text that is inserted when selecting - * this completion. - */ - label: string; - - /** - * The kind of this completion item. Based on the kind - * an icon is chosen by the editor. - */ - kind?: CompletionItemKind; - - /** - * A human-readable string with additional information - * about this item, like type or symbol information. - */ - detail?: string; - - /** - * A human-readable string that represents a doc-comment. - */ - documentation?: string; - - /** - * A string that should be used when comparing this item - * with other items. When `falsy` the [label](#CompletionItem.label) - * is used. - */ - sortText?: string; - - /** - * A string that should be used when filtering a set of - * completion items. When `falsy` the [label](#CompletionItem.label) - * is used. - */ - filterText?: string; - - /** - * A string or snippet that should be inserted in a document when selecting - * this completion. When `falsy` the [label](#CompletionItem.label) - * is used. - */ - insertText?: string | SnippetString; - - /** - * A range of text that should be replaced by this completion item. - * - * Defaults to a range from the start of the [current word](#TextDocument.getWordRangeAtPosition) to the - * current position. - * - * *Note:* The range must be a [single line](#Range.isSingleLine) and it must - * [contain](#Range.contains) the position at which completion has been [requested](#CompletionItemProvider.provideCompletionItems). - */ - range?: Range; - - /** - * An optional set of characters that when pressed while this completion is active will accept it first and - * then type that character. *Note* that all commit characters should have `length=1` and that superfluous - * characters will be ignored. - */ - commitCharacters?: string[]; - - /** - * @deprecated **Deprecated** in favor of `CompletionItem.insertText` and `CompletionItem.range`. - * - * ~~An [edit](#TextEdit) which is applied to a document when selecting - * this completion. When an edit is provided the value of - * [insertText](#CompletionItem.insertText) is ignored.~~ - * - * ~~The [range](#Range) of the edit must be single-line and on the same - * line completions were [requested](#CompletionItemProvider.provideCompletionItems) at.~~ - */ - textEdit?: TextEdit; - - /** - * An optional array of additional [text edits](#TextEdit) that are applied when - * selecting this completion. Edits must not overlap with the main [edit](#CompletionItem.textEdit) - * nor with themselves. - */ - additionalTextEdits?: TextEdit[]; - - /** - * An optional [command](#Command) that is executed *after* inserting this completion. *Note* that - * additional modifications to the current document should be described with the - * [additionalTextEdits](#CompletionItem.additionalTextEdits)-property. - */ - command?: Command; - - /** - * Creates a new completion item. - * - * Completion items must have at least a [label](#CompletionItem.label) which then - * will be used as insert text as well as for sorting and filtering. - * - * @param label The label of the completion. - * @param kind The [kind](#CompletionItemKind) of the completion. - */ - constructor(label: string, kind?: CompletionItemKind); - } - - /** - * Represents a collection of [completion items](#CompletionItem) to be presented - * in the editor. - */ - export class CompletionList { - - /** - * This list it not complete. Further typing should result in recomputing - * this list. - */ - isIncomplete?: boolean; - - /** - * The completion items. - */ - items: CompletionItem[]; - - /** - * Creates a new completion list. - * - * @param items The completion items. - * @param isIncomplete The list is not complete. - */ - constructor(items?: CompletionItem[], isIncomplete?: boolean); - } - - /** - * The completion item provider interface defines the contract between extensions and - * [IntelliSense](https://code.visualstudio.com/docs/editor/intellisense). - * - * When computing *complete* completion items is expensive, providers can optionally implement - * the `resolveCompletionItem`-function. In that case it is enough to return completion - * items with a [label](#CompletionItem.label) from the - * [provideCompletionItems](#CompletionItemProvider.provideCompletionItems)-function. Subsequently, - * when a completion item is shown in the UI and gains focus this provider is asked to resolve - * the item, like adding [doc-comment](#CompletionItem.documentation) or [details](#CompletionItem.detail). - * - * Providers are asked for completions either explicitly by a user gesture or -depending on the configuration- - * implicitly when typing words or trigger characters. - */ - export interface CompletionItemProvider { - - /** - * Provide completion items for the given position and document. - * - * @param document The document in which the command was invoked. - * @param position The position at which the command was invoked. - * @param token A cancellation token. - * @return An array of completions, a [completion list](#CompletionList), or a thenable that resolves to either. - * The lack of a result can be signaled by returning `undefined`, `null`, or an empty array. - */ - provideCompletionItems(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; - - /** - * Given a completion item fill in more data, like [doc-comment](#CompletionItem.documentation) - * or [details](#CompletionItem.detail). - * - * The editor will only resolve a completion item once. - * - * @param item A completion item currently active in the UI. - * @param token A cancellation token. - * @return The resolved completion item or a thenable that resolves to of such. It is OK to return the given - * `item`. When no result is returned, the given `item` will be used. - */ - resolveCompletionItem?(item: CompletionItem, token: CancellationToken): ProviderResult; - } - - - /** - * A document link is a range in a text document that links to an internal or external resource, like another - * text document or a web site. - */ - export class DocumentLink { - - /** - * The range this link applies to. - */ - range: Range; - - /** - * The uri this link points to. - */ - target?: Uri; - - /** - * Creates a new document link. - * - * @param range The range the document link applies to. Must not be empty. - * @param target The uri the document link points to. - */ - constructor(range: Range, target?: Uri); - } - - /** - * The document link provider defines the contract between extensions and feature of showing - * links in the editor. - */ - export interface DocumentLinkProvider { - - /** - * Provide links for the given document. Note that the editor ships with a default provider that detects - * `http(s)` and `file` links. - * - * @param document The document in which the command was invoked. - * @param token A cancellation token. - * @return An array of [document links](#DocumentLink) or a thenable that resolves to such. The lack of a result - * can be signaled by returning `undefined`, `null`, or an empty array. - */ - provideDocumentLinks(document: TextDocument, token: CancellationToken): ProviderResult; - - /** - * Given a link fill in its [target](#DocumentLink.target). This method is called when an incomplete - * link is selected in the UI. Providers can implement this method and return incomple links - * (without target) from the [`provideDocumentLinks`](#DocumentLinkProvider.provideDocumentLinks) method which - * often helps to improve performance. - * - * @param link The link that is to be resolved. - * @param token A cancellation token. - */ - resolveDocumentLink?(link: DocumentLink, token: CancellationToken): ProviderResult; - } - - /** - * A tuple of two characters, like a pair of - * opening and closing brackets. - */ - export type CharacterPair = [string, string]; - - /** - * Describes how comments for a language work. - */ - export interface CommentRule { - - /** - * The line comment token, like `// this is a comment` - */ - lineComment?: string; - - /** - * The block comment character pair, like `/* block comment */` - */ - blockComment?: CharacterPair; - } - - /** - * Describes indentation rules for a language. - */ - export interface IndentationRule { - /** - * If a line matches this pattern, then all the lines after it should be unindendented once (until another rule matches). - */ - decreaseIndentPattern: RegExp; - /** - * If a line matches this pattern, then all the lines after it should be indented once (until another rule matches). - */ - increaseIndentPattern: RegExp; - /** - * If a line matches this pattern, then **only the next line** after it should be indented once. - */ - indentNextLinePattern?: RegExp; - /** - * If a line matches this pattern, then its indentation should not be changed and it should not be evaluated against the other rules. - */ - unIndentedLinePattern?: RegExp; - } - - /** - * Describes what to do with the indentation when pressing Enter. - */ - export enum IndentAction { - /** - * Insert new line and copy the previous line's indentation. - */ - None = 0, - /** - * Insert new line and indent once (relative to the previous line's indentation). - */ - Indent = 1, - /** - * Insert two new lines: - * - the first one indented which will hold the cursor - * - the second one at the same indentation level - */ - IndentOutdent = 2, - /** - * Insert new line and outdent once (relative to the previous line's indentation). - */ - Outdent = 3 - } - - /** - * Describes what to do when pressing Enter. - */ - export interface EnterAction { - /** - * Describe what to do with the indentation. - */ - indentAction: IndentAction; - /** - * Describes text to be appended after the new line and after the indentation. - */ - appendText?: string; - /** - * Describes the number of characters to remove from the new line's indentation. - */ - removeText?: number; - } - - /** - * Describes a rule to be evaluated when pressing Enter. - */ - export interface OnEnterRule { - /** - * This rule will only execute if the text before the cursor matches this regular expression. - */ - beforeText: RegExp; - /** - * This rule will only execute if the text after the cursor matches this regular expression. - */ - afterText?: RegExp; - /** - * The action to execute. - */ - action: EnterAction; - } - - /** - * The language configuration interfaces defines the contract between extensions - * and various editor features, like automatic bracket insertion, automatic indentation etc. - */ - export interface LanguageConfiguration { - /** - * The language's comment settings. - */ - comments?: CommentRule; - /** - * The language's brackets. - * This configuration implicitly affects pressing Enter around these brackets. - */ - brackets?: CharacterPair[]; - /** - * The language's word definition. - * If the language supports Unicode identifiers (e.g. JavaScript), it is preferable - * to provide a word definition that uses exclusion of known separators. - * e.g.: A regex that matches anything except known separators (and dot is allowed to occur in a floating point number): - * /(-?\d*\.\d\w*)|([^\`\~\!\@\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g - */ - wordPattern?: RegExp; - /** - * The language's indentation settings. - */ - indentationRules?: IndentationRule; - /** - * The language's rules to be evaluated when pressing Enter. - */ - onEnterRules?: OnEnterRule[]; - - /** - * **Deprecated** Do not use. - * - * @deprecated Will be replaced by a better API soon. - */ - __electricCharacterSupport?: { - /** - * This property is deprecated and will be **ignored** from - * the editor. - * @deprecated - */ - brackets?: any; - /** - * This property is deprecated and not fully supported anymore by - * the editor (scope and lineStart are ignored). - * Use the the autoClosingPairs property in the language configuration file instead. - * @deprecated - */ - docComment?: { - scope: string; - open: string; - lineStart: string; - close?: string; - }; - }; - - /** - * **Deprecated** Do not use. - * - * @deprecated * Use the the autoClosingPairs property in the language configuration file instead. - */ - __characterPairSupport?: { - autoClosingPairs: { - open: string; - close: string; - notIn?: string[]; - }[]; - }; - } - - /** - * Represents the workspace configuration. - * - * The workspace configuration is a merged view: Configurations of the current [workspace](#workspace.rootPath) - * (if available), files like `launch.json`, and the installation-wide configuration. Workspace specific values - * shadow installation-wide values. - * - * *Note:* The merged configuration of the current [workspace](#workspace.rootPath) - * also contains settings from files like `launch.json` and `tasks.json`. Their basename will be - * part of the section identifier. The following snippets shows how to retrieve all configurations - * from `launch.json`: - * - * ```ts - * // launch.json configuration - * const config = workspace.getConfiguration('launch'); - * - * // retrieve values - * const values = config.get('configurations'); - * ``` - */ - export interface WorkspaceConfiguration { - - /** - * Return a value from this configuration. - * - * @param section Configuration name, supports _dotted_ names. - * @return The value `section` denotes or `undefined`. - */ - get(section: string): T | undefined; - - /** - * Return a value from this configuration. - * - * @param section Configuration name, supports _dotted_ names. - * @param defaultValue A value should be returned when no value could be found, is `undefined`. - * @return The value `section` denotes or the default. - */ - get(section: string, defaultValue: T): T; - - - /** - * Check if this configuration has a certain value. - * - * @param section Configuration name, supports _dotted_ names. - * @return `true` if the section doesn't resolve to `undefined`. - */ - has(section: string): boolean; - - /** - * Retrieve all information about a configuration setting. A configuration value - * often consists of a *default* value, a global or installation-wide value, and - * a workspace-specific value. The *effective* value (returned by [`get`](#WorkspaceConfiguration.get)) - * is computed like this: `defaultValue` overwritten by `globalValue`, - * `globalValue` overwritten by `workspaceValue`. - * - * *Note:* The configuration name must denote a leaf in the configuration tree - * (`editor.fontSize` vs `editor`) otherwise no result is returned. - * - * @param section Configuration name, supports _dotted_ names. - * @return Information about a configuration setting or `undefined`. - */ - inspect(section: string): { key: string; defaultValue?: T; globalValue?: T; workspaceValue?: T } | undefined; - - /** - * Update a configuration value. A value can be changed for the current - * [workspace](#workspace.rootPath) only, or globally for all instances of the - * editor. The updated configuration values are persisted. - * - * *Note 1:* Setting an installation-wide value (`global: true`) in the presence of - * a more specific workspace value has no observable effect in that workspace, but - * in others. - * - * *Note 2:* To remove a configuration value use `undefined`, like so: `config.update('somekey', undefined)` - * - * @param section Configuration name, supports _dotted_ names. - * @param value The new value. - * @param global When `true` changes the configuration value for all instances of the editor. - */ - update(section: string, value: any, global?: boolean): Thenable; - - /** - * Readable dictionary that backs this configuration. - */ - readonly [key: string]: any; - } - - /** - * Represents a location inside a resource, such as a line - * inside a text file. - */ - export class Location { - - /** - * The resource identifier of this location. - */ - uri: Uri; - - /** - * The document range of this locations. - */ - range: Range; - - /** - * Creates a new location object. - * - * @param uri The resource identifier. - * @param rangeOrPosition The range or position. Positions will be converted to an empty range. - */ - constructor(uri: Uri, rangeOrPosition: Range | Position); - } - - /** - * Represents the severity of diagnostics. - */ - export enum DiagnosticSeverity { - - /** - * Something not allowed by the rules of a language or other means. - */ - Error = 0, - - /** - * Something suspicious but allowed. - */ - Warning = 1, - - /** - * Something to inform about but not a problem. - */ - Information = 2, - - /** - * Something to hint to a better way of doing it, like proposing - * a refactoring. - */ - Hint = 3 - } - - /** - * Represents a diagnostic, such as a compiler error or warning. Diagnostic objects - * are only valid in the scope of a file. - */ - export class Diagnostic { - - /** - * The range to which this diagnostic applies. - */ - range: Range; - - /** - * The human-readable message. - */ - message: string; - - /** - * A human-readable string describing the source of this - * diagnostic, e.g. 'typescript' or 'super lint'. - */ - source: string; - - /** - * The severity, default is [error](#DiagnosticSeverity.Error). - */ - severity: DiagnosticSeverity; - - /** - * A code or identifier for this diagnostics. Will not be surfaced - * to the user, but should be used for later processing, e.g. when - * providing [code actions](#CodeActionContext). - */ - code: string | number; - - /** - * Creates a new diagnostic object. - * - * @param range The range to which this diagnostic applies. - * @param message The human-readable message. - * @param severity The severity, default is [error](#DiagnosticSeverity.Error). - */ - constructor(range: Range, message: string, severity?: DiagnosticSeverity); - } - - /** - * A diagnostics collection is a container that manages a set of - * [diagnostics](#Diagnostic). Diagnostics are always scopes to a - * diagnostics collection and a resource. - * - * To get an instance of a `DiagnosticCollection` use - * [createDiagnosticCollection](#languages.createDiagnosticCollection). - */ - export interface DiagnosticCollection { - - /** - * The name of this diagnostic collection, for instance `typescript`. Every diagnostic - * from this collection will be associated with this name. Also, the task framework uses this - * name when defining [problem matchers](https://code.visualstudio.com/docs/editor/tasks#_defining-a-problem-matcher). - */ - readonly name: string; - - /** - * Assign diagnostics for given resource. Will replace - * existing diagnostics for that resource. - * - * @param uri A resource identifier. - * @param diagnostics Array of diagnostics or `undefined` - */ - set(uri: Uri, diagnostics: Diagnostic[] | undefined): void; - - /** - * Replace all entries in this collection. - * - * Diagnostics of multiple tuples of the same uri will be merged, e.g - * `[[file1, [d1]], [file1, [d2]]]` is equivalent to `[[file1, [d1, d2]]]`. - * If a diagnostics item is `undefined` as in `[file1, undefined]` - * all previous but not subsequent diagnostics are removed. - * - * @param entries An array of tuples, like `[[file1, [d1, d2]], [file2, [d3, d4, d5]]]`, or `undefined`. - */ - set(entries: [Uri, Diagnostic[] | undefined][]): void; - - /** - * Remove all diagnostics from this collection that belong - * to the provided `uri`. The same as `#set(uri, undefined)`. - * - * @param uri A resource identifier. - */ - delete(uri: Uri): void; - - /** - * Remove all diagnostics from this collection. The same - * as calling `#set(undefined)`; - */ - clear(): void; - - /** - * Iterate over each entry in this collection. - * - * @param callback Function to execute for each entry. - * @param thisArg The `this` context used when invoking the handler function. - */ - forEach(callback: (uri: Uri, diagnostics: Diagnostic[], collection: DiagnosticCollection) => any, thisArg?: any): void; - - /** - * Get the diagnostics for a given resource. *Note* that you cannot - * modify the diagnostics-array returned from this call. - * - * @param uri A resource identifier. - * @returns An immutable array of [diagnostics](#Diagnostic) or `undefined`. - */ - get(uri: Uri): Diagnostic[] | undefined; - - /** - * Check if this collection contains diagnostics for a - * given resource. - * - * @param uri A resource identifier. - * @returns `true` if this collection has diagnostic for the given resource. - */ - has(uri: Uri): boolean; - - /** - * Dispose and free associated resources. Calls - * [clear](#DiagnosticCollection.clear). - */ - dispose(): void; - } - - /** - * Denotes a column in the editor window. Columns are - * used to show editors side by side. - */ - export enum ViewColumn { - One = 1, - Two = 2, - Three = 3 - } - - /** - * An output channel is a container for readonly textual information. - * - * To get an instance of an `OutputChannel` use - * [createOutputChannel](#window.createOutputChannel). - */ - export interface OutputChannel { - - /** - * The human-readable name of this output channel. - */ - readonly name: string; - - /** - * Append the given value to the channel. - * - * @param value A string, falsy values will not be printed. - */ - append(value: string): void; - - /** - * Append the given value and a line feed character - * to the channel. - * - * @param value A string, falsy values will be printed. - */ - appendLine(value: string): void; - - /** - * Removes all output from the channel. - */ - clear(): void; - - /** - * Reveal this channel in the UI. - * - * @param preserveFocus When `true` the channel will not take focus. - */ - show(preserveFocus?: boolean): void; - - /** - * Reveal this channel in the UI. - * - * @deprecated This method is **deprecated** and the overload with - * just one parameter should be used (`show(preserveFocus?: boolean): void`). - * - * @param column This argument is **deprecated** and will be ignored. - * @param preserveFocus When `true` the channel will not take focus. - */ - show(column?: ViewColumn, preserveFocus?: boolean): void; - - /** - * Hide this channel from the UI. - */ - hide(): void; - - /** - * Dispose and free associated resources. - */ - dispose(): void; - } - - /** - * Represents the alignment of status bar items. - */ - export enum StatusBarAlignment { - - /** - * Aligned to the left side. - */ - Left = 1, - - /** - * Aligned to the right side. - */ - Right = 2 - } - - /** - * A status bar item is a status bar contribution that can - * show text and icons and run a command on click. - */ - export interface StatusBarItem { - - /** - * The alignment of this item. - */ - readonly alignment: StatusBarAlignment; - - /** - * The priority of this item. Higher value means the item should - * be shown more to the left. - */ - readonly priority: number; - - /** - * The text to show for the entry. You can embed icons in the text by leveraging the syntax: - * - * `My text $(icon-name) contains icons like $(icon'name) this one.` - * - * Where the icon-name is taken from the [octicon](https://octicons.github.com) icon set, e.g. - * `light-bulb`, `thumbsup`, `zap` etc. - */ - text: string; - - /** - * The tooltip text when you hover over this entry. - */ - tooltip: string | undefined; - - /** - * The foreground color for this entry. - */ - color: string | ThemeColor | undefined; - - /** - * The identifier of a command to run on click. The command must be - * [known](#commands.getCommands). - */ - command: string | undefined; - - /** - * Shows the entry in the status bar. - */ - show(): void; - - /** - * Hide the entry in the status bar. - */ - hide(): void; - - /** - * Dispose and free associated resources. Call - * [hide](#StatusBarItem.hide). - */ - dispose(): void; - } - - /** - * Defines a generalized way of reporting progress updates. - */ - export interface Progress { - - /** - * Report a progress update. - * @param value A progress item, like a message or an updated percentage value - */ - report(value: T): void; - } - - /** - * An individual terminal instance within the integrated terminal. - */ - export interface Terminal { - - /** - * The name of the terminal. - */ - readonly name: string; - - /** - * The process ID of the shell process. - */ - readonly processId: Thenable; - - /** - * Send text to the terminal. The text is written to the stdin of the underlying pty process - * (shell) of the terminal. - * - * @param text The text to send. - * @param addNewLine Whether to add a new line to the text being sent, this is normally - * required to run a command in the terminal. The character(s) added are \n or \r\n - * depending on the platform. This defaults to `true`. - */ - sendText(text: string, addNewLine?: boolean): void; - - /** - * Show the terminal panel and reveal this terminal in the UI. - * - * @param preserveFocus When `true` the terminal will not take focus. - */ - show(preserveFocus?: boolean): void; - - /** - * Hide the terminal panel if this terminal is currently showing. - */ - hide(): void; - - /** - * Dispose and free associated resources. - */ - dispose(): void; - } - - /** - * Represents an extension. - * - * To get an instance of an `Extension` use [getExtension](#extensions.getExtension). - */ - export interface Extension { - - /** - * The canonical extension identifier in the form of: `publisher.name`. - */ - readonly id: string; - - /** - * The absolute file path of the directory containing this extension. - */ - readonly extensionPath: string; - - /** - * `true` if the extension has been activated. - */ - readonly isActive: boolean; - - /** - * The parsed contents of the extension's package.json. - */ - readonly packageJSON: any; - - /** - * The public API exported by this extension. It is an invalid action - * to access this field before this extension has been activated. - */ - readonly exports: T; - - /** - * Activates this extension and returns its public API. - * - * @return A promise that will resolve when this extension has been activated. - */ - activate(): Thenable; - } - - /** - * An extension context is a collection of utilities private to an - * extension. - * - * An instance of an `ExtensionContext` is provided as the first - * parameter to the `activate`-call of an extension. - */ - export interface ExtensionContext { - - /** - * An array to which disposables can be added. When this - * extension is deactivated the disposables will be disposed. - */ - subscriptions: { dispose(): any }[]; - - /** - * A memento object that stores state in the context - * of the currently opened [workspace](#workspace.rootPath). - */ - workspaceState: Memento; - - /** - * A memento object that stores state independent - * of the current opened [workspace](#workspace.rootPath). - */ - globalState: Memento; - - /** - * The absolute file path of the directory containing the extension. - */ - extensionPath: string; - - /** - * Get the absolute path of a resource contained in the extension. - * - * @param relativePath A relative path to a resource contained in the extension. - * @return The absolute path of the resource. - */ - asAbsolutePath(relativePath: string): string; - - /** - * An absolute file path of a workspace specific directory in which the extension - * can store private state. The directory might not exist on disk and creation is - * up to the extension. However, the parent directory is guaranteed to be existent. - * - * Use [`workspaceState`](#ExtensionContext.workspaceState) or - * [`globalState`](#ExtensionContext.globalState) to store key value data. - */ - storagePath: string | undefined; - } - - /** - * A memento represents a storage utility. It can store and retrieve - * values. - */ - export interface Memento { - - /** - * Return a value. - * - * @param key A string. - * @return The stored value or `undefined`. - */ - get(key: string): T | undefined; - - /** - * Return a value. - * - * @param key A string. - * @param defaultValue A value that should be returned when there is no - * value (`undefined`) with the given key. - * @return The stored value or the defaultValue. - */ - get(key: string, defaultValue: T): T; - - /** - * Store a value. The value must be JSON-stringifyable. - * - * @param key A string. - * @param value A value. MUST not contain cyclic references. - */ - update(key: string, value: any): Thenable; - } - - /** - * Namespace describing the environment the editor runs in. - */ - export namespace env { - - /** - * The application name of the editor, like 'VS Code'. - * - * @readonly - */ - export let appName: string; - - /** - * Represents the preferred user-language, like `de-CH`, `fr`, or `en-US`. - * - * @readonly - */ - export let language: string; - - /** - * A unique identifier for the computer. - * - * @readonly - */ - export let machineId: string; - - /** - * A unique identifier for the current session. - * Changes each time the editor is started. - * - * @readonly - */ - export let sessionId: string; - } - - /** - * Namespace for dealing with commands. In short, a command is a function with a - * unique identifier. The function is sometimes also called _command handler_. - * - * Commands can be added to the editor using the [registerCommand](#commands.registerCommand) - * and [registerTextEditorCommand](#commands.registerTextEditorCommand) functions. Commands - * can be executed [manually](#commands.executeCommand) or from a UI gesture. Those are: - * - * * palette - Use the `commands`-section in `package.json` to make a command show in - * the [command palette](https://code.visualstudio.com/docs/getstarted/userinterface#_command-palette). - * * keybinding - Use the `keybindings`-section in `package.json` to enable - * [keybindings](https://code.visualstudio.com/docs/getstarted/keybindings#_customizing-shortcuts) - * for your extension. - * - * Commands from other extensions and from the editor itself are accessible to an extension. However, - * when invoking an editor command not all argument types are supported. - * - * This is a sample that registers a command handler and adds an entry for that command to the palette. First - * register a command handler with the identifier `extension.sayHello`. - * ```javascript - * commands.registerCommand('extension.sayHello', () => { - * window.showInformationMessage('Hello World!'); - * }); - * ``` - * Second, bind the command identifier to a title under which it will show in the palette (`package.json`). - * ```json - * { - * "contributes": { - * "commands": [{ - * "command": "extension.sayHello", - * "title": "Hello World" - * }] - * } - * } - * ``` - */ - export namespace commands { - - /** - * Registers a command that can be invoked via a keyboard shortcut, - * a menu item, an action, or directly. - * - * Registering a command with an existing command identifier twice - * will cause an error. - * - * @param command A unique identifier for the command. - * @param callback A command handler function. - * @param thisArg The `this` context used when invoking the handler function. - * @return Disposable which unregisters this command on disposal. - */ - export function registerCommand(command: string, callback: (...args: any[]) => any, thisArg?: any): Disposable; - - /** - * Registers a text editor command that can be invoked via a keyboard shortcut, - * a menu item, an action, or directly. - * - * Text editor commands are different from ordinary [commands](#commands.registerCommand) as - * they only execute when there is an active editor when the command is called. Also, the - * command handler of an editor command has access to the active editor and to an - * [edit](#TextEditorEdit)-builder. - * - * @param command A unique identifier for the command. - * @param callback A command handler function with access to an [editor](#TextEditor) and an [edit](#TextEditorEdit). - * @param thisArg The `this` context used when invoking the handler function. - * @return Disposable which unregisters this command on disposal. - */ - export function registerTextEditorCommand(command: string, callback: (textEditor: TextEditor, edit: TextEditorEdit, ...args: any[]) => void, thisArg?: any): Disposable; - - /** - * Executes the command denoted by the given command identifier. - * - * When executing an editor command not all types are allowed to - * be passed as arguments. Allowed are the primitive types `string`, `boolean`, - * `number`, `undefined`, and `null`, as well as classes defined in this API. - * There are no restrictions when executing commands that have been contributed - * by extensions. - * - * @param command Identifier of the command to execute. - * @param rest Parameters passed to the command function. - * @return A thenable that resolves to the returned value of the given command. `undefined` when - * the command handler function doesn't return anything. - */ - export function executeCommand(command: string, ...rest: any[]): Thenable; - - /** - * Retrieve the list of all available commands. Commands starting an underscore are - * treated as internal commands. - * - * @param filterInternal Set `true` to not see internal commands (starting with an underscore) - * @return Thenable that resolves to a list of command ids. - */ - export function getCommands(filterInternal?: boolean): Thenable; - } - - /** - * Namespace for dealing with the current window of the editor. That is visible - * and active editors, as well as, UI elements to show messages, selections, and - * asking for user input. - */ - export namespace window { - - /** - * The currently active editor or `undefined`. The active editor is the one - * that currently has focus or, when none has focus, the one that has changed - * input most recently. - */ - export let activeTextEditor: TextEditor | undefined; - - /** - * The currently visible editors or an empty array. - */ - export let visibleTextEditors: TextEditor[]; - - /** - * An [event](#Event) which fires when the [active editor](#window.activeTextEditor) - * has changed. *Note* that the event also fires when the active editor changes - * to `undefined`. - */ - export const onDidChangeActiveTextEditor: Event; - - /** - * An [event](#Event) which fires when the array of [visible editors](#window.visibleTextEditors) - * has changed. - */ - export const onDidChangeVisibleTextEditors: Event; - - /** - * An [event](#Event) which fires when the selection in an editor has changed. - */ - export const onDidChangeTextEditorSelection: Event; - - /** - * An [event](#Event) which fires when the options of an editor have changed. - */ - export const onDidChangeTextEditorOptions: Event; - - /** - * An [event](#Event) which fires when the view column of an editor has changed. - */ - export const onDidChangeTextEditorViewColumn: Event; - - /** - * An [event](#Event) which fires when a terminal is disposed. - */ - export const onDidCloseTerminal: Event; - - /** - * Show the given document in a text editor. A [column](#ViewColumn) can be provided - * to control where the editor is being shown. Might change the [active editor](#window.activeTextEditor). - * - * @param document A text document to be shown. - * @param column A view column in which the editor should be shown. The default is the [one](#ViewColumn.One), other values - * are adjusted to be __Min(column, columnCount + 1)__. - * @param preserveFocus When `true` the editor will not take focus. - * @return A promise that resolves to an [editor](#TextEditor). - */ - export function showTextDocument(document: TextDocument, column?: ViewColumn, preserveFocus?: boolean): Thenable; - - /** - * Show the given document in a text editor. A [column](#ViewColumn) can be provided - * to control where the editor is being shown. Might change the [active editor](#window.activeTextEditor). - * - * @param document A text document to be shown. - * @param options [Editor options](#ShowTextDocumentOptions) to configure the behavior of showing the [editor](#TextEditor). - * @return A promise that resolves to an [editor](#TextEditor). - */ - export function showTextDocument(document: TextDocument, options?: TextDocumentShowOptions): Thenable; - - /** - * Create a TextEditorDecorationType that can be used to add decorations to text editors. - * - * @param options Rendering options for the decoration type. - * @return A new decoration type instance. - */ - export function createTextEditorDecorationType(options: DecorationRenderOptions): TextEditorDecorationType; - - /** - * Show an information message to users. Optionally provide an array of items which will be presented as - * clickable buttons. - * - * @param message The message to show. - * @param items A set of items that will be rendered as actions in the message. - * @return A thenable that resolves to the selected item or `undefined` when being dismissed. - */ - export function showInformationMessage(message: string, ...items: string[]): Thenable; - - /** - * Show an information message to users. Optionally provide an array of items which will be presented as - * clickable buttons. - * - * @param message The message to show. - * @param options Configures the behaviour of the message. - * @param items A set of items that will be rendered as actions in the message. - * @return A thenable that resolves to the selected item or `undefined` when being dismissed. - */ - export function showInformationMessage(message: string, options: MessageOptions, ...items: string[]): Thenable; - - /** - * Show an information message. - * - * @see [showInformationMessage](#window.showInformationMessage) - * - * @param message The message to show. - * @param items A set of items that will be rendered as actions in the message. - * @return A thenable that resolves to the selected item or `undefined` when being dismissed. - */ - export function showInformationMessage(message: string, ...items: T[]): Thenable; - - /** - * Show an information message. - * - * @see [showInformationMessage](#window.showInformationMessage) - * - * @param message The message to show. - * @param options Configures the behaviour of the message. - * @param items A set of items that will be rendered as actions in the message. - * @return A thenable that resolves to the selected item or `undefined` when being dismissed. - */ - export function showInformationMessage(message: string, options: MessageOptions, ...items: T[]): Thenable; - - /** - * Show a warning message. - * - * @see [showInformationMessage](#window.showInformationMessage) - * - * @param message The message to show. - * @param items A set of items that will be rendered as actions in the message. - * @return A thenable that resolves to the selected item or `undefined` when being dismissed. - */ - export function showWarningMessage(message: string, ...items: string[]): Thenable; - - /** - * Show a warning message. - * - * @see [showInformationMessage](#window.showInformationMessage) - * - * @param message The message to show. - * @param options Configures the behaviour of the message. - * @param items A set of items that will be rendered as actions in the message. - * @return A thenable that resolves to the selected item or `undefined` when being dismissed. - */ - export function showWarningMessage(message: string, options: MessageOptions, ...items: string[]): Thenable; - - /** - * Show a warning message. - * - * @see [showInformationMessage](#window.showInformationMessage) - * - * @param message The message to show. - * @param items A set of items that will be rendered as actions in the message. - * @return A thenable that resolves to the selected item or `undefined` when being dismissed. - */ - export function showWarningMessage(message: string, ...items: T[]): Thenable; - - /** - * Show a warning message. - * - * @see [showInformationMessage](#window.showInformationMessage) - * - * @param message The message to show. - * @param options Configures the behaviour of the message. - * @param items A set of items that will be rendered as actions in the message. - * @return A thenable that resolves to the selected item or `undefined` when being dismissed. - */ - export function showWarningMessage(message: string, options: MessageOptions, ...items: T[]): Thenable; - - /** - * Show an error message. - * - * @see [showInformationMessage](#window.showInformationMessage) - * - * @param message The message to show. - * @param items A set of items that will be rendered as actions in the message. - * @return A thenable that resolves to the selected item or `undefined` when being dismissed. - */ - export function showErrorMessage(message: string, ...items: string[]): Thenable; - - /** - * Show an error message. - * - * @see [showInformationMessage](#window.showInformationMessage) - * - * @param message The message to show. - * @param options Configures the behaviour of the message. - * @param items A set of items that will be rendered as actions in the message. - * @return A thenable that resolves to the selected item or `undefined` when being dismissed. - */ - export function showErrorMessage(message: string, options: MessageOptions, ...items: string[]): Thenable; - - /** - * Show an error message. - * - * @see [showInformationMessage](#window.showInformationMessage) - * - * @param message The message to show. - * @param items A set of items that will be rendered as actions in the message. - * @return A thenable that resolves to the selected item or `undefined` when being dismissed. - */ - export function showErrorMessage(message: string, ...items: T[]): Thenable; - - /** - * Show an error message. - * - * @see [showInformationMessage](#window.showInformationMessage) - * - * @param message The message to show. - * @param options Configures the behaviour of the message. - * @param items A set of items that will be rendered as actions in the message. - * @return A thenable that resolves to the selected item or `undefined` when being dismissed. - */ - export function showErrorMessage(message: string, options: MessageOptions, ...items: T[]): Thenable; - - /** - * Shows a selection list. - * - * @param items An array of strings, or a promise that resolves to an array of strings. - * @param options Configures the behavior of the selection list. - * @param token A token that can be used to signal cancellation. - * @return A promise that resolves to the selection or `undefined`. - */ - export function showQuickPick(items: string[] | Thenable, options?: QuickPickOptions, token?: CancellationToken): Thenable; - - /** - * Shows a selection list. - * - * @param items An array of items, or a promise that resolves to an array of items. - * @param options Configures the behavior of the selection list. - * @param token A token that can be used to signal cancellation. - * @return A promise that resolves to the selected item or `undefined`. - */ - export function showQuickPick(items: T[] | Thenable, options?: QuickPickOptions, token?: CancellationToken): Thenable; - - /** - * Opens an input box to ask the user for input. - * - * The returned value will be `undefined` if the input box was canceled (e.g. pressing ESC). Otherwise the - * returned value will be the string typed by the user or an empty string if the user did not type - * anything but dismissed the input box with OK. - * - * @param options Configures the behavior of the input box. - * @param token A token that can be used to signal cancellation. - * @return A promise that resolves to a string the user provided or to `undefined` in case of dismissal. - */ - export function showInputBox(options?: InputBoxOptions, token?: CancellationToken): Thenable; - - /** - * Create a new [output channel](#OutputChannel) with the given name. - * - * @param name Human-readable string which will be used to represent the channel in the UI. - */ - export function createOutputChannel(name: string): OutputChannel; - - /** - * Set a message to the status bar. This is a short hand for the more powerful - * status bar [items](#window.createStatusBarItem). - * - * @param text The message to show, supports icon substitution as in status bar [items](#StatusBarItem.text). - * @param hideAfterTimeout Timeout in milliseconds after which the message will be disposed. - * @return A disposable which hides the status bar message. - */ - export function setStatusBarMessage(text: string, hideAfterTimeout: number): Disposable; - - /** - * Set a message to the status bar. This is a short hand for the more powerful - * status bar [items](#window.createStatusBarItem). - * - * @param text The message to show, supports icon substitution as in status bar [items](#StatusBarItem.text). - * @param hideWhenDone Thenable on which completion (resolve or reject) the message will be disposed. - * @return A disposable which hides the status bar message. - */ - export function setStatusBarMessage(text: string, hideWhenDone: Thenable): Disposable; - - /** - * Set a message to the status bar. This is a short hand for the more powerful - * status bar [items](#window.createStatusBarItem). - * - * *Note* that status bar messages stack and that they must be disposed when no - * longer used. - * - * @param text The message to show, supports icon substitution as in status bar [items](#StatusBarItem.text). - * @return A disposable which hides the status bar message. - */ - export function setStatusBarMessage(text: string): Disposable; - - /** - * @deprecated This function **deprecated**. Use `withProgress` instead. - * - * ~~Show progress in the Source Control viewlet while running the given callback and while - * its returned promise isn't resolve or rejected.~~ - * - * @param task A callback returning a promise. Progress increments can be reported with - * the provided [progress](#Progress)-object. - * @return The thenable the task did rseturn. - */ - export function withScmProgress(task: (progress: Progress) => Thenable): Thenable; - - /** - * Show progress in the editor. Progress is shown while running the given callback - * and while the promise it returned isn't resolved nor rejected. The location at which - * progress should show (and other details) is defined via the passed [`ProgressOptions`](#ProgressOptions). - * - * @param task A callback returning a promise. Progress state can be reported with - * the provided [progress](#Progress)-object. - * @return The thenable the task-callback returned. - */ - export function withProgress(options: ProgressOptions, task: (progress: Progress<{ message?: string; }>) => Thenable): Thenable; - - /** - * Creates a status bar [item](#StatusBarItem). - * - * @param alignment The alignment of the item. - * @param priority The priority of the item. Higher values mean the item should be shown more to the left. - * @return A new status bar item. - */ - export function createStatusBarItem(alignment?: StatusBarAlignment, priority?: number): StatusBarItem; - - /** - * Creates a [Terminal](#Terminal). The cwd of the terminal will be the workspace directory - * if it exists, regardless of whether an explicit customStartPath setting exists. - * - * @param name Optional human-readable string which will be used to represent the terminal in the UI. - * @param shellPath Optional path to a custom shell executable to be used in the terminal. - * @param shellArgs Optional args for the custom shell executable, this does not work on Windows (see #8429) - * @return A new Terminal. - */ - export function createTerminal(name?: string, shellPath?: string, shellArgs?: string[]): Terminal; - - /** - * Creates a [Terminal](#Terminal). The cwd of the terminal will be the workspace directory - * if it exists, regardless of whether an explicit customStartPath setting exists. - * - * @param options A TerminalOptions object describing the characteristics of the new terminal. - * @return A new Terminal. - */ - export function createTerminal(options: TerminalOptions): Terminal; - - /** - * Register a [TreeDataProvider](#TreeDataProvider) for the view contributed using the extension point `views`. - * @param viewId Id of the view contributed using the extension point `views`. - * @param treeDataProvider A [TreeDataProvider](#TreeDataProvider) that provides tree data for the view - */ - export function registerTreeDataProvider(viewId: string, treeDataProvider: TreeDataProvider): Disposable; - } - - /** - * A data provider that provides tree data - */ - export interface TreeDataProvider { - /** - * An optional event to signal that an element or root has changed. - * To signal that root has changed, do not pass any argument or pass `undefined` or `null`. - */ - onDidChangeTreeData?: Event; - - /** - * Get [TreeItem](#TreeItem) representation of the `element` - * - * @param element The element for which [TreeItem](#TreeItem) representation is asked for. - * @return [TreeItem](#TreeItem) representation of the element - */ - getTreeItem(element: T): TreeItem | Thenable; - - /** - * Get the children of `element` or root if no element is passed. - * - * @param element The element from which the provider gets children. Can be `undefined`. - * @return Children of `element` or root if no element is passed. - */ - getChildren(element?: T): ProviderResult; - } - - export class TreeItem { - /** - * A human-readable string describing this item - */ - label: string; - - /** - * The icon path for the tree item - */ - iconPath?: string | Uri | { light: string | Uri; dark: string | Uri }; - - /** - * The [command](#Command) which should be run when the tree item is selected. - */ - command?: Command; - - /** - * [TreeItemCollapsibleState](#TreeItemCollapsibleState) of the tree item. - */ - collapsibleState?: TreeItemCollapsibleState; - - /** - * Context value of the tree item. This can be used to contribute item specific actions in the tree. - * For example, a tree item is given a context value as `folder`. When contributing actions to `view/item/context` - * using `menus` extension point, you can specify context value for key `viewItem` in `when` expression like `viewItem == folder`. - * ``` - * "contributes": { - * "menus": { - * "view/item/context": [ - * { - * "command": "extension.deleteFolder", - * "when": "viewItem == folder" - * } - * ] - * } - * } - * ``` - * This will show action `extension.deleteFolder` only for items with `contextValue` is `folder`. - */ - contextValue?: string; - - /** - * @param label A human-readable string describing this item - * @param collapsibleState [TreeItemCollapsibleState](#TreeItemCollapsibleState) of the tree item. Default is [TreeItemCollapsibleState.None](#TreeItemCollapsibleState.None) - */ - constructor(label: string, collapsibleState?: TreeItemCollapsibleState); - } - - /** - * Collapsible state of the tree item - */ - export enum TreeItemCollapsibleState { - /** - * Determines an item can be neither collapsed nor expanded. Implies it has no children. - */ - None = 0, - /** - * Determines an item is collapsed - */ - Collapsed = 1, - /** - * Determines an item is expanded - */ - Expanded = 2 - } - - /** - * Value-object describing what options a terminal should use. - */ - export interface TerminalOptions { - /** - * A human-readable string which will be used to represent the terminal in the UI. - */ - name?: string; - - /** - * A path to a custom shell executable to be used in the terminal. - */ - shellPath?: string; - - /** - * Args for the custom shell executable, this does not work on Windows (see #8429) - */ - shellArgs?: string[]; - } - - /** - * A location in the editor at which progress information can be shown. It depends on the - * location how progress is visually represented. - */ - export enum ProgressLocation { - - /** - * Show progress for the source control viewlet, as overlay for the icon and as progress bar - * inside the viewlet (when visible). - */ - SourceControl = 1, - - /** - * Show progress in the status bar of the editor. - */ - Window = 10 - } - - /** - * Value-object describing where and how progress should show. - */ - export interface ProgressOptions { - - /** - * The location at which progress should show. - */ - location: ProgressLocation; - - /** - * A human-readable string which will be used to describe the - * operation. - */ - title?: string; - } - - /** - * An event describing an individual change in the text of a [document](#TextDocument). - */ - export interface TextDocumentContentChangeEvent { - /** - * The range that got replaced. - */ - range: Range; - /** - * The length of the range that got replaced. - */ - rangeLength: number; - /** - * The new text for the range. - */ - text: string; - } - - /** - * An event describing a transactional [document](#TextDocument) change. - */ - export interface TextDocumentChangeEvent { - - /** - * The affected document. - */ - document: TextDocument; - - /** - * An array of content changes. - */ - contentChanges: TextDocumentContentChangeEvent[]; - } - - /** - * Represents reasons why a text document is saved. - */ - export enum TextDocumentSaveReason { - - /** - * Manually triggered, e.g. by the user pressing save, by starting debugging, - * or by an API call. - */ - Manual = 1, - - /** - * Automatic after a delay. - */ - AfterDelay = 2, - - /** - * When the editor lost focus. - */ - FocusOut = 3 - } - - /** - * An event that is fired when a [document](#TextDocument) will be saved. - * - * To make modifications to the document before it is being saved, call the - * [`waitUntil`](#TextDocumentWillSaveEvent.waitUntil)-function with a thenable - * that resolves to an array of [text edits](#TextEdit). - */ - export interface TextDocumentWillSaveEvent { - - /** - * The document that will be saved. - */ - document: TextDocument; - - /** - * The reason why save was triggered. - */ - reason: TextDocumentSaveReason; - - /** - * Allows to pause the event loop and to apply [pre-save-edits](#TextEdit). - * Edits of subsequent calls to this function will be applied in order. The - * edits will be *ignored* if concurrent modifications of the document happened. - * - * *Note:* This function can only be called during event dispatch and not - * in an asynchronous manner: - * - * ```ts - * workspace.onWillSaveTextDocument(event => { - * // async, will *throw* an error - * setTimeout(() => event.waitUntil(promise)); - * - * // sync, OK - * event.waitUntil(promise); - * }) - * ``` - * - * @param thenable A thenable that resolves to [pre-save-edits](#TextEdit). - */ - waitUntil(thenable: Thenable): void; - - /** - * Allows to pause the event loop until the provided thenable resolved. - * - * *Note:* This function can only be called during event dispatch. - * - * @param thenable A thenable that delays saving. - */ - waitUntil(thenable: Thenable): void; - } - - /** - * Namespace for dealing with the current workspace. A workspace is the representation - * of the folder that has been opened. There is no workspace when just a file but not a - * folder has been opened. - * - * The workspace offers support for [listening](#workspace.createFileSystemWatcher) to fs - * events and for [finding](#workspace.findFiles) files. Both perform well and run _outside_ - * the editor-process so that they should be always used instead of nodejs-equivalents. - */ - export namespace workspace { - - /** - * Creates a file system watcher. - * - * A glob pattern that filters the file events must be provided. Optionally, flags to ignore certain - * kinds of events can be provided. To stop listening to events the watcher must be disposed. - * - * *Note* that only files within the current [workspace](#workspace.rootPath) can be watched. - * - * @param globPattern A glob pattern that is applied to the names of created, changed, and deleted files. - * @param ignoreCreateEvents Ignore when files have been created. - * @param ignoreChangeEvents Ignore when files have been changed. - * @param ignoreDeleteEvents Ignore when files have been deleted. - * @return A new file system watcher instance. - */ - export function createFileSystemWatcher(globPattern: string, ignoreCreateEvents?: boolean, ignoreChangeEvents?: boolean, ignoreDeleteEvents?: boolean): FileSystemWatcher; - - /** - * The folder that is open in the editor. `undefined` when no folder - * has been opened. - * - * @readonly - */ - export let rootPath: string | undefined; - - /** - * Returns a path that is relative to the workspace root. - * - * When there is no [workspace root](#workspace.rootPath) or when the path - * is not a child of that folder, the input is returned. - * - * @param pathOrUri A path or uri. When a uri is given its [fsPath](#Uri.fsPath) is used. - * @return A path relative to the root or the input. - */ - export function asRelativePath(pathOrUri: string | Uri): string; - - /** - * Find files in the workspace. - * - * @sample `findFiles('**∕*.js', '**∕node_modules∕**', 10)` - * @param include A glob pattern that defines the files to search for. - * @param exclude A glob pattern that defines files and folders to exclude. - * @param maxResults An upper-bound for the result. - * @param token A token that can be used to signal cancellation to the underlying search engine. - * @return A thenable that resolves to an array of resource identifiers. - */ - export function findFiles(include: string, exclude?: string, maxResults?: number, token?: CancellationToken): Thenable; - - /** - * Save all dirty files. - * - * @param includeUntitled Also save files that have been created during this session. - * @return A thenable that resolves when the files have been saved. - */ - export function saveAll(includeUntitled?: boolean): Thenable; - - /** - * Make changes to one or many resources as defined by the given - * [workspace edit](#WorkspaceEdit). - * - * When applying a workspace edit, the editor implements an 'all-or-nothing'-strategy, - * that means failure to load one document or make changes to one document will cause - * the edit to be rejected. - * - * @param edit A workspace edit. - * @return A thenable that resolves when the edit could be applied. - */ - export function applyEdit(edit: WorkspaceEdit): Thenable; - - /** - * All text documents currently known to the system. - * - * @readonly - */ - export let textDocuments: TextDocument[]; - - /** - * Opens a document. Will return early if this document is already open. Otherwise - * the document is loaded and the [didOpen](#workspace.onDidOpenTextDocument)-event fires. - * - * The document is denoted by an [uri](#Uri). Depending on the [scheme](#Uri.scheme) the - * following rules apply: - * * `file`-scheme: Open a file on disk, will be rejected if the file does not exist or cannot be loaded. - * * `untitled`-scheme: A new file that should be saved on disk, e.g. `untitled:c:\frodo\new.js`. The language - * will be derived from the file name. - * * For all other schemes the registered text document content [providers](#TextDocumentContentProvider) are consulted. - * - * *Note* that the lifecycle of the returned document is owned by the editor and not by the extension. That means an - * [`onDidClose`](#workspace.onDidCloseTextDocument)-event can occur at any time after opening it. - * - * @param uri Identifies the resource to open. - * @return A promise that resolves to a [document](#TextDocument). - */ - export function openTextDocument(uri: Uri): Thenable; - - /** - * A short-hand for `openTextDocument(Uri.file(fileName))`. - * - * @see [openTextDocument](#openTextDocument) - * @param fileName A name of a file on disk. - * @return A promise that resolves to a [document](#TextDocument). - */ - export function openTextDocument(fileName: string): Thenable; - - /** - * Opens an untitled text document. The editor will prompt the user for a file - * path when the document is to be saved. The `options` parameter allows to - * specify the *language* and/or the *content* of the document. - * - * @param options Options to control how the document will be created. - * @return A promise that resolves to a [document](#TextDocument). - */ - export function openTextDocument(options?: { language?: string; content?: string; }): Thenable; - - /** - * Register a text document content provider. - * - * Only one provider can be registered per scheme. - * - * @param scheme The uri-scheme to register for. - * @param provider A content provider. - * @return A [disposable](#Disposable) that unregisters this provider when being disposed. - */ - export function registerTextDocumentContentProvider(scheme: string, provider: TextDocumentContentProvider): Disposable; - - /** - * An event that is emitted when a [text document](#TextDocument) is opened. - */ - export const onDidOpenTextDocument: Event; - - /** - * An event that is emitted when a [text document](#TextDocument) is disposed. - */ - export const onDidCloseTextDocument: Event; - - /** - * An event that is emitted when a [text document](#TextDocument) is changed. - */ - export const onDidChangeTextDocument: Event; - - /** - * An event that is emitted when a [text document](#TextDocument) will be saved to disk. - * - * *Note 1:* Subscribers can delay saving by registering asynchronous work. For the sake of data integrity the editor - * might save without firing this event. For instance when shutting down with dirty files. - * - * *Note 2:* Subscribers are called sequentially and they can [delay](#TextDocumentWillSaveEvent.waitUntil) saving - * by registering asynchronous work. Protection against misbehaving listeners is implemented as such: - * * there is an overall time budget that all listeners share and if that is exhausted no further listener is called - * * listeners that take a long time or produce errors frequently will not be called anymore - * - * The current thresholds are 1.5 seconds as overall time budget and a listener can misbehave 3 times before being ignored. - */ - export const onWillSaveTextDocument: Event; - - /** - * An event that is emitted when a [text document](#TextDocument) is saved to disk. - */ - export const onDidSaveTextDocument: Event; - - /** - * Get a configuration object. - * - * When a section-identifier is provided only that part of the configuration - * is returned. Dots in the section-identifier are interpreted as child-access, - * like `{ myExt: { setting: { doIt: true }}}` and `getConfiguration('myExt.setting').get('doIt') === true`. - * - * @param section A dot-separated identifier. - * @return The full workspace configuration or a subset. - */ - export function getConfiguration(section?: string): WorkspaceConfiguration; - - /** - * An event that is emitted when the [configuration](#WorkspaceConfiguration) changed. - */ - export const onDidChangeConfiguration: Event; - } - - /** - * Namespace for participating in language-specific editor [features](https://code.visualstudio.com/docs/editor/editingevolved), - * like IntelliSense, code actions, diagnostics etc. - * - * Many programming languages exist and there is huge variety in syntaxes, semantics, and paradigms. Despite that, features - * like automatic word-completion, code navigation, or code checking have become popular across different tools for different - * programming languages. - * - * The editor provides an API that makes it simple to provide such common features by having all UI and actions already in place and - * by allowing you to participate by providing data only. For instance, to contribute a hover all you have to do is provide a function - * that can be called with a [TextDocument](#TextDocument) and a [Position](#Position) returning hover info. The rest, like tracking the - * mouse, positioning the hover, keeping the hover stable etc. is taken care of by the editor. - * - * ```javascript - * languages.registerHoverProvider('javascript', { - * provideHover(document, position, token) { - * return new Hover('I am a hover!'); - * } - * }); - * ``` - * - * Registration is done using a [document selector](#DocumentSelector) which is either a language id, like `javascript` or - * a more complex [filter](#DocumentFilter) like `{ language: 'typescript', scheme: 'file' }`. Matching a document against such - * a selector will result in a [score](#languages.match) that is used to determine if and how a provider shall be used. When - * scores are equal the provider that came last wins. For features that allow full arity, like [hover](#languages.registerHoverProvider), - * the score is only checked to be `>0`, for other features, like [IntelliSense](#languages.registerCompletionItemProvider) the - * score is used for determining the order in which providers are asked to participate. - */ - export namespace languages { - - /** - * Return the identifiers of all known languages. - * @return Promise resolving to an array of identifier strings. - */ - export function getLanguages(): Thenable; - - /** - * Compute the match between a document [selector](#DocumentSelector) and a document. Values - * greater than zero mean the selector matches the document. - * - * A match is computed according to these rules: - * 1. When [`DocumentSelector`](#DocumentSelector) is an array, compute the match for each contained `DocumentFilter` or language identifier and take the maximum value. - * 2. A string will be desugared to become the `language`-part of a [`DocumentFilter`](#DocumentFilter), so `"fooLang"` is like `{ language: "fooLang" }`. - * 3. A [`DocumentFilter`](#DocumentFilter) will be matched against the document by comparing its parts with the document. The following rules apply: - * 1. When the `DocumentFilter` is empty (`{}`) the result is `0` - * 2. When `scheme`, `language`, or `pattern` are defined but one doesn’t match, the result is `0` - * 3. Matching against `*` gives a score of `5`, matching via equality or via a glob-pattern gives a score of `10` - * 4. The result is the maximun value of each match - * - * Samples: - * ```js - * // default document from disk (file-scheme) - * doc.uri; //'file:///my/file.js' - * doc.languageId; // 'javascript' - * match('javascript', doc); // 10; - * match({language: 'javascript'}, doc); // 10; - * match({language: 'javascript', scheme: 'file'}, doc); // 10; - * match('*', doc); // 5 - * match('fooLang', doc); // 0 - * match(['fooLang', '*'], doc); // 5 - * - * // virtual document, e.g. from git-index - * doc.uri; // 'git:/my/file.js' - * doc.languageId; // 'javascript' - * match('javascript', doc); // 10; - * match({language: 'javascript', scheme: 'git'}, doc); // 10; - * match('*', doc); // 5 - * ``` - * - * @param selector A document selector. - * @param document A text document. - * @return A number `>0` when the selector matches and `0` when the selector does not match. - */ - export function match(selector: DocumentSelector, document: TextDocument): number; - - /** - * Create a diagnostics collection. - * - * @param name The [name](#DiagnosticCollection.name) of the collection. - * @return A new diagnostic collection. - */ - export function createDiagnosticCollection(name?: string): DiagnosticCollection; - - /** - * Register a completion provider. - * - * Multiple providers can be registered for a language. In that case providers are sorted - * by their [score](#languages.match) and groups of equal score are sequentially asked for - * completion items. The process stops when one or many providers of a group return a - * result. A failing provider (rejected promise or exception) will not fail the whole - * operation. - * - * @param selector A selector that defines the documents this provider is applicable to. - * @param provider A completion provider. - * @param triggerCharacters Trigger completion when the user types one of the characters, like `.` or `:`. - * @return A [disposable](#Disposable) that unregisters this provider when being disposed. - */ - export function registerCompletionItemProvider(selector: DocumentSelector, provider: CompletionItemProvider, ...triggerCharacters: string[]): Disposable; - - /** - * Register a code action provider. - * - * Multiple providers can be registered for a language. In that case providers are asked in - * parallel and the results are merged. A failing provider (rejected promise or exception) will - * not cause a failure of the whole operation. - * - * @param selector A selector that defines the documents this provider is applicable to. - * @param provider A code action provider. - * @return A [disposable](#Disposable) that unregisters this provider when being disposed. - */ - export function registerCodeActionsProvider(selector: DocumentSelector, provider: CodeActionProvider): Disposable; - - /** - * Register a code lens provider. - * - * Multiple providers can be registered for a language. In that case providers are asked in - * parallel and the results are merged. A failing provider (rejected promise or exception) will - * not cause a failure of the whole operation. - * - * @param selector A selector that defines the documents this provider is applicable to. - * @param provider A code lens provider. - * @return A [disposable](#Disposable) that unregisters this provider when being disposed. - */ - export function registerCodeLensProvider(selector: DocumentSelector, provider: CodeLensProvider): Disposable; - - /** - * Register a definition provider. - * - * Multiple providers can be registered for a language. In that case providers are asked in - * parallel and the results are merged. A failing provider (rejected promise or exception) will - * not cause a failure of the whole operation. - * - * @param selector A selector that defines the documents this provider is applicable to. - * @param provider A definition provider. - * @return A [disposable](#Disposable) that unregisters this provider when being disposed. - */ - export function registerDefinitionProvider(selector: DocumentSelector, provider: DefinitionProvider): Disposable; - - /** - * Register an implementation provider. - * - * Multiple providers can be registered for a language. In that case providers are asked in - * parallel and the results are merged. A failing provider (rejected promise or exception) will - * not cause a failure of the whole operation. - * - * @param selector A selector that defines the documents this provider is applicable to. - * @param provider An implementation provider. - * @return A [disposable](#Disposable) that unregisters this provider when being disposed. - */ - export function registerImplementationProvider(selector: DocumentSelector, provider: ImplementationProvider): Disposable; - - /** - * Register a type definition provider. - * - * Multiple providers can be registered for a language. In that case providers are asked in - * parallel and the results are merged. A failing provider (rejected promise or exception) will - * not cause a failure of the whole operation. - * - * @param selector A selector that defines the documents this provider is applicable to. - * @param provider A type definition provider. - * @return A [disposable](#Disposable) that unregisters this provider when being disposed. - */ - export function registerTypeDefinitionProvider(selector: DocumentSelector, provider: TypeDefinitionProvider): Disposable; - - /** - * Register a hover provider. - * - * Multiple providers can be registered for a language. In that case providers are asked in - * parallel and the results are merged. A failing provider (rejected promise or exception) will - * not cause a failure of the whole operation. - * - * @param selector A selector that defines the documents this provider is applicable to. - * @param provider A hover provider. - * @return A [disposable](#Disposable) that unregisters this provider when being disposed. - */ - export function registerHoverProvider(selector: DocumentSelector, provider: HoverProvider): Disposable; - - /** - * Register a document highlight provider. - * - * Multiple providers can be registered for a language. In that case providers are sorted - * by their [score](#languages.match) and groups sequentially asked for document highlights. - * The process stops when a provider returns a `non-falsy` or `non-failure` result. - * - * @param selector A selector that defines the documents this provider is applicable to. - * @param provider A document highlight provider. - * @return A [disposable](#Disposable) that unregisters this provider when being disposed. - */ - export function registerDocumentHighlightProvider(selector: DocumentSelector, provider: DocumentHighlightProvider): Disposable; - - /** - * Register a document symbol provider. - * - * Multiple providers can be registered for a language. In that case providers are asked in - * parallel and the results are merged. A failing provider (rejected promise or exception) will - * not cause a failure of the whole operation. - * - * @param selector A selector that defines the documents this provider is applicable to. - * @param provider A document symbol provider. - * @return A [disposable](#Disposable) that unregisters this provider when being disposed. - */ - export function registerDocumentSymbolProvider(selector: DocumentSelector, provider: DocumentSymbolProvider): Disposable; - - /** - * Register a workspace symbol provider. - * - * Multiple providers can be registered. In that case providers are asked in parallel and - * the results are merged. A failing provider (rejected promise or exception) will not cause - * a failure of the whole operation. - * - * @param provider A workspace symbol provider. - * @return A [disposable](#Disposable) that unregisters this provider when being disposed. - */ - export function registerWorkspaceSymbolProvider(provider: WorkspaceSymbolProvider): Disposable; - - /** - * Register a reference provider. - * - * Multiple providers can be registered for a language. In that case providers are asked in - * parallel and the results are merged. A failing provider (rejected promise or exception) will - * not cause a failure of the whole operation. - * - * @param selector A selector that defines the documents this provider is applicable to. - * @param provider A reference provider. - * @return A [disposable](#Disposable) that unregisters this provider when being disposed. - */ - export function registerReferenceProvider(selector: DocumentSelector, provider: ReferenceProvider): Disposable; - - /** - * Register a reference provider. - * - * Multiple providers can be registered for a language. In that case providers are sorted - * by their [score](#languages.match) and the best-matching provider is used. Failure - * of the selected provider will cause a failure of the whole operation. - * - * @param selector A selector that defines the documents this provider is applicable to. - * @param provider A rename provider. - * @return A [disposable](#Disposable) that unregisters this provider when being disposed. - */ - export function registerRenameProvider(selector: DocumentSelector, provider: RenameProvider): Disposable; - - /** - * Register a formatting provider for a document. - * - * Multiple providers can be registered for a language. In that case providers are sorted - * by their [score](#languages.match) and the best-matching provider is used. Failure - * of the selected provider will cause a failure of the whole operation. - * - * @param selector A selector that defines the documents this provider is applicable to. - * @param provider A document formatting edit provider. - * @return A [disposable](#Disposable) that unregisters this provider when being disposed. - */ - export function registerDocumentFormattingEditProvider(selector: DocumentSelector, provider: DocumentFormattingEditProvider): Disposable; - - /** - * Register a formatting provider for a document range. - * - * *Note:* A document range provider is also a [document formatter](#DocumentFormattingEditProvider) - * which means there is no need to [register](registerDocumentFormattingEditProvider) a document - * formatter when also registering a range provider. - * - * Multiple providers can be registered for a language. In that case providers are sorted - * by their [score](#languages.match) and the best-matching provider is used. Failure - * of the selected provider will cause a failure of the whole operation. - * - * @param selector A selector that defines the documents this provider is applicable to. - * @param provider A document range formatting edit provider. - * @return A [disposable](#Disposable) that unregisters this provider when being disposed. - */ - export function registerDocumentRangeFormattingEditProvider(selector: DocumentSelector, provider: DocumentRangeFormattingEditProvider): Disposable; - - /** - * Register a formatting provider that works on type. The provider is active when the user enables the setting `editor.formatOnType`. - * - * Multiple providers can be registered for a language. In that case providers are sorted - * by their [score](#languages.match) and the best-matching provider is used. Failure - * of the selected provider will cause a failure of the whole operation. - * - * @param selector A selector that defines the documents this provider is applicable to. - * @param provider An on type formatting edit provider. - * @param firstTriggerCharacter A character on which formatting should be triggered, like `}`. - * @param moreTriggerCharacter More trigger characters. - * @return A [disposable](#Disposable) that unregisters this provider when being disposed. - */ - export function registerOnTypeFormattingEditProvider(selector: DocumentSelector, provider: OnTypeFormattingEditProvider, firstTriggerCharacter: string, ...moreTriggerCharacter: string[]): Disposable; - - /** - * Register a signature help provider. - * - * Multiple providers can be registered for a language. In that case providers are sorted - * by their [score](#languages.match) and called sequentially until a provider returns a - * valid result. - * - * @param selector A selector that defines the documents this provider is applicable to. - * @param provider A signature help provider. - * @param triggerCharacters Trigger signature help when the user types one of the characters, like `,` or `(`. - * @return A [disposable](#Disposable) that unregisters this provider when being disposed. - */ - export function registerSignatureHelpProvider(selector: DocumentSelector, provider: SignatureHelpProvider, ...triggerCharacters: string[]): Disposable; - - /** - * Register a document link provider. - * - * Multiple providers can be registered for a language. In that case providers are asked in - * parallel and the results are merged. A failing provider (rejected promise or exception) will - * not cause a failure of the whole operation. - * - * @param selector A selector that defines the documents this provider is applicable to. - * @param provider A document link provider. - * @return A [disposable](#Disposable) that unregisters this provider when being disposed. - */ - export function registerDocumentLinkProvider(selector: DocumentSelector, provider: DocumentLinkProvider): Disposable; - - /** - * Set a [language configuration](#LanguageConfiguration) for a language. - * - * @param language A language identifier like `typescript`. - * @param configuration Language configuration. - * @return A [disposable](#Disposable) that unsets this configuration. - */ - export function setLanguageConfiguration(language: string, configuration: LanguageConfiguration): Disposable; - } - - /** - * Represents the input box in the Source Control viewlet. - */ - export interface SourceControlInputBox { - - /** - * Setter and getter for the contents of the input box. - */ - value: string; - } - - interface QuickDiffProvider { - - /** - * Provide a [uri](#Uri) to the original resource of any given resource uri. - * - * @param uri The uri of the resource open in a text editor. - * @param token A cancellation token. - * @return A thenable that resolves to uri of the matching original resource. - */ - provideOriginalResource?(uri: Uri, token: CancellationToken): ProviderResult; - } - - /** - * The theme-aware decorations for a - * [source control resource state](#SourceControlResourceState). - */ - export interface SourceControlResourceThemableDecorations { - - /** - * The icon path for a specific - * [source control resource state](#SourceControlResourceState). - */ - readonly iconPath?: string | Uri; - } - - /** - * The decorations for a [source control resource state](#SourceControlResourceState). - * Can be independently specified for light and dark themes. - */ - export interface SourceControlResourceDecorations extends SourceControlResourceThemableDecorations { - - /** - * Whether the [source control resource state](#SourceControlResourceState) should - * be striked-through in the UI. - */ - readonly strikeThrough?: boolean; - - /** - * Whether the [source control resource state](#SourceControlResourceState) should - * be faded in the UI. - */ - readonly faded?: boolean; - - /** - * The light theme decorations. - */ - readonly light?: SourceControlResourceThemableDecorations; - - /** - * The dark theme decorations. - */ - readonly dark?: SourceControlResourceThemableDecorations; - } - - /** - * An source control resource state represents the state of an underlying workspace - * resource within a certain [source control group](#SourceControlResourceGroup). - */ - export interface SourceControlResourceState { - - /** - * The [uri](#Uri) of the underlying resource inside the workspace. - */ - readonly resourceUri: Uri; - - /** - * The [command](#Command) which should be run when the resource - * state is open in the Source Control viewlet. - */ - readonly command?: Command; - - /** - * The [decorations](#SourceControlResourceDecorations) for this source control - * resource state. - */ - readonly decorations?: SourceControlResourceDecorations; - } - - /** - * A source control resource group is a collection of - * [source control resource states](#SourceControlResourceState). - */ - export interface SourceControlResourceGroup { - - /** - * The id of this source control resource group. - */ - readonly id: string; - - /** - * The label of this source control resource group. - */ - label: string; - - /** - * Whether this source control resource group is hidden when it contains - * no [source control resource states](#SourceControlResourceState). - */ - hideWhenEmpty?: boolean; - - /** - * This group's collection of - * [source control resource states](#SourceControlResourceState). - */ - resourceStates: SourceControlResourceState[]; - - /** - * Dispose this source control resource group. - */ - dispose(): void; - } - - /** - * An source control is able to provide [resource states](#SourceControlResourceState) - * to the editor and interact with the editor in several source control related ways. - */ - export interface SourceControl { - - /** - * The id of this source control. - */ - readonly id: string; - - /** - * The human-readable label of this source control. - */ - readonly label: string; - - /** - * The UI-visible count of [resource states](#SourceControlResourceState) of - * this source control. - * - * Equals to the total number of [resource state](#SourceControlResourceState) - * of this source control, if undefined. - */ - count?: number; - - /** - * An optional [quick diff provider](#QuickDiffProvider). - */ - quickDiffProvider?: QuickDiffProvider; - - /** - * Optional commit template string. - * - * The Source Control viewlet will populate the Source Control - * input with this value when appropriate. - */ - commitTemplate?: string; - - /** - * Optional accept input command. - * - * This command will be invoked when the user accepts the value - * in the Source Control input. - */ - acceptInputCommand?: Command; - - /** - * Optional status bar commands. - * - * These commands will be displayed in the editor's status bar. - */ - statusBarCommands?: Command[]; - - /** - * Create a new [resource group](#SourceControlResourceGroup). - */ - createResourceGroup(id: string, label: string): SourceControlResourceGroup; - - /** - * Dispose this source control. - */ - dispose(): void; - } - - export namespace scm { - - /** - * The [input box](#SourceControlInputBox) in the Source Control viewlet. - */ - export const inputBox: SourceControlInputBox; - - /** - * Creates a new [source control](#SourceControl) instance. - * - * @param id A unique `id` for the source control. Something short, eg: `git`. - * @param label A human-readable string for the source control. Eg: `Git`. - * @return An instance of [source control](#SourceControl). - */ - export function createSourceControl(id: string, label: string): SourceControl; - } - - /** - * Namespace for dealing with installed extensions. Extensions are represented - * by an [extension](#Extension)-interface which allows to reflect on them. - * - * Extension writers can provide APIs to other extensions by returning their API public - * surface from the `activate`-call. - * - * ```javascript - * export function activate(context: vscode.ExtensionContext) { - * let api = { - * sum(a, b) { - * return a + b; - * }, - * mul(a, b) { - * return a * b; - * } - * }; - * // 'export' public api-surface - * return api; - * } - * ``` - * When depending on the API of another extension add an `extensionDependency`-entry - * to `package.json`, and use the [getExtension](#extensions.getExtension)-function - * and the [exports](#Extension.exports)-property, like below: - * - * ```javascript - * let mathExt = extensions.getExtension('genius.math'); - * let importedApi = mathExt.exports; - * - * console.log(importedApi.mul(42, 1)); - * ``` - */ - export namespace extensions { - - /** - * Get an extension by its full identifier in the form of: `publisher.name`. - * - * @param extensionId An extension identifier. - * @return An extension or `undefined`. - */ - export function getExtension(extensionId: string): Extension | undefined; - - /** - * Get an extension its full identifier in the form of: `publisher.name`. - * - * @param extensionId An extension identifier. - * @return An extension or `undefined`. - */ - export function getExtension(extensionId: string): Extension | undefined; - - /** - * All extensions currently known to the system. - */ - export let all: Extension[]; - } -} - -/** - * Thenable is a common denominator between ES6 promises, Q, jquery.Deferred, WinJS.Promise, - * and others. This API makes no assumption about what promise libary is being used which - * enables reusing existing code without migrating to a specific promise implementation. Still, - * we recommend the use of native promises which are available in this editor. - */ -interface Thenable { - /** - * Attaches callbacks for the resolution and/or rejection of the Promise. - * @param onfulfilled The callback to execute when the Promise is resolved. - * @param onrejected The callback to execute when the Promise is rejected. - * @returns A Promise for the completion of which ever callback is executed. - */ - then(onfulfilled?: (value: T) => TResult | Thenable, onrejected?: (reason: any) => TResult | Thenable): Thenable; - then(onfulfilled?: (value: T) => TResult | Thenable, onrejected?: (reason: any) => void): Thenable; -} From 3196f6a8691020f5119e49f229e3f103494b8bea Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Mon, 17 Jul 2017 22:59:19 -0700 Subject: [PATCH 10/31] Reduce noise in html emmet completions --- src/emmetHelper.ts | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/src/emmetHelper.ts b/src/emmetHelper.ts index 94350dd..9f3c07e 100644 --- a/src/emmetHelper.ts +++ b/src/emmetHelper.ts @@ -11,6 +11,7 @@ import * as path from 'path'; import * as fs from 'fs'; const snippetKeyCache = new Map(); +let htmlSnippetKeys: string[]; const htmlAbbreviationStartRegex = /^[a-z,A-Z,!,(,[,#,\.]/; const htmlAbbreviationEndRegex = /[a-z,A-Z,!,),\],#,\.,},\d,*,$]$/; const cssAbbreviationRegex = /^[a-z,A-Z,!,@,#]/; @@ -30,16 +31,27 @@ export function doComplete(document: TextDocument, position: Position, syntax: s return; } + if (!snippetKeyCache.has('html')) { + let registry = createSnippetsRegistry('html'); + htmlSnippetKeys = registry.all({ type: 'string' }).map(snippet => { + return snippet.key; + }); + snippetKeyCache.set('html', htmlSnippetKeys); + } + let expandedAbbr: CompletionItem; let [abbreviationRange, abbreviation] = extractAbbreviation(document, position); let expandOptions = getExpandOptions(emmetConfig.syntaxProfiles, emmetConfig.variables, syntax); if (isAbbreviationValid(syntax, abbreviation)) { let expandedText; - try { - expandedText = expand(abbreviation, expandOptions); - } catch (e) { + // Skip cases where abc -> ${1} as this is noise + if (isStyleSheet(syntax) || !/^[a-z,A-Z]*$/.test(abbreviation) || htmlSnippetKeys.indexOf(abbreviation) > -1) { + try { + expandedText = expand(abbreviation, expandOptions); + } catch (e) { + } } if (expandedText) { @@ -119,9 +131,11 @@ function getAbbreviationSuggestions(syntax: string, prefix: string, abbreviation function getCurrentWord(document: TextDocument, position: Position): string { let currentLine = getCurrentLine(document, position); - let matches = currentLine.match(/[\w,:]*$/); - if (matches) { - return matches[0]; + if (currentLine) { + let matches = currentLine.match(/[\w,:]*$/); + if (matches) { + return matches[0]; + } } } @@ -136,6 +150,9 @@ function getCurrentLine(document: TextDocument, position: Position): string { if (text[i] === '\n') { return text.substring(i + 1, offset); } + if (i === 0) { + return text.substring(0, offset); + } } } From 4c2e543d9822427b52d3a09da02dbabac70fddb7 Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Tue, 18 Jul 2017 10:17:33 -0700 Subject: [PATCH 11/31] Update version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index feb1842..1fd03f7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vscode-emmet-helper", - "version": "0.0.19", + "version": "0.0.20", "description": "Helper to use emmet modules in Visual Studio Code", "main": "./out/emmetHelper.js", "author": "Microsoft Corporation", From 671ce76e4f77cc6818eac8f47f10bdbada9c645d Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Tue, 18 Jul 2017 12:17:43 -0700 Subject: [PATCH 12/31] Reduce noise in css emmet completions --- package.json | 2 +- src/emmetHelper.ts | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 1fd03f7..64834bb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vscode-emmet-helper", - "version": "0.0.20", + "version": "0.0.21", "description": "Helper to use emmet modules in Visual Studio Code", "main": "./out/emmetHelper.js", "author": "Microsoft Corporation", diff --git a/src/emmetHelper.ts b/src/emmetHelper.ts index 9f3c07e..777cf07 100644 --- a/src/emmetHelper.ts +++ b/src/emmetHelper.ts @@ -49,6 +49,10 @@ export function doComplete(document: TextDocument, position: Position, syntax: s if (isStyleSheet(syntax) || !/^[a-z,A-Z]*$/.test(abbreviation) || htmlSnippetKeys.indexOf(abbreviation) > -1) { try { expandedText = expand(abbreviation, expandOptions); + // Skip cases when abc -> abc: ; as this is noise + if (isStyleSheet(syntax) && expandedText === `${abbreviation}: \${1};`) { + expandedText = ''; + } } catch (e) { } From bd63b6aea1761c1b2bd0db2dc834a9260b3cb5dd Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Tue, 18 Jul 2017 14:37:46 -0700 Subject: [PATCH 13/31] Filter out overeager css matches Microsoft/vscode#29473 --- package.json | 2 +- src/emmetHelper.ts | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 64834bb..a76f32a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vscode-emmet-helper", - "version": "0.0.21", + "version": "0.0.22", "description": "Helper to use emmet modules in Visual Studio Code", "main": "./out/emmetHelper.js", "author": "Microsoft Corporation", diff --git a/src/emmetHelper.ts b/src/emmetHelper.ts index 777cf07..f97c567 100644 --- a/src/emmetHelper.ts +++ b/src/emmetHelper.ts @@ -65,8 +65,12 @@ export function doComplete(document: TextDocument, position: Position, syntax: s expandedAbbr.insertTextFormat = InsertTextFormat.Snippet; expandedAbbr.detail = 'Emmet Abbreviation'; if (isStyleSheet(syntax)) { - // Temporary fix for https://github.com/Microsoft/vscode/issues/28933 - expandedAbbr.filterText = abbreviation; + // See https://github.com/Microsoft/vscode/issues/28933#issuecomment-309236902 + // Due to this we set filterText, sortText and label to expanded abbreviation + // - Label makes it clear to the user what their choice is + // - FilterText fixes the issue when user types in propertyname and emmet uses it to match with abbreviations + // - SortText will sort the choice in a way that is intutive to the user + expandedAbbr.filterText = expandedAbbr.documentation; expandedAbbr.sortText = expandedAbbr.documentation; expandedAbbr.label = expandedAbbr.documentation; return CompletionList.create([expandedAbbr], true); From 689431e47844b93b950f761733a94b8cad942cc2 Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Tue, 18 Jul 2017 16:57:01 -0700 Subject: [PATCH 14/31] Support custom snippets --- package.json | 2 +- src/emmetHelper.ts | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index a76f32a..1e09c20 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vscode-emmet-helper", - "version": "0.0.22", + "version": "0.0.23", "description": "Helper to use emmet modules in Visual Studio Code", "main": "./out/emmetHelper.js", "author": "Microsoft Corporation", diff --git a/src/emmetHelper.ts b/src/emmetHelper.ts index f97c567..b4dd89e 100644 --- a/src/emmetHelper.ts +++ b/src/emmetHelper.ts @@ -32,7 +32,7 @@ export function doComplete(document: TextDocument, position: Position, syntax: s } if (!snippetKeyCache.has('html')) { - let registry = createSnippetsRegistry('html'); + let registry = customSnippetRegistry[syntax] ? customSnippetRegistry[syntax] : createSnippetsRegistry('html'); htmlSnippetKeys = registry.all({ type: 'string' }).map(snippet => { return snippet.key; }); @@ -97,7 +97,7 @@ export function doComplete(document: TextDocument, position: Position, syntax: s function getAbbreviationSuggestions(syntax: string, prefix: string, abbreviation: string, abbreviationRange: Range, expandOptions: object): CompletionItem[] { if (!snippetKeyCache.has(syntax)) { - let registry = createSnippetsRegistry(syntax); + let registry = customSnippetRegistry[syntax] ? customSnippetRegistry[syntax] : createSnippetsRegistry(syntax); let snippetKeys: string[] = registry.all({ type: 'string' }).map(snippet => { return snippet.key; }); @@ -164,7 +164,7 @@ function getCurrentLine(document: TextDocument, position: Position): string { } } - +let customSnippetRegistry = {}; let variablesFromFile = {}; let profilesFromFile = {}; let emmetExtensionsPath = ''; @@ -217,7 +217,8 @@ export function getExpandOptions(syntaxProfiles: object, variables: object, synt profile: getProfile(syntax, syntaxProfiles), addons: syntax === 'jsx' ? { 'jsx': true } : null, variables: getVariables(variables), - text: textToReplace ? textToReplace : null + text: textToReplace ? textToReplace : null, + snippets: customSnippetRegistry[syntax] }; } @@ -225,7 +226,7 @@ export function getExpandOptions(syntaxProfiles: object, variables: object, synt * Maps and returns syntaxProfiles of previous format to ones compatible with new emmet modules * @param syntax */ -export function getProfile(syntax: string, profilesFromSettings: object): any { +function getProfile(syntax: string, profilesFromSettings: object): any { let profilesConfig = Object.assign({}, profilesFromFile, profilesFromSettings); let options = profilesConfig[syntax]; @@ -278,7 +279,7 @@ export function getProfile(syntax: string, profilesFromSettings: object): any { /** * Returns variables to be used while expanding snippets */ -export function getVariables(variablesFromSettings: object): any { +function getVariables(variablesFromSettings: object): any { return Object.assign({}, variablesFromFile, variablesFromSettings); } @@ -301,6 +302,11 @@ export function updateExtensionsPath(currentEmmetExtensionsPath: string) { try { let snippetsJson = JSON.parse(snippetsData.toString()); variablesFromFile = snippetsJson['variables']; + Object.keys(snippetsJson).forEach(syntax => { + if (snippetsJson[syntax]['snippets']) { + customSnippetRegistry[syntax] = createSnippetsRegistry(syntax, snippetsJson[syntax]['snippets']); + } + }); } catch (e) { } From 5352e186023998f1cd68661bda671cfb1609d8b1 Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Tue, 18 Jul 2017 21:36:15 -0700 Subject: [PATCH 15/31] Fix getCurrentLine to fetch complete line --- package.json | 2 +- src/emmetHelper.ts | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 1e09c20..37bacf5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vscode-emmet-helper", - "version": "0.0.23", + "version": "0.0.24", "description": "Helper to use emmet modules in Visual Studio Code", "main": "./out/emmetHelper.js", "author": "Microsoft Corporation", diff --git a/src/emmetHelper.ts b/src/emmetHelper.ts index b4dd89e..17cc4e1 100644 --- a/src/emmetHelper.ts +++ b/src/emmetHelper.ts @@ -96,6 +96,9 @@ export function doComplete(document: TextDocument, position: Position, syntax: s } function getAbbreviationSuggestions(syntax: string, prefix: string, abbreviation: string, abbreviationRange: Range, expandOptions: object): CompletionItem[] { + if (!prefix) { + return []; + } if (!snippetKeyCache.has(syntax)) { let registry = customSnippetRegistry[syntax] ? customSnippetRegistry[syntax] : createSnippetsRegistry(syntax); let snippetKeys: string[] = registry.all({ type: 'string' }).map(snippet => { @@ -154,14 +157,21 @@ function removeTabStops(expandedWord: string): string { function getCurrentLine(document: TextDocument, position: Position): string { let offset = document.offsetAt(position); let text = document.getText(); + let start = 0; + let end = text.length - 1; for (let i = offset - 1; i >= 0; i--) { if (text[i] === '\n') { - return text.substring(i + 1, offset); - } - if (i === 0) { - return text.substring(0, offset); + start = i + 1; + break; } } + for (let i = offset; i < text.length; i++) { + if (text[i] === '\n') { + end = i; + break; + } + } + return text.substring(start, end); } let customSnippetRegistry = {}; From d9514ac1b94b14c368067109164794ad5371f26e Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Wed, 19 Jul 2017 12:53:52 -0700 Subject: [PATCH 16/31] Combine custom snippets from basesyntax --- src/emmetHelper.ts | 112 ++++++++++++++++++++++++++------------------- 1 file changed, 64 insertions(+), 48 deletions(-) diff --git a/src/emmetHelper.ts b/src/emmetHelper.ts index 17cc4e1..876c2eb 100644 --- a/src/emmetHelper.ts +++ b/src/emmetHelper.ts @@ -32,7 +32,7 @@ export function doComplete(document: TextDocument, position: Position, syntax: s } if (!snippetKeyCache.has('html')) { - let registry = customSnippetRegistry[syntax] ? customSnippetRegistry[syntax] : createSnippetsRegistry('html'); + let registry = customSnippetRegistry['html'] ? customSnippetRegistry['html'] : createSnippetsRegistry('html'); htmlSnippetKeys = registry.all({ type: 'string' }).map(snippet => { return snippet.key; }); @@ -96,18 +96,11 @@ export function doComplete(document: TextDocument, position: Position, syntax: s } function getAbbreviationSuggestions(syntax: string, prefix: string, abbreviation: string, abbreviationRange: Range, expandOptions: object): CompletionItem[] { - if (!prefix) { + if (!prefix || isStyleSheet(syntax)) { return []; } - if (!snippetKeyCache.has(syntax)) { - let registry = customSnippetRegistry[syntax] ? customSnippetRegistry[syntax] : createSnippetsRegistry(syntax); - let snippetKeys: string[] = registry.all({ type: 'string' }).map(snippet => { - return snippet.key; - }); - snippetKeyCache.set(syntax, snippetKeys); - } - let snippetKeys = snippetKeyCache.get(syntax); + let snippetKeys = snippetKeyCache.has(syntax) ? snippetKeyCache.get(syntax) : snippetKeyCache.get('html'); let snippetCompletions = []; snippetKeys.forEach(snippetKey => { if (!snippetKey.startsWith(prefix) || snippetKey === prefix) { @@ -177,7 +170,6 @@ function getCurrentLine(document: TextDocument, position: Position): string { let customSnippetRegistry = {}; let variablesFromFile = {}; let profilesFromFile = {}; -let emmetExtensionsPath = ''; const field = (index, placeholder) => `\${${index}${placeholder ? ':' + placeholder : ''}}`; @@ -221,6 +213,11 @@ export function isAbbreviationValid(syntax: string, abbreviation: string): boole * @param textToReplace */ export function getExpandOptions(syntaxProfiles: object, variables: object, syntax: string, textToReplace?: string) { + let baseSyntax = isStyleSheet(syntax) ? 'css' : 'html'; + if (!customSnippetRegistry[syntax] && customSnippetRegistry[baseSyntax]) { + customSnippetRegistry[syntax] = customSnippetRegistry[baseSyntax]; + } + return { field: field, syntax: syntax, @@ -290,50 +287,69 @@ function getProfile(syntax: string, profilesFromSettings: object): any { * Returns variables to be used while expanding snippets */ function getVariables(variablesFromSettings: object): any { + if (!variablesFromSettings) { + return variablesFromFile; + } return Object.assign({}, variablesFromFile, variablesFromSettings); } /** * Updates customizations from snippets.json and syntaxProfiles.json files in the directory configured in emmet.extensionsPath setting */ -export function updateExtensionsPath(currentEmmetExtensionsPath: string) { - if (emmetExtensionsPath !== currentEmmetExtensionsPath) { - emmetExtensionsPath = currentEmmetExtensionsPath; - - if (emmetExtensionsPath && emmetExtensionsPath.trim() && path.isAbsolute(emmetExtensionsPath.trim())) { - let dirPath = emmetExtensionsPath.trim(); - let snippetsPath = path.join(dirPath, 'snippets.json'); - let profilesPath = path.join(dirPath, 'syntaxProfiles.json'); - if (dirExists(dirPath)) { - fs.readFile(snippetsPath, (err, snippetsData) => { - if (err) { - return; - } - try { - let snippetsJson = JSON.parse(snippetsData.toString()); - variablesFromFile = snippetsJson['variables']; - Object.keys(snippetsJson).forEach(syntax => { - if (snippetsJson[syntax]['snippets']) { - customSnippetRegistry[syntax] = createSnippetsRegistry(syntax, snippetsJson[syntax]['snippets']); - } - }); - } catch (e) { - - } - }); - fs.readFile(profilesPath, (err, profilesData) => { - if (err) { - return; - } - try { - profilesFromFile = JSON.parse(profilesData.toString()); - } catch (e) { - - } - }); - } - } +export function updateExtensionsPath(emmetExtensionsPath: string) { + if (!emmetExtensionsPath || !emmetExtensionsPath.trim() || !path.isAbsolute(emmetExtensionsPath.trim()) || !dirExists(emmetExtensionsPath.trim())) { + customSnippetRegistry = {}; + snippetKeyCache.clear(); + return; } + + let dirPath = emmetExtensionsPath.trim(); + let snippetsPath = path.join(dirPath, 'snippets.json'); + let profilesPath = path.join(dirPath, 'syntaxProfiles.json'); + + fs.readFile(snippetsPath, (err, snippetsData) => { + if (err) { + return; + } + try { + let snippetsJson = JSON.parse(snippetsData.toString()); + variablesFromFile = snippetsJson['variables']; + customSnippetRegistry = {}; + snippetKeyCache.clear(); + Object.keys(snippetsJson).forEach(syntax => { + if (!snippetsJson[syntax]['snippets']) { + return; + } + let baseSyntax = isStyleSheet(syntax) ? 'css' : 'html'; + let customSnippets = snippetsJson[syntax]['snippets']; + if (snippetsJson[baseSyntax]['snippets'] && baseSyntax !== syntax) { + customSnippets = Object.assign({}, snippetsJson[baseSyntax]['snippets'], snippetsJson[syntax]['snippets']) + } + + customSnippetRegistry[syntax] = createSnippetsRegistry(syntax, customSnippets); + + let snippetKeys: string[] = customSnippetRegistry[syntax].all({ type: 'string' }).map(snippet => { + return snippet.key; + }); + snippetKeyCache.set(syntax, snippetKeys); + }); + } catch (e) { + + } + }); + fs.readFile(profilesPath, (err, profilesData) => { + if (err) { + return; + } + try { + profilesFromFile = JSON.parse(profilesData.toString()); + } catch (e) { + + } + }); + + + } function dirExists(dirPath: string): boolean { From 3cda0fe00825d67742337a1cc9d6e3f8412d18cf Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Wed, 19 Jul 2017 12:55:30 -0700 Subject: [PATCH 17/31] Combine custom snippets from basesyntax --- package.json | 2 +- src/emmetHelper.ts | 112 ++++++++++++++++++++++++++------------------- 2 files changed, 65 insertions(+), 49 deletions(-) diff --git a/package.json b/package.json index 37bacf5..e7ee1ec 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vscode-emmet-helper", - "version": "0.0.24", + "version": "0.0.25", "description": "Helper to use emmet modules in Visual Studio Code", "main": "./out/emmetHelper.js", "author": "Microsoft Corporation", diff --git a/src/emmetHelper.ts b/src/emmetHelper.ts index 17cc4e1..876c2eb 100644 --- a/src/emmetHelper.ts +++ b/src/emmetHelper.ts @@ -32,7 +32,7 @@ export function doComplete(document: TextDocument, position: Position, syntax: s } if (!snippetKeyCache.has('html')) { - let registry = customSnippetRegistry[syntax] ? customSnippetRegistry[syntax] : createSnippetsRegistry('html'); + let registry = customSnippetRegistry['html'] ? customSnippetRegistry['html'] : createSnippetsRegistry('html'); htmlSnippetKeys = registry.all({ type: 'string' }).map(snippet => { return snippet.key; }); @@ -96,18 +96,11 @@ export function doComplete(document: TextDocument, position: Position, syntax: s } function getAbbreviationSuggestions(syntax: string, prefix: string, abbreviation: string, abbreviationRange: Range, expandOptions: object): CompletionItem[] { - if (!prefix) { + if (!prefix || isStyleSheet(syntax)) { return []; } - if (!snippetKeyCache.has(syntax)) { - let registry = customSnippetRegistry[syntax] ? customSnippetRegistry[syntax] : createSnippetsRegistry(syntax); - let snippetKeys: string[] = registry.all({ type: 'string' }).map(snippet => { - return snippet.key; - }); - snippetKeyCache.set(syntax, snippetKeys); - } - let snippetKeys = snippetKeyCache.get(syntax); + let snippetKeys = snippetKeyCache.has(syntax) ? snippetKeyCache.get(syntax) : snippetKeyCache.get('html'); let snippetCompletions = []; snippetKeys.forEach(snippetKey => { if (!snippetKey.startsWith(prefix) || snippetKey === prefix) { @@ -177,7 +170,6 @@ function getCurrentLine(document: TextDocument, position: Position): string { let customSnippetRegistry = {}; let variablesFromFile = {}; let profilesFromFile = {}; -let emmetExtensionsPath = ''; const field = (index, placeholder) => `\${${index}${placeholder ? ':' + placeholder : ''}}`; @@ -221,6 +213,11 @@ export function isAbbreviationValid(syntax: string, abbreviation: string): boole * @param textToReplace */ export function getExpandOptions(syntaxProfiles: object, variables: object, syntax: string, textToReplace?: string) { + let baseSyntax = isStyleSheet(syntax) ? 'css' : 'html'; + if (!customSnippetRegistry[syntax] && customSnippetRegistry[baseSyntax]) { + customSnippetRegistry[syntax] = customSnippetRegistry[baseSyntax]; + } + return { field: field, syntax: syntax, @@ -290,50 +287,69 @@ function getProfile(syntax: string, profilesFromSettings: object): any { * Returns variables to be used while expanding snippets */ function getVariables(variablesFromSettings: object): any { + if (!variablesFromSettings) { + return variablesFromFile; + } return Object.assign({}, variablesFromFile, variablesFromSettings); } /** * Updates customizations from snippets.json and syntaxProfiles.json files in the directory configured in emmet.extensionsPath setting */ -export function updateExtensionsPath(currentEmmetExtensionsPath: string) { - if (emmetExtensionsPath !== currentEmmetExtensionsPath) { - emmetExtensionsPath = currentEmmetExtensionsPath; - - if (emmetExtensionsPath && emmetExtensionsPath.trim() && path.isAbsolute(emmetExtensionsPath.trim())) { - let dirPath = emmetExtensionsPath.trim(); - let snippetsPath = path.join(dirPath, 'snippets.json'); - let profilesPath = path.join(dirPath, 'syntaxProfiles.json'); - if (dirExists(dirPath)) { - fs.readFile(snippetsPath, (err, snippetsData) => { - if (err) { - return; - } - try { - let snippetsJson = JSON.parse(snippetsData.toString()); - variablesFromFile = snippetsJson['variables']; - Object.keys(snippetsJson).forEach(syntax => { - if (snippetsJson[syntax]['snippets']) { - customSnippetRegistry[syntax] = createSnippetsRegistry(syntax, snippetsJson[syntax]['snippets']); - } - }); - } catch (e) { - - } - }); - fs.readFile(profilesPath, (err, profilesData) => { - if (err) { - return; - } - try { - profilesFromFile = JSON.parse(profilesData.toString()); - } catch (e) { - - } - }); - } - } +export function updateExtensionsPath(emmetExtensionsPath: string) { + if (!emmetExtensionsPath || !emmetExtensionsPath.trim() || !path.isAbsolute(emmetExtensionsPath.trim()) || !dirExists(emmetExtensionsPath.trim())) { + customSnippetRegistry = {}; + snippetKeyCache.clear(); + return; } + + let dirPath = emmetExtensionsPath.trim(); + let snippetsPath = path.join(dirPath, 'snippets.json'); + let profilesPath = path.join(dirPath, 'syntaxProfiles.json'); + + fs.readFile(snippetsPath, (err, snippetsData) => { + if (err) { + return; + } + try { + let snippetsJson = JSON.parse(snippetsData.toString()); + variablesFromFile = snippetsJson['variables']; + customSnippetRegistry = {}; + snippetKeyCache.clear(); + Object.keys(snippetsJson).forEach(syntax => { + if (!snippetsJson[syntax]['snippets']) { + return; + } + let baseSyntax = isStyleSheet(syntax) ? 'css' : 'html'; + let customSnippets = snippetsJson[syntax]['snippets']; + if (snippetsJson[baseSyntax]['snippets'] && baseSyntax !== syntax) { + customSnippets = Object.assign({}, snippetsJson[baseSyntax]['snippets'], snippetsJson[syntax]['snippets']) + } + + customSnippetRegistry[syntax] = createSnippetsRegistry(syntax, customSnippets); + + let snippetKeys: string[] = customSnippetRegistry[syntax].all({ type: 'string' }).map(snippet => { + return snippet.key; + }); + snippetKeyCache.set(syntax, snippetKeys); + }); + } catch (e) { + + } + }); + fs.readFile(profilesPath, (err, profilesData) => { + if (err) { + return; + } + try { + profilesFromFile = JSON.parse(profilesData.toString()); + } catch (e) { + + } + }); + + + } function dirExists(dirPath: string): boolean { From 9b1ebee1767e2b36b73da97942d7adbfdd711b72 Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Wed, 19 Jul 2017 17:55:07 -0700 Subject: [PATCH 18/31] end in substring is not inclusive --- package.json | 2 +- src/emmetHelper.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index e7ee1ec..1a1c15f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vscode-emmet-helper", - "version": "0.0.25", + "version": "0.0.26", "description": "Helper to use emmet modules in Visual Studio Code", "main": "./out/emmetHelper.js", "author": "Microsoft Corporation", diff --git a/src/emmetHelper.ts b/src/emmetHelper.ts index 876c2eb..d3cb6b4 100644 --- a/src/emmetHelper.ts +++ b/src/emmetHelper.ts @@ -164,7 +164,7 @@ function getCurrentLine(document: TextDocument, position: Position): string { break; } } - return text.substring(start, end); + return text.substring(start, end + 1); } let customSnippetRegistry = {}; From 392a11e03ecc28e65bb4645adc21d13213bbaada Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Thu, 20 Jul 2017 10:08:48 -0700 Subject: [PATCH 19/31] Clear cache when extensionsPath is not valid --- src/emmetHelper.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/emmetHelper.ts b/src/emmetHelper.ts index d3cb6b4..bfd9d79 100644 --- a/src/emmetHelper.ts +++ b/src/emmetHelper.ts @@ -300,6 +300,8 @@ export function updateExtensionsPath(emmetExtensionsPath: string) { if (!emmetExtensionsPath || !emmetExtensionsPath.trim() || !path.isAbsolute(emmetExtensionsPath.trim()) || !dirExists(emmetExtensionsPath.trim())) { customSnippetRegistry = {}; snippetKeyCache.clear(); + profilesFromFile = {}; + variablesFromFile = {}; return; } From 781bf996af5c5e0a7318c6282badaba09a67dfcf Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Thu, 20 Jul 2017 10:20:46 -0700 Subject: [PATCH 20/31] Fix current line --- src/emmetHelper.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/emmetHelper.ts b/src/emmetHelper.ts index bfd9d79..f45c9ec 100644 --- a/src/emmetHelper.ts +++ b/src/emmetHelper.ts @@ -151,7 +151,7 @@ function getCurrentLine(document: TextDocument, position: Position): string { let offset = document.offsetAt(position); let text = document.getText(); let start = 0; - let end = text.length - 1; + let end = text.length; for (let i = offset - 1; i >= 0; i--) { if (text[i] === '\n') { start = i + 1; @@ -164,7 +164,7 @@ function getCurrentLine(document: TextDocument, position: Position): string { break; } } - return text.substring(start, end + 1); + return text.substring(start, end); } let customSnippetRegistry = {}; From d230b55cccae386f0fc3f6251217543f8de8565f Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Thu, 20 Jul 2017 10:25:46 -0700 Subject: [PATCH 21/31] Support expansion of commonly used html tags --- src/emmetHelper.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/emmetHelper.ts b/src/emmetHelper.ts index f45c9ec..624d25a 100644 --- a/src/emmetHelper.ts +++ b/src/emmetHelper.ts @@ -16,6 +16,7 @@ const htmlAbbreviationStartRegex = /^[a-z,A-Z,!,(,[,#,\.]/; const htmlAbbreviationEndRegex = /[a-z,A-Z,!,),\],#,\.,},\d,*,$]$/; const cssAbbreviationRegex = /^[a-z,A-Z,!,@,#]/; const emmetModes = ['html', 'pug', 'slim', 'haml', 'xml', 'xsl', 'jsx', 'css', 'scss', 'sass', 'less', 'stylus']; +const commonlyUsedTags = ['div', 'span', 'p', 'b', 'i', 'body', 'html', 'ul', 'ol', 'li', 'head', 'script']; export interface EmmetConfiguration { useNewEmmet: string; @@ -46,7 +47,7 @@ export function doComplete(document: TextDocument, position: Position, syntax: s if (isAbbreviationValid(syntax, abbreviation)) { let expandedText; // Skip cases where abc -> ${1} as this is noise - if (isStyleSheet(syntax) || !/^[a-z,A-Z]*$/.test(abbreviation) || htmlSnippetKeys.indexOf(abbreviation) > -1) { + if (isStyleSheet(syntax) || !/^[a-z,A-Z]*$/.test(abbreviation) || htmlSnippetKeys.indexOf(abbreviation) > -1 || commonlyUsedTags.indexOf(abbreviation) > -1) { try { expandedText = expand(abbreviation, expandOptions); // Skip cases when abc -> abc: ; as this is noise From 772bca6f09126256d4b0494ab33ae2728150aad9 Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Thu, 20 Jul 2017 11:20:45 -0700 Subject: [PATCH 22/31] More validations on html abbreviations --- src/emmetHelper.ts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/emmetHelper.ts b/src/emmetHelper.ts index 624d25a..c485658 100644 --- a/src/emmetHelper.ts +++ b/src/emmetHelper.ts @@ -16,7 +16,7 @@ const htmlAbbreviationStartRegex = /^[a-z,A-Z,!,(,[,#,\.]/; const htmlAbbreviationEndRegex = /[a-z,A-Z,!,),\],#,\.,},\d,*,$]$/; const cssAbbreviationRegex = /^[a-z,A-Z,!,@,#]/; const emmetModes = ['html', 'pug', 'slim', 'haml', 'xml', 'xsl', 'jsx', 'css', 'scss', 'sass', 'less', 'stylus']; -const commonlyUsedTags = ['div', 'span', 'p', 'b', 'i', 'body', 'html', 'ul', 'ol', 'li', 'head', 'script']; +const commonlyUsedTags = ['div', 'span', 'p', 'b', 'i', 'body', 'html', 'ul', 'ol', 'li', 'head', 'script', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6']; export interface EmmetConfiguration { useNewEmmet: string; @@ -47,7 +47,7 @@ export function doComplete(document: TextDocument, position: Position, syntax: s if (isAbbreviationValid(syntax, abbreviation)) { let expandedText; // Skip cases where abc -> ${1} as this is noise - if (isStyleSheet(syntax) || !/^[a-z,A-Z]*$/.test(abbreviation) || htmlSnippetKeys.indexOf(abbreviation) > -1 || commonlyUsedTags.indexOf(abbreviation) > -1) { + if (isStyleSheet(syntax) || !/^[a-z,A-Z,\d]*$/.test(abbreviation) || htmlSnippetKeys.indexOf(abbreviation) > -1 || commonlyUsedTags.indexOf(abbreviation) > -1) { try { expandedText = expand(abbreviation, expandOptions); // Skip cases when abc -> abc: ; as this is noise @@ -205,7 +205,17 @@ export function extractAbbreviation(document: TextDocument, position: Position): * @param abbreviation string */ export function isAbbreviationValid(syntax: string, abbreviation: string): boolean { - return !isStyleSheet(syntax) ? (htmlAbbreviationStartRegex.test(abbreviation) && htmlAbbreviationEndRegex.test(abbreviation)) : cssAbbreviationRegex.test(abbreviation); + if (isStyleSheet(syntax)) { + return cssAbbreviationRegex.test(abbreviation); + } + if (abbreviation.startsWith('!') && /[^!]/.test(abbreviation)) { + return false; + } + // Its common for users to type (sometextinsidebrackets), this should not be treated as an abbreviation + if (abbreviation.startsWith('(') && abbreviation.endsWith(')') && !/^\(.+[>,+,*].+\)$/.test(abbreviation)) { + return false; + } + return (htmlAbbreviationStartRegex.test(abbreviation) && htmlAbbreviationEndRegex.test(abbreviation)); } /** From cc092138151252e9b0fb526b1fb3d533933be90d Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Thu, 20 Jul 2017 11:26:12 -0700 Subject: [PATCH 23/31] Update version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1a1c15f..f577d3d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vscode-emmet-helper", - "version": "0.0.26", + "version": "0.0.27", "description": "Helper to use emmet modules in Visual Studio Code", "main": "./out/emmetHelper.js", "author": "Microsoft Corporation", From 8a96b07b8151bd38ceb28f20a57052a3120f344b Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Thu, 20 Jul 2017 18:03:44 -0700 Subject: [PATCH 24/31] Add tests (finally) --- .vscode/launch.json | 22 +++ package.json | 6 +- src/emmetHelper.ts | 91 ++++++------ src/emmetHelperTest.ts | 260 +++++++++++++++++++++++++++++++++++ testData/snippets.json | 10 ++ testData/syntaxProfiles.json | 5 + tsconfig.json | 3 - 7 files changed, 348 insertions(+), 49 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 src/emmetHelperTest.ts create mode 100644 testData/snippets.json create mode 100644 testData/syntaxProfiles.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..919dca5 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,22 @@ +{ + // Use IntelliSense to learn about possible Node.js debug attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Mocha Tests", + "program": "${workspaceRoot}/node_modules/mocha/bin/_mocha", + "args": [ + "-u", + "tdd", + "--timeout", + "999999", + "--colors", + "${workspaceRoot}/out/emmetHelperTest.js" + ] + } + ] +} \ No newline at end of file diff --git a/package.json b/package.json index f577d3d..d3840fb 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,8 @@ }, "devDependencies": { "@types/node": "^6.0.46", - "typescript": "^2.1.5" + "typescript": "^2.1.5", + "mocha": "3.4.2" }, "dependencies": { "@emmetio/expand-abbreviation": "^0.5.8", @@ -23,6 +24,7 @@ }, "scripts": { "prepublish": "tsc -p ./", - "compile": "tsc -watch -p ./" + "compile": "tsc -watch -p ./", + "test": "mocha out/emmetHelperTest.js" } } diff --git a/src/emmetHelper.ts b/src/emmetHelper.ts index c485658..453e574 100644 --- a/src/emmetHelper.ts +++ b/src/emmetHelper.ts @@ -19,7 +19,7 @@ const emmetModes = ['html', 'pug', 'slim', 'haml', 'xml', 'xsl', 'jsx', 'css', ' const commonlyUsedTags = ['div', 'span', 'p', 'b', 'i', 'body', 'html', 'ul', 'ol', 'li', 'head', 'script', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6']; export interface EmmetConfiguration { - useNewEmmet: string; + useNewEmmet: boolean; showExpandedAbbreviation: string; showAbbreviationSuggestions: boolean; syntaxProfiles: object; @@ -172,7 +172,7 @@ let customSnippetRegistry = {}; let variablesFromFile = {}; let profilesFromFile = {}; -const field = (index, placeholder) => `\${${index}${placeholder ? ':' + placeholder : ''}}`; +export const emmetSnippetField = (index, placeholder) => `\${${index}${placeholder ? ':' + placeholder : ''}}`; export function isStyleSheet(syntax): boolean { let stylesheetSyntaxes = ['css', 'scss', 'sass', 'less', 'stylus']; @@ -230,7 +230,7 @@ export function getExpandOptions(syntaxProfiles: object, variables: object, synt } return { - field: field, + field: emmetSnippetField, syntax: syntax, profile: getProfile(syntax, syntaxProfiles), addons: syntax === 'jsx' ? { 'jsx': true } : null, @@ -269,10 +269,7 @@ function getProfile(syntax: string, profilesFromSettings: object): any { newOptions['attributeQuotes'] = options[key]; break; case 'tag_nl': - newOptions['format'] = (options[key] === 'true' || options[key] === 'false') ? options[key] : 'true'; - break; - case 'indent': - newOptions['attrCase'] = (options[key] === 'true' || options[key] === 'false') ? '\t' : options[key]; + newOptions['format'] = (options[key] === true || options[key] === false) ? options[key] : true; break; case 'inline_break': newOptions['inlineBreak'] = options[key]; @@ -307,61 +304,67 @@ function getVariables(variablesFromSettings: object): any { /** * Updates customizations from snippets.json and syntaxProfiles.json files in the directory configured in emmet.extensionsPath setting */ -export function updateExtensionsPath(emmetExtensionsPath: string) { +export function updateExtensionsPath(emmetExtensionsPath: string): Promise { if (!emmetExtensionsPath || !emmetExtensionsPath.trim() || !path.isAbsolute(emmetExtensionsPath.trim()) || !dirExists(emmetExtensionsPath.trim())) { customSnippetRegistry = {}; snippetKeyCache.clear(); profilesFromFile = {}; variablesFromFile = {}; - return; + return Promise.resolve(); } let dirPath = emmetExtensionsPath.trim(); let snippetsPath = path.join(dirPath, 'snippets.json'); let profilesPath = path.join(dirPath, 'syntaxProfiles.json'); - fs.readFile(snippetsPath, (err, snippetsData) => { - if (err) { - return; - } - try { - let snippetsJson = JSON.parse(snippetsData.toString()); - variablesFromFile = snippetsJson['variables']; - customSnippetRegistry = {}; - snippetKeyCache.clear(); - Object.keys(snippetsJson).forEach(syntax => { - if (!snippetsJson[syntax]['snippets']) { - return; - } - let baseSyntax = isStyleSheet(syntax) ? 'css' : 'html'; - let customSnippets = snippetsJson[syntax]['snippets']; - if (snippetsJson[baseSyntax]['snippets'] && baseSyntax !== syntax) { - customSnippets = Object.assign({}, snippetsJson[baseSyntax]['snippets'], snippetsJson[syntax]['snippets']) - } + let snippetsPromise = new Promise((resolve, reject) => { + fs.readFile(snippetsPath, (err, snippetsData) => { + if (err) { + return resolve(); + } + try { + let snippetsJson = JSON.parse(snippetsData.toString()); + variablesFromFile = snippetsJson['variables']; + customSnippetRegistry = {}; + snippetKeyCache.clear(); + Object.keys(snippetsJson).forEach(syntax => { + if (!snippetsJson[syntax]['snippets']) { + return; + } + let baseSyntax = isStyleSheet(syntax) ? 'css' : 'html'; + let customSnippets = snippetsJson[syntax]['snippets']; + if (snippetsJson[baseSyntax]['snippets'] && baseSyntax !== syntax) { + customSnippets = Object.assign({}, snippetsJson[baseSyntax]['snippets'], snippetsJson[syntax]['snippets']) + } - customSnippetRegistry[syntax] = createSnippetsRegistry(syntax, customSnippets); + customSnippetRegistry[syntax] = createSnippetsRegistry(syntax, customSnippets); - let snippetKeys: string[] = customSnippetRegistry[syntax].all({ type: 'string' }).map(snippet => { - return snippet.key; + let snippetKeys: string[] = customSnippetRegistry[syntax].all({ type: 'string' }).map(snippet => { + return snippet.key; + }); + snippetKeyCache.set(syntax, snippetKeys); }); - snippetKeyCache.set(syntax, snippetKeys); - }); - } catch (e) { + } catch (e) { - } - }); - fs.readFile(profilesPath, (err, profilesData) => { - if (err) { - return; - } - try { - profilesFromFile = JSON.parse(profilesData.toString()); - } catch (e) { - - } + } + return resolve(); + }); }); + let variablesPromise = new Promise((resolve, reject) => { + fs.readFile(profilesPath, (err, profilesData) => { + try { + if (!err) { + profilesFromFile = JSON.parse(profilesData.toString()); + } + } catch (e) { + } + return resolve(); + }); + }); + + return Promise.all([snippetsPromise, variablesFromFile]).then(()=> Promise.resolve()); } diff --git a/src/emmetHelperTest.ts b/src/emmetHelperTest.ts new file mode 100644 index 0000000..49dab33 --- /dev/null +++ b/src/emmetHelperTest.ts @@ -0,0 +1,260 @@ +import { TextDocument, Position } from 'vscode-languageserver-types' +import { isAbbreviationValid, extractAbbreviation, getExpandOptions, emmetSnippetField, updateExtensionsPath, doComplete } from './emmetHelper'; +import { describe, it } from 'mocha'; +import * as assert from 'assert'; +import * as path from 'path'; + +const extensionsPath = path.join(path.normalize(path.join(__dirname, '..')), 'testData'); + +describe('Validate Abbreviations', () => { + it('should return true for valid abbreivations', () => { + const htmlAbbreviations = ['ul>li', 'ul', 'h1', 'ul>li*3', '(ul>li)+div', '.hello', '!', '#hello', '.item[id=ok]']; + htmlAbbreviations.forEach(abbr => { + assert(isAbbreviationValid('html', abbr)); + }); + htmlAbbreviations.forEach(abbr => { + assert(isAbbreviationValid('haml', abbr)); + }); + }); + it('should return false for invalid abbreivations', () => { + const htmlAbbreviations = ['!ul!', '(hello)']; + const cssAbbreviations = ['123']; + htmlAbbreviations.forEach(abbr => { + assert(!isAbbreviationValid('html', abbr)); + }); + htmlAbbreviations.forEach(abbr => { + assert(!isAbbreviationValid('haml', abbr)); + }); + cssAbbreviations.forEach(abbr => { + assert(!isAbbreviationValid('css', abbr)); + }); + cssAbbreviations.forEach(abbr => { + assert(!isAbbreviationValid('scss', abbr)); + }); + }) +}); + +describe('Extract Abbreviations', () => { + it('should extract abbreviations', () => { + const testCases: [string, number, number, string, number, number, number, number][] = [ + ['
ul>li*3
', 0, 7, 'ul', 0, 5, 0, 7], + ['
ul>li*3
', 0, 10, 'ul>li', 0, 5, 0, 10], + ['
ul>li*3
', 0, 12, 'ul>li*3', 0, 5, 0, 12], + ['ul>li', 0, 5, 'ul>li', 0, 0, 0, 5] + ] + + testCases.forEach(([content, positionLine, positionChar, expectedAbbr, expectedRangeStartLine, expectedRangeStartChar, expectedRangeEndLine, expectedRangeEndChar]) => { + const document = TextDocument.create('test://test/test.html', 'html', 0, content); + const position = Position.create(positionLine, positionChar); + const [abbrRange, abbr] = extractAbbreviation(document, position); + + assert.equal(expectedAbbr, abbr); + assert.equal(expectedRangeStartLine, abbrRange.start.line); + assert.equal(expectedRangeStartChar, abbrRange.start.character); + assert.equal(expectedRangeEndLine, abbrRange.end.line); + assert.equal(expectedRangeEndChar, abbrRange.end.character); + }); + }); +}); + +describe('Test Basic Expand Options', () => { + it('should check for basic expand options', () => { + const textToReplace = 'textToReplace'; + const syntax = 'anythingreally'; + let expandOptions = getExpandOptions({}, {}, syntax, textToReplace); + + assert.equal(expandOptions.field, emmetSnippetField) + assert.equal(expandOptions.syntax, syntax); + assert.equal(expandOptions.text, textToReplace) + }); +}); + +describe('Test output profile settings', () => { + it('should convert output profile from old format to new', () => { + const profile = { + tag_case: 'lower', + attr_case: 'lower', + attr_quotes: 'single', + tag_nl: true, + inline_break: 2, + self_closing_tag: 'xhtml' + } + + const expandOptions = getExpandOptions({ html: profile }, {}, 'html'); + + assert.equal(profile['tag_case'], expandOptions.profile['tagCase']); + assert.equal(profile['attr_case'], expandOptions.profile['attributeCase']); + assert.equal(profile['attr_quotes'], expandOptions.profile['attributeQuotes']); + assert.equal(profile['tag_nl'], expandOptions.profile['format']); + assert.equal(profile['inline_break'], expandOptions.profile['inlineBreak']); + assert.equal(profile['self_closing_tag'], expandOptions.profile['selfClosingStyle']); + }); + + it('should convert self_closing_style', () => { + const testCases = [true, false, 'xhtml']; + const expectedValue = ['xml', 'html', 'xhtml']; + + for (let i = 0; i < testCases.length; i++) { + const expandOptions = getExpandOptions({ html: { self_closing_tag: testCases[i] } }, {}, 'html'); + assert.equal(expandOptions.profile['selfClosingStyle'], expectedValue[i]); + } + }); + + it('should convert tag_nl', () => { + const testCases = [true, false, 'decide']; + const expectedValue = [true, false, true]; + + for (let i = 0; i < testCases.length; i++) { + const expandOptions = getExpandOptions({ html: { tag_nl: testCases[i] } }, {}, 'html'); + assert.equal(expandOptions.profile['format'], expectedValue[i]); + } + }); + + it('shoud use output profile in new format as is', () => { + const profile = { + tagCase: 'lower', + attributeCase: 'lower', + attributeQuotes: 'single', + format: true, + inlineBreak: 2, + selfClosingStyle: 'xhtml' + }; + + const expandOptions = getExpandOptions({ html: profile }, {}, 'html'); + Object.keys(profile).forEach(key => { + assert.equal(expandOptions.profile[key], profile[key]); + }); + }); + + it('should use profile from extensionsPath', () => { + updateExtensionsPath(extensionsPath).then(() => { + const profile = { + tag_case: 'lower', + attr_case: 'lower', + attr_quotes: 'single', + tag_nl: true, + inline_break: 2, + self_closing_tag: 'xhtml' + } + + const expandOptions = getExpandOptions({ html: profile }, {}, 'html'); + assert.equal(expandOptions.profile['tagCase'], 'upper'); + assert.equal(profile['tag_case'], 'lower'); + }); + }); +}); + +describe('Test variables settings', () => { + it('should take in variables as is', () => { + const variables = { + lang: 'de', + charset: 'UTF-8' + } + + const expandOptions = getExpandOptions({}, variables, 'html'); + Object.keys(variables).forEach(key => { + assert.equal(expandOptions.variables[key], variables[key]); + }); + }); + + it('should use variables from extensionsPath', () => { + updateExtensionsPath(extensionsPath).then(() => { + const variables = { + lang: 'en', + charset: 'UTF-8' + } + + const expandOptions = getExpandOptions({}, variables, 'html'); + assert.equal(expandOptions.variables['lang'], 'fr'); + assert.equal(variables['lang'], 'en'); + }); + }); +}); + +describe('Test custom snippets', () => { + it('should use custom snippets from extensionsPath', () => { + const customSnippetKey = 'ch'; + + + updateExtensionsPath(null).then(() => { + const expandOptionsWithoutCustomSnippets = getExpandOptions({}, {}, 'css'); + assert(!expandOptionsWithoutCustomSnippets.snippets); + + // Use custom snippets from extensionsPath + updateExtensionsPath(extensionsPath).then(() => { + let foundCustomSnippet = false; + let foundCustomSnippetInInhertitedSyntax = false; + + const expandOptionsWithCustomSnippets = getExpandOptions({}, {}, 'css'); + const expandOptionsWithCustomSnippetsInhertedSytnax = getExpandOptions({}, {}, 'scss'); + + expandOptionsWithoutCustomSnippets.snippets.all({ type: 'string' }).forEach(snippet => { + if (snippet.key === customSnippetKey) { + foundCustomSnippet = true; + } + }); + + expandOptionsWithCustomSnippetsInhertedSytnax.snippets.all({ type: 'string' }).forEach(snippet => { + if (snippet.key === customSnippetKey) { + foundCustomSnippet = true; + } + }); + + assert.equal(foundCustomSnippet, true); + assert.equal(foundCustomSnippetInInhertitedSyntax, true); + }); + }); + }); +}); + +describe('Test completions', () => { + it('should provide completions', () => { + updateExtensionsPath(null).then(() => { + const testCases: [string, number, number, string, string, number, number, number, number][] = [ + ['
ul>li*3
', 0, 7, 'ul', '
    ', 0, 5, 0, 7], + ['
    ul>li*3
    ', 0, 10, 'ul>li', '
      \n\t
    • \n
    ', 0, 5, 0, 10] + ]; + + testCases.forEach(([content, positionLine, positionChar, expectedAbbr, expectedExpansion, expectedRangeStartLine, expectedRangeStartChar, expectedRangeEndLine, expectedRangeEndChar]) => { + const document = TextDocument.create('test://test/test.html', 'html', 0, content); + const position = Position.create(positionLine, positionChar); + const completionList = doComplete(document, position, 'html', { + useNewEmmet: true, + showExpandedAbbreviation: 'always', + showAbbreviationSuggestions: false, + syntaxProfiles: {}, + variables: {} + }); + + assert.equal(completionList.items[0].label, expectedAbbr); + assert.equal(completionList.items[0].documentation, expectedExpansion); + }); + + }); + }); + + it('should provide completions', () => { + updateExtensionsPath(null).then(() => { + const testCases: [string, number, number][] = [ + ['
    abc
    ', 0, 8], + ['
    (div)
    ', 0, 10] + ]; + + testCases.forEach(([content, positionLine, positionChar]) => { + const document = TextDocument.create('test://test/test.html', 'html', 0, content); + const position = Position.create(positionLine, positionChar); + const completionList = doComplete(document, position, 'html', { + useNewEmmet: true, + showExpandedAbbreviation: 'always', + showAbbreviationSuggestions: false, + syntaxProfiles: {}, + variables: {} + }); + + assert.equal(completionList.items.length, 0); + }); + + }); + }); + +}) \ No newline at end of file diff --git a/testData/snippets.json b/testData/snippets.json new file mode 100644 index 0000000..53047ec --- /dev/null +++ b/testData/snippets.json @@ -0,0 +1,10 @@ +{ + "css": { + "snippets": { + "ch": "color:hsl(${1:0}, ${2:100}%, ${3:50}%);" + } + }, + "variables": { + "lang": "fr" + } +} \ No newline at end of file diff --git a/testData/syntaxProfiles.json b/testData/syntaxProfiles.json new file mode 100644 index 0000000..c818e1b --- /dev/null +++ b/testData/syntaxProfiles.json @@ -0,0 +1,5 @@ +{ + "html": { + "tag_case": "upper" + } +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index a395fbb..a78f418 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,8 +10,5 @@ }, "exclude": [ "node_modules" - ], - "include": [ - "src/**/*" ] } \ No newline at end of file From 600b5dcb4828f1ece12bdbbd4acf1c090e5d348d Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Fri, 21 Jul 2017 16:42:50 -0700 Subject: [PATCH 25/31] Provide commonly used tags as suggestions --- package.json | 2 +- src/emmetHelper.ts | 27 +++++++++++++++++++-------- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index d3840fb..5467009 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vscode-emmet-helper", - "version": "0.0.27", + "version": "0.0.28", "description": "Helper to use emmet modules in Visual Studio Code", "main": "./out/emmetHelper.js", "author": "Microsoft Corporation", diff --git a/src/emmetHelper.ts b/src/emmetHelper.ts index 453e574..f1bb387 100644 --- a/src/emmetHelper.ts +++ b/src/emmetHelper.ts @@ -84,10 +84,13 @@ export function doComplete(document: TextDocument, position: Position, syntax: s if (expandedAbbr) { // Workaround for the main expanded abbr not appearing before the snippet suggestions expandedAbbr.sortText = '0' + expandedAbbr.label; - } + } + + let currentWord = getCurrentWord(document, position); + let commonlyUsedTagSuggestions = makeSnippetSuggestion(commonlyUsedTags, currentWord, abbreviation, abbreviationRange, expandOptions); + completionItems = completionItems.concat(commonlyUsedTagSuggestions); if (emmetConfig.showAbbreviationSuggestions) { - let currentWord = getCurrentWord(document, position); let abbreviationSuggestions = getAbbreviationSuggestions(syntax, currentWord, abbreviation, abbreviationRange, expandOptions); completionItems = completionItems.concat(abbreviationSuggestions); } @@ -96,14 +99,12 @@ export function doComplete(document: TextDocument, position: Position, syntax: s return CompletionList.create(completionItems, true); } -function getAbbreviationSuggestions(syntax: string, prefix: string, abbreviation: string, abbreviationRange: Range, expandOptions: object): CompletionItem[] { - if (!prefix || isStyleSheet(syntax)) { +function makeSnippetSuggestion(snippets: string[], prefix: string, abbreviation: string, abbreviationRange: Range, expandOptions: any): CompletionItem[] { + if (!prefix) { return []; } - - let snippetKeys = snippetKeyCache.has(syntax) ? snippetKeyCache.get(syntax) : snippetKeyCache.get('html'); let snippetCompletions = []; - snippetKeys.forEach(snippetKey => { + snippets.forEach(snippetKey => { if (!snippetKey.startsWith(prefix) || snippetKey === prefix) { return; } @@ -130,10 +131,20 @@ function getAbbreviationSuggestions(syntax: string, prefix: string, abbreviation snippetCompletions.push(item); }); - return snippetCompletions; } +function getAbbreviationSuggestions(syntax: string, prefix: string, abbreviation: string, abbreviationRange: Range, expandOptions: object): CompletionItem[] { + if (!prefix || isStyleSheet(syntax)) { + return []; + } + + let snippetKeys = snippetKeyCache.has(syntax) ? snippetKeyCache.get(syntax) : snippetKeyCache.get('html'); + let snippetCompletions = []; + + return makeSnippetSuggestion(snippetKeys, prefix, abbreviation, abbreviationRange, expandOptions);; +} + function getCurrentWord(document: TextDocument, position: Position): string { let currentLine = getCurrentLine(document, position); if (currentLine) { From 435083dded5fc2885221be8e538292dac603eda1 Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Sun, 23 Jul 2017 15:05:54 -0700 Subject: [PATCH 26/31] Use custom html snippets when checking for abbreviations --- src/emmetHelper.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/emmetHelper.ts b/src/emmetHelper.ts index f1bb387..c5af595 100644 --- a/src/emmetHelper.ts +++ b/src/emmetHelper.ts @@ -16,7 +16,7 @@ const htmlAbbreviationStartRegex = /^[a-z,A-Z,!,(,[,#,\.]/; const htmlAbbreviationEndRegex = /[a-z,A-Z,!,),\],#,\.,},\d,*,$]$/; const cssAbbreviationRegex = /^[a-z,A-Z,!,@,#]/; const emmetModes = ['html', 'pug', 'slim', 'haml', 'xml', 'xsl', 'jsx', 'css', 'scss', 'sass', 'less', 'stylus']; -const commonlyUsedTags = ['div', 'span', 'p', 'b', 'i', 'body', 'html', 'ul', 'ol', 'li', 'head', 'script', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6']; +const commonlyUsedTags = ['div', 'span', 'p', 'b', 'i', 'body', 'html', 'ul', 'ol', 'li', 'head', 'script', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'section']; export interface EmmetConfiguration { useNewEmmet: boolean; @@ -38,6 +38,8 @@ export function doComplete(document: TextDocument, position: Position, syntax: s return snippet.key; }); snippetKeyCache.set('html', htmlSnippetKeys); + } else { + htmlSnippetKeys = snippetKeyCache.get('html'); } let expandedAbbr: CompletionItem; From 70e493fbeb44ef05857d39e54a2b9115d2ea9882 Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Mon, 24 Jul 2017 10:56:18 -0700 Subject: [PATCH 27/31] Support custom snippets for non html sytnax in completion --- src/emmetHelper.ts | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/emmetHelper.ts b/src/emmetHelper.ts index c5af595..b6656b4 100644 --- a/src/emmetHelper.ts +++ b/src/emmetHelper.ts @@ -11,7 +11,7 @@ import * as path from 'path'; import * as fs from 'fs'; const snippetKeyCache = new Map(); -let htmlSnippetKeys: string[]; +let markupSnippetKeys: string[]; const htmlAbbreviationStartRegex = /^[a-z,A-Z,!,(,[,#,\.]/; const htmlAbbreviationEndRegex = /[a-z,A-Z,!,),\],#,\.,},\d,*,$]$/; const cssAbbreviationRegex = /^[a-z,A-Z,!,@,#]/; @@ -32,14 +32,16 @@ export function doComplete(document: TextDocument, position: Position, syntax: s return; } - if (!snippetKeyCache.has('html')) { - let registry = customSnippetRegistry['html'] ? customSnippetRegistry['html'] : createSnippetsRegistry('html'); - htmlSnippetKeys = registry.all({ type: 'string' }).map(snippet => { - return snippet.key; - }); - snippetKeyCache.set('html', htmlSnippetKeys); - } else { - htmlSnippetKeys = snippetKeyCache.get('html'); + if (!isStyleSheet(syntax)) { + if (!snippetKeyCache.has(syntax)) { + let registry = customSnippetRegistry[syntax] ? customSnippetRegistry[syntax] : createSnippetsRegistry(syntax); + markupSnippetKeys = registry.all({ type: 'string' }).map(snippet => { + return snippet.key; + }); + snippetKeyCache.set(syntax, markupSnippetKeys); + } else { + markupSnippetKeys = snippetKeyCache.get(syntax); + } } let expandedAbbr: CompletionItem; @@ -49,7 +51,7 @@ export function doComplete(document: TextDocument, position: Position, syntax: s if (isAbbreviationValid(syntax, abbreviation)) { let expandedText; // Skip cases where abc -> ${1} as this is noise - if (isStyleSheet(syntax) || !/^[a-z,A-Z,\d]*$/.test(abbreviation) || htmlSnippetKeys.indexOf(abbreviation) > -1 || commonlyUsedTags.indexOf(abbreviation) > -1) { + if (isStyleSheet(syntax) || !/^[a-z,A-Z,\d]*$/.test(abbreviation) || markupSnippetKeys.indexOf(abbreviation) > -1 || commonlyUsedTags.indexOf(abbreviation) > -1) { try { expandedText = expand(abbreviation, expandOptions); // Skip cases when abc -> abc: ; as this is noise @@ -86,7 +88,7 @@ export function doComplete(document: TextDocument, position: Position, syntax: s if (expandedAbbr) { // Workaround for the main expanded abbr not appearing before the snippet suggestions expandedAbbr.sortText = '0' + expandedAbbr.label; - } + } let currentWord = getCurrentWord(document, position); let commonlyUsedTagSuggestions = makeSnippetSuggestion(commonlyUsedTags, currentWord, abbreviation, abbreviationRange, expandOptions); @@ -143,7 +145,7 @@ function getAbbreviationSuggestions(syntax: string, prefix: string, abbreviation let snippetKeys = snippetKeyCache.has(syntax) ? snippetKeyCache.get(syntax) : snippetKeyCache.get('html'); let snippetCompletions = []; - + return makeSnippetSuggestion(snippetKeys, prefix, abbreviation, abbreviationRange, expandOptions);; } @@ -377,7 +379,7 @@ export function updateExtensionsPath(emmetExtensionsPath: string): Promise }); }); - return Promise.all([snippetsPromise, variablesFromFile]).then(()=> Promise.resolve()); + return Promise.all([snippetsPromise, variablesFromFile]).then(() => Promise.resolve()); } From fc8d27160cfebad21d481310b7aa5fff88880651 Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Mon, 24 Jul 2017 10:57:08 -0700 Subject: [PATCH 28/31] 0.0.29 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5467009..9917f92 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vscode-emmet-helper", - "version": "0.0.28", + "version": "0.0.29", "description": "Helper to use emmet modules in Visual Studio Code", "main": "./out/emmetHelper.js", "author": "Microsoft Corporation", From ae7f6ce75e42cd70376924add1ca1f07f8f50a48 Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Wed, 26 Jul 2017 11:55:49 -0700 Subject: [PATCH 29/31] Skip abbr ending with . Microsoft/vscode#31453 --- src/emmetHelper.ts | 9 +++++++-- src/emmetHelperTest.ts | 4 +++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/emmetHelper.ts b/src/emmetHelper.ts index b6656b4..e0216f5 100644 --- a/src/emmetHelper.ts +++ b/src/emmetHelper.ts @@ -50,8 +50,13 @@ export function doComplete(document: TextDocument, position: Position, syntax: s if (isAbbreviationValid(syntax, abbreviation)) { let expandedText; - // Skip cases where abc -> ${1} as this is noise - if (isStyleSheet(syntax) || !/^[a-z,A-Z,\d]*$/.test(abbreviation) || markupSnippetKeys.indexOf(abbreviation) > -1 || commonlyUsedTags.indexOf(abbreviation) > -1) { + // Skip non stylesheet abbreviations that are just letters/numbers unless they are valid snippets or commonly used tags + // This is to avoid noise where abc -> ${1} + // Also skip abbreviations ending with `.` This will be noise when people are typing simple text and ending it with period. + if (isStyleSheet(syntax) + || (!/^[a-z,A-Z,\d]*$/.test(abbreviation) && !abbreviation.endsWith('.')) + || markupSnippetKeys.indexOf(abbreviation) > -1 + || commonlyUsedTags.indexOf(abbreviation) > -1) { try { expandedText = expand(abbreviation, expandOptions); // Skip cases when abc -> abc: ; as this is noise diff --git a/src/emmetHelperTest.ts b/src/emmetHelperTest.ts index 49dab33..90161fa 100644 --- a/src/emmetHelperTest.ts +++ b/src/emmetHelperTest.ts @@ -233,10 +233,12 @@ describe('Test completions', () => { }); }); - it('should provide completions', () => { + it('should not provide completions', () => { updateExtensionsPath(null).then(() => { const testCases: [string, number, number][] = [ ['
    abc
    ', 0, 8], + ['
    abc12
    ', 0, 10], + ['
    abc.
    ', 0, 9], ['
    (div)
    ', 0, 10] ]; From 99dcbb3766fd3f9af64926898946045f1fc34b03 Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Wed, 26 Jul 2017 14:35:08 -0700 Subject: [PATCH 30/31] Support bem filter, refactor --- package.json | 2 +- src/emmetHelper.ts | 82 ++++++++++++++++++++++++++++++++---------- src/emmetHelperTest.ts | 76 +++++++++++++++++++++++++-------------- 3 files changed, 114 insertions(+), 46 deletions(-) diff --git a/package.json b/package.json index 9917f92..e9a60e9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vscode-emmet-helper", - "version": "0.0.29", + "version": "1.0.0", "description": "Helper to use emmet modules in Visual Studio Code", "main": "./out/emmetHelper.js", "author": "Microsoft Corporation", diff --git a/src/emmetHelper.ts b/src/emmetHelper.ts index e0216f5..73607db 100644 --- a/src/emmetHelper.ts +++ b/src/emmetHelper.ts @@ -17,6 +17,8 @@ const htmlAbbreviationEndRegex = /[a-z,A-Z,!,),\],#,\.,},\d,*,$]$/; const cssAbbreviationRegex = /^[a-z,A-Z,!,@,#]/; const emmetModes = ['html', 'pug', 'slim', 'haml', 'xml', 'xsl', 'jsx', 'css', 'scss', 'sass', 'less', 'stylus']; const commonlyUsedTags = ['div', 'span', 'p', 'b', 'i', 'body', 'html', 'ul', 'ol', 'li', 'head', 'script', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'section']; +const bemFilterSuffix = 'bem'; +const filterDelimitor = '|'; export interface EmmetConfiguration { useNewEmmet: boolean; @@ -45,18 +47,18 @@ export function doComplete(document: TextDocument, position: Position, syntax: s } let expandedAbbr: CompletionItem; - let [abbreviationRange, abbreviation] = extractAbbreviation(document, position); - let expandOptions = getExpandOptions(emmetConfig.syntaxProfiles, emmetConfig.variables, syntax); + let { abbreviationRange, abbreviation, filters } = extractAbbreviation(document, position); + let expandOptions = getExpandOptions(syntax, emmetConfig.syntaxProfiles, emmetConfig.variables, filters); if (isAbbreviationValid(syntax, abbreviation)) { let expandedText; // Skip non stylesheet abbreviations that are just letters/numbers unless they are valid snippets or commonly used tags - // This is to avoid noise where abc -> ${1} - // Also skip abbreviations ending with `.` This will be noise when people are typing simple text and ending it with period. - if (isStyleSheet(syntax) - || (!/^[a-z,A-Z,\d]*$/.test(abbreviation) && !abbreviation.endsWith('.')) - || markupSnippetKeys.indexOf(abbreviation) > -1 - || commonlyUsedTags.indexOf(abbreviation) > -1) { + // This is to avoid noise where abc -> ${1} + // Also skip abbreviations ending with `.` This will be noise when people are typing simple text and ending it with period. + if (isStyleSheet(syntax) + || (!/^[a-z,A-Z,\d]*$/.test(abbreviation) && !abbreviation.endsWith('.')) + || markupSnippetKeys.indexOf(abbreviation) > -1 + || commonlyUsedTags.indexOf(abbreviation) > -1) { try { expandedText = expand(abbreviation, expandOptions); // Skip cases when abc -> abc: ; as this is noise @@ -74,6 +76,9 @@ export function doComplete(document: TextDocument, position: Position, syntax: s expandedAbbr.documentation = removeTabStops(expandedText); expandedAbbr.insertTextFormat = InsertTextFormat.Snippet; expandedAbbr.detail = 'Emmet Abbreviation'; + if (filters.indexOf('bem') > -1) { + expandedAbbr.label = abbreviation + filterDelimitor + bemFilterSuffix; + } if (isStyleSheet(syntax)) { // See https://github.com/Microsoft/vscode/issues/28933#issuecomment-309236902 // Due to this we set filterText, sortText and label to expanded abbreviation @@ -202,20 +207,54 @@ export function isStyleSheet(syntax): boolean { /** * Extracts abbreviation from the given position in the given document */ -export function extractAbbreviation(document: TextDocument, position: Position): [Range, string] { +export function extractAbbreviation(document: TextDocument, position: Position) { + let filters = []; + let pos = position.character; let currentLine = getCurrentLine(document, position); + let currentLineTillPosition = currentLine.substr(0, position.character); + let lengthOccupiedByFilter = 0; + if (currentLineTillPosition.endsWith(`${filterDelimitor}${bemFilterSuffix}`)) { + lengthOccupiedByFilter = 4; + pos -= lengthOccupiedByFilter; + filters.push(bemFilterSuffix) + } let result; try { - result = extract(currentLine, position.character, true); - } catch (e) { - + result = extract(currentLine, pos, true); + } + catch (e) { } if (!result) { - return [null, '']; + return null; } + let rangeToReplace = Range.create(position.line, result.location, position.line, result.location + result.abbreviation.length + lengthOccupiedByFilter); + return { + abbreviationRange: rangeToReplace, + abbreviation: result.abbreviation, + filters + }; +} - let rangeToReplace = Range.create(position.line, result.location, position.line, result.location + result.abbreviation.length); - return [rangeToReplace, result.abbreviation]; +export function extractAbbreviationFromText(text: string): any { + let filters = []; + let pos = text.length; + if (text.endsWith(`${filterDelimitor}${bemFilterSuffix}`)) { + pos -= 4; + filters.push(bemFilterSuffix) + } + let result; + try { + result = extract(text, pos, true); + } + catch (e) { + } + if (!result) { + return null; + } + return { + abbreviation: result.abbreviation, + filters + }; } /** @@ -243,19 +282,21 @@ export function isAbbreviationValid(syntax: string, abbreviation: string): boole * @param syntax * @param textToReplace */ -export function getExpandOptions(syntaxProfiles: object, variables: object, syntax: string, textToReplace?: string) { +export function getExpandOptions(syntax: string, syntaxProfiles?: object, variables?: object, filters?: string[], ) { let baseSyntax = isStyleSheet(syntax) ? 'css' : 'html'; if (!customSnippetRegistry[syntax] && customSnippetRegistry[baseSyntax]) { customSnippetRegistry[syntax] = customSnippetRegistry[baseSyntax]; } - + let addons = syntax === 'jsx' ? { 'jsx': true } : {}; + if (filters && filters.indexOf('bem') > -1) { + addons['bem'] = { element: '__' }; + } return { field: emmetSnippetField, syntax: syntax, profile: getProfile(syntax, syntaxProfiles), - addons: syntax === 'jsx' ? { 'jsx': true } : null, + addons: addons, variables: getVariables(variables), - text: textToReplace ? textToReplace : null, snippets: customSnippetRegistry[syntax] }; } @@ -265,6 +306,9 @@ export function getExpandOptions(syntaxProfiles: object, variables: object, synt * @param syntax */ function getProfile(syntax: string, profilesFromSettings: object): any { + if (!profilesFromSettings) { + profilesFromSettings = {}; + } let profilesConfig = Object.assign({}, profilesFromFile, profilesFromSettings); let options = profilesConfig[syntax]; diff --git a/src/emmetHelperTest.ts b/src/emmetHelperTest.ts index 90161fa..b603c7b 100644 --- a/src/emmetHelperTest.ts +++ b/src/emmetHelperTest.ts @@ -1,5 +1,5 @@ import { TextDocument, Position } from 'vscode-languageserver-types' -import { isAbbreviationValid, extractAbbreviation, getExpandOptions, emmetSnippetField, updateExtensionsPath, doComplete } from './emmetHelper'; +import { isAbbreviationValid, extractAbbreviation, extractAbbreviationFromText, getExpandOptions, emmetSnippetField, updateExtensionsPath, doComplete } from './emmetHelper'; import { describe, it } from 'mocha'; import * as assert from 'assert'; import * as path from 'path'; @@ -35,24 +35,49 @@ describe('Validate Abbreviations', () => { }); describe('Extract Abbreviations', () => { - it('should extract abbreviations', () => { - const testCases: [string, number, number, string, number, number, number, number][] = [ - ['
    ul>li*3
    ', 0, 7, 'ul', 0, 5, 0, 7], - ['
    ul>li*3
    ', 0, 10, 'ul>li', 0, 5, 0, 10], - ['
    ul>li*3
    ', 0, 12, 'ul>li*3', 0, 5, 0, 12], - ['ul>li', 0, 5, 'ul>li', 0, 0, 0, 5] + it('should extract abbreviations from document', () => { + const testCases: [string, number, number, string, number, number, number, number, string[]][] = [ + ['
    ul>li*3
    ', 0, 7, 'ul', 0, 5, 0, 7,[]], + ['
    ul>li*3
    ', 0, 10, 'ul>li', 0, 5, 0, 10,[]], + ['
    ul>li*3
    ', 0, 12, 'ul>li*3', 0, 5, 0, 12,[]], + ['ul>li', 0, 5, 'ul>li', 0, 0, 0, 5,[]], + ['ul>li|bem', 0, 9, 'ul>li', 0, 0, 0, 9,['bem']] ] - testCases.forEach(([content, positionLine, positionChar, expectedAbbr, expectedRangeStartLine, expectedRangeStartChar, expectedRangeEndLine, expectedRangeEndChar]) => { + testCases.forEach(([content, positionLine, positionChar, expectedAbbr, expectedRangeStartLine, expectedRangeStartChar, expectedRangeEndLine, expectedRangeEndChar, expectedFilters]) => { const document = TextDocument.create('test://test/test.html', 'html', 0, content); const position = Position.create(positionLine, positionChar); - const [abbrRange, abbr] = extractAbbreviation(document, position); + const {abbreviationRange, abbreviation, filters} = extractAbbreviation(document, position); - assert.equal(expectedAbbr, abbr); - assert.equal(expectedRangeStartLine, abbrRange.start.line); - assert.equal(expectedRangeStartChar, abbrRange.start.character); - assert.equal(expectedRangeEndLine, abbrRange.end.line); - assert.equal(expectedRangeEndChar, abbrRange.end.character); + assert.equal(expectedAbbr, abbreviation); + assert.equal(expectedRangeStartLine, abbreviationRange.start.line); + assert.equal(expectedRangeStartChar, abbreviationRange.start.character); + assert.equal(expectedRangeEndLine, abbreviationRange.end.line); + assert.equal(expectedRangeEndChar, abbreviationRange.end.character); + assert.equal(filters.length, expectedFilters.length); + for(let i = 0; i < filters.length; i++) { + assert.equal(filters[i], expectedFilters[i]); + } + }); + }); + + it('should extract abbreviations from text', () => { + const testCases: [string, string, string[]][] = [ + ['ul', 'ul', []], + ['ul>li', 'ul>li', []], + ['ul>li*3', 'ul>li*3', []], + ['ul>li|bem', 'ul>li', ['bem']] + ] + + testCases.forEach(([content, expectedAbbr, expectedFilters]) => { + const {abbreviation, filters} = extractAbbreviationFromText(content); + + assert.equal(expectedAbbr, abbreviation); + assert.equal(filters.length, expectedFilters.length); + for(let i = 0; i < filters.length; i++) { + assert.equal(filters[i], expectedFilters[i]); + } + }); }); }); @@ -61,11 +86,10 @@ describe('Test Basic Expand Options', () => { it('should check for basic expand options', () => { const textToReplace = 'textToReplace'; const syntax = 'anythingreally'; - let expandOptions = getExpandOptions({}, {}, syntax, textToReplace); + let expandOptions = getExpandOptions(syntax); assert.equal(expandOptions.field, emmetSnippetField) assert.equal(expandOptions.syntax, syntax); - assert.equal(expandOptions.text, textToReplace) }); }); @@ -80,7 +104,7 @@ describe('Test output profile settings', () => { self_closing_tag: 'xhtml' } - const expandOptions = getExpandOptions({ html: profile }, {}, 'html'); + const expandOptions = getExpandOptions('html', { html: profile }); assert.equal(profile['tag_case'], expandOptions.profile['tagCase']); assert.equal(profile['attr_case'], expandOptions.profile['attributeCase']); @@ -95,7 +119,7 @@ describe('Test output profile settings', () => { const expectedValue = ['xml', 'html', 'xhtml']; for (let i = 0; i < testCases.length; i++) { - const expandOptions = getExpandOptions({ html: { self_closing_tag: testCases[i] } }, {}, 'html'); + const expandOptions = getExpandOptions('html', { html: { self_closing_tag: testCases[i] } }); assert.equal(expandOptions.profile['selfClosingStyle'], expectedValue[i]); } }); @@ -105,7 +129,7 @@ describe('Test output profile settings', () => { const expectedValue = [true, false, true]; for (let i = 0; i < testCases.length; i++) { - const expandOptions = getExpandOptions({ html: { tag_nl: testCases[i] } }, {}, 'html'); + const expandOptions = getExpandOptions('html', { html: { tag_nl: testCases[i] } }); assert.equal(expandOptions.profile['format'], expectedValue[i]); } }); @@ -120,7 +144,7 @@ describe('Test output profile settings', () => { selfClosingStyle: 'xhtml' }; - const expandOptions = getExpandOptions({ html: profile }, {}, 'html'); + const expandOptions = getExpandOptions('html', { html: profile }); Object.keys(profile).forEach(key => { assert.equal(expandOptions.profile[key], profile[key]); }); @@ -137,7 +161,7 @@ describe('Test output profile settings', () => { self_closing_tag: 'xhtml' } - const expandOptions = getExpandOptions({ html: profile }, {}, 'html'); + const expandOptions = getExpandOptions('html', { html: profile }); assert.equal(expandOptions.profile['tagCase'], 'upper'); assert.equal(profile['tag_case'], 'lower'); }); @@ -151,7 +175,7 @@ describe('Test variables settings', () => { charset: 'UTF-8' } - const expandOptions = getExpandOptions({}, variables, 'html'); + const expandOptions = getExpandOptions('html', {}, variables); Object.keys(variables).forEach(key => { assert.equal(expandOptions.variables[key], variables[key]); }); @@ -164,7 +188,7 @@ describe('Test variables settings', () => { charset: 'UTF-8' } - const expandOptions = getExpandOptions({}, variables, 'html'); + const expandOptions = getExpandOptions('html', {}, variables); assert.equal(expandOptions.variables['lang'], 'fr'); assert.equal(variables['lang'], 'en'); }); @@ -177,7 +201,7 @@ describe('Test custom snippets', () => { updateExtensionsPath(null).then(() => { - const expandOptionsWithoutCustomSnippets = getExpandOptions({}, {}, 'css'); + const expandOptionsWithoutCustomSnippets = getExpandOptions('css'); assert(!expandOptionsWithoutCustomSnippets.snippets); // Use custom snippets from extensionsPath @@ -185,8 +209,8 @@ describe('Test custom snippets', () => { let foundCustomSnippet = false; let foundCustomSnippetInInhertitedSyntax = false; - const expandOptionsWithCustomSnippets = getExpandOptions({}, {}, 'css'); - const expandOptionsWithCustomSnippetsInhertedSytnax = getExpandOptions({}, {}, 'scss'); + const expandOptionsWithCustomSnippets = getExpandOptions('css'); + const expandOptionsWithCustomSnippetsInhertedSytnax = getExpandOptions('scss'); expandOptionsWithoutCustomSnippets.snippets.all({ type: 'string' }).forEach(snippet => { if (snippet.key === customSnippetKey) { From 1184a6c6d427bb96df8dce16a4899c96c9f45529 Mon Sep 17 00:00:00 2001 From: Ramya Rao Date: Fri, 28 Jul 2017 16:08:11 -0700 Subject: [PATCH 31/31] Updated Readme as per latest changes --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f283b15..b8c1eb9 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,9 @@ A helper module to use emmet modules with Visual Studio Code Visual Studio Code extensions that provide language service and want to provide emmet abbreviation expansions -in auto-complete can include this module and use the EmmetCompletionProvider. -Just pass the one of the emmet supported syntaxes that you would like the completion provider to use. +in auto-complete can include this module and use the `doComplete` method. +Just pass the one of the emmet supported syntaxes that you would like the completion provider to use along with other parameters that you would generally pass to a completion provider. -If `emmet.syntaxPofiles` has a mapping for your language, then the builit-in emmet extension will provide +If `emmet.includeLanguages` has a mapping for your language, then the builit-in emmet extension will provide html emmet abbreviations. Ask the user to remove the mapping, if your extension decides to provide -emmet completions using this module \ No newline at end of file +emmet completions using this module