diff --git a/.eslintrc.json b/.eslintrc.json index 62e6178c38..b9145f7a53 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -21,5 +21,9 @@ ], "no-throw-literal": "warn", "semi": "off" - } + }, + "ignorePatterns": [ + "**/vendor/**/*.ts", + "**/vendor/**/*.js" + ] } \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index ef8fe33cdb..8e4aec3cce 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -36,6 +36,25 @@ "${workspaceFolder}/out/test/**/*.js" ], "preLaunchTask": "${defaultBuildTask}" + }, + { + "name": "Update fixtures", + "type": "extensionHost", + "request": "launch", + "env": { + "CURSORLESS_TEST": "true", + "CURSORLESS_TEST_UPDATE_FIXTURES": "true", + }, + "args": [ + "--disable-extension", + "asvetliakov.vscode-neovim", + "--extensionDevelopmentPath=${workspaceFolder}", + "--extensionTestsPath=${workspaceFolder}/out/test/suite/index" + ], + "outFiles": [ + "${workspaceFolder}/out/test/**/*.js" + ], + "preLaunchTask": "${defaultBuildTask}" } ] } \ No newline at end of file diff --git a/cursorless-snippets/ifElseStatement.cursorless-snippets b/cursorless-snippets/ifElseStatement.cursorless-snippets new file mode 100644 index 0000000000..fa05102ce1 --- /dev/null +++ b/cursorless-snippets/ifElseStatement.cursorless-snippets @@ -0,0 +1,49 @@ +{ + "ifElseStatement": { + "definitions": [ + { + "scope": { + "langIds": [ + "typescript", + "typescriptreact", + "javascript", + "javascriptreact", + "cpp", + "c", + "java", + "csharp" + ] + }, + "body": [ + "if ($condition) {", + "\t$consequence", + "} else {", + "\t$alternative", + "}" + ] + }, + { + "scope": { + "langIds": [ + "python" + ] + }, + "body": [ + "if $condition:", + "\t$consequence", + "else:", + "\t$alternative" + ] + } + ], + "description": "If else statement", + "variables": { + "consequence": { + "wrapperScopeType": "statement" + }, + "alternative": { + "wrapperScopeType": "statement" + } + } + } +} diff --git a/cursorless-snippets/ifStatement.cursorless-snippets b/cursorless-snippets/ifStatement.cursorless-snippets new file mode 100644 index 0000000000..3eb229ea3e --- /dev/null +++ b/cursorless-snippets/ifStatement.cursorless-snippets @@ -0,0 +1,42 @@ +{ + "ifStatement": { + "definitions": [ + { + "scope": { + "langIds": [ + "typescript", + "typescriptreact", + "javascript", + "javascriptreact", + "cpp", + "c", + "java", + "csharp" + ] + }, + "body": [ + "if ($condition) {", + "\t$consequence", + "}" + ] + }, + { + "scope": { + "langIds": [ + "python" + ] + }, + "body": [ + "if $condition:", + "\t$consequence" + ] + } + ], + "description": "If statement", + "variables": { + "consequence": { + "wrapperScopeType": "statement" + } + } + } +} diff --git a/cursorless-snippets/tryCatchStatement.cursorless-snippets b/cursorless-snippets/tryCatchStatement.cursorless-snippets new file mode 100644 index 0000000000..4c0155f0a5 --- /dev/null +++ b/cursorless-snippets/tryCatchStatement.cursorless-snippets @@ -0,0 +1,49 @@ +{ + "tryCatchStatement": { + "definitions": [ + { + "scope": { + "langIds": [ + "typescript", + "typescriptreact", + "javascript", + "javascriptreact", + "cpp", + "c", + "java", + "csharp" + ] + }, + "body": [ + "try {", + "\t$body", + "} catch ($error) {", + "\t$exceptBody", + "}" + ] + }, + { + "scope": { + "langIds": [ + "python" + ] + }, + "body": [ + "try:", + "\t$body", + "except $error:", + "\t$exceptBody" + ] + } + ], + "description": "Try catch statement", + "variables": { + "body": { + "wrapperScopeType": "statement" + }, + "exceptBody": { + "wrapperScopeType": "statement" + } + } + } +} diff --git a/docs/experimental/snippets.md b/docs/experimental/snippets.md new file mode 100644 index 0000000000..a5ab2fe372 --- /dev/null +++ b/docs/experimental/snippets.md @@ -0,0 +1,5 @@ +# Cursorless snippets + +Cursorless has experimental support for snippets. Currently these snippets are just used for wrapping targets. + +The best place to start is to look at the [core cursorless snippets](../../cursorless-snippets). Additionally, there is autocomplete with documentation as you're writing a snippet. diff --git a/images/nestWrapNearPastDrum.gif b/images/nestWrapNearPastDrum.gif new file mode 100644 index 0000000000..a6106af7c3 Binary files /dev/null and b/images/nestWrapNearPastDrum.gif differ diff --git a/images/tryWrapFine.gif b/images/tryWrapFine.gif new file mode 100644 index 0000000000..d02b9e13c3 Binary files /dev/null and b/images/tryWrapFine.gif differ diff --git a/package.json b/package.json index 70fe8801b4..645669b693 100644 --- a/package.json +++ b/package.json @@ -400,9 +400,27 @@ "verticalOffset": 0 } } + }, + "cursorless.experimental.snippetsDir": { + "description": "Directory containing snippets for use in cursorless", + "type": "string" } } - } + }, + "languages": [ + { + "id": "json", + "extensions": [ + ".cursorless-snippets" + ] + } + ], + "jsonValidation": [ + { + "fileMatch": "*.cursorless-snippets", + "url": "./schemas/cursorless-snippets.json" + } + ] }, "scripts": { "vscode:prepublish": "npm run -S esbuild-base -- --minify", @@ -420,7 +438,7 @@ "@types/glob": "^7.1.3", "@types/js-yaml": "^4.0.2", "@types/mocha": "^8.0.4", - "@types/node": "^12.11.7", + "@types/node": "^16.11.3", "@types/sinon": "^10.0.2", "@types/vscode": "^1.53.0", "@typescript-eslint/eslint-plugin": "^4.9.0", @@ -432,7 +450,7 @@ "js-yaml": "^4.1.0", "mocha": "^8.1.3", "sinon": "^11.1.1", - "typescript": "^4.1.2", + "typescript": "^4.4.4", "vscode-test": "^1.4.1" }, "dependencies": { @@ -440,4 +458,4 @@ "immutability-helper": "^3.1.1", "lodash": "^4.17.21" } -} \ No newline at end of file +} diff --git a/schemas/cursorless-snippets.json b/schemas/cursorless-snippets.json new file mode 100644 index 0000000000..0a1e648813 --- /dev/null +++ b/schemas/cursorless-snippets.json @@ -0,0 +1,97 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Snippets for use in cursorless", + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "definitions": { + "type": "array", + "descriptions": "List of possible definitions for this snippet", + "items": { + "type": "object", + "properties": { + "scope": { + "type": "object", + "description": "Scopes where this snippet is active", + "properties": { + "langIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "scopeType": { + "$ref": "#/$defs/scopeType", + "description": "Cursorless scopes in which this snippet is active. Allows, for example, to have different snippets to define a function if you're in a class or at global scope." + } + } + }, + "body": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Inline snippet text using VSCode snippet syntax; entries joined by newline. Named variables of the form $foo can be used as wrappers" + } + }, + "required": [ + "body" + ] + } + }, + "variables": { + "type": "object", + "description": "For each named variable in the snippet, provides extra information about the variable.", + "additionalProperties": { + "type": "object", + "properties": { + "wrapperScopeType": { + "$ref": "#/$defs/scopeType", + "description": "Default to this scope type when wrapping a target without scope type specified" + }, + "description": { + "type": "string", + "description": "Description of the snippet variable" + } + } + } + }, + "description": { + "type": "string", + "description": "Description of the snippet" + } + } + }, + "$defs": { + "scopeType": { + "type": "string", + "enum": [ + "argumentOrParameter", + "anonymousFunction", + "attribute", + "class", + "className", + "collectionItem", + "collectionKey", + "comment", + "functionCall", + "functionName", + "ifStatement", + "list", + "map", + "name", + "namedFunction", + "regularExpression", + "statement", + "string", + "type", + "value", + "xmlBothTags", + "xmlElement", + "xmlEndTag", + "xmlStartTag" + ] + } + } +} \ No newline at end of file diff --git a/src/actions/BringMoveSwap.ts b/src/actions/BringMoveSwap.ts index 948dd27d44..52023d07aa 100644 --- a/src/actions/BringMoveSwap.ts +++ b/src/actions/BringMoveSwap.ts @@ -31,7 +31,7 @@ interface MarkEntry { } class BringMoveSwap implements Action { - targetPreferences: ActionPreferences[] = [ + getTargetPreferences: () => ActionPreferences[] = () => [ { insideOutsideType: null }, { insideOutsideType: null }, ]; diff --git a/src/actions/Call.ts b/src/actions/Call.ts index ee63f5adf4..fc5a72554c 100644 --- a/src/actions/Call.ts +++ b/src/actions/Call.ts @@ -8,7 +8,7 @@ import { import { ensureSingleTarget } from "../util/targetUtils"; export default class Call implements Action { - targetPreferences: ActionPreferences[] = [ + getTargetPreferences: () => ActionPreferences[] = () => [ { insideOutsideType: "inside" }, { insideOutsideType: "inside" }, ]; diff --git a/src/actions/Clear.ts b/src/actions/Clear.ts index 2e9056a82d..01a97c64fe 100644 --- a/src/actions/Clear.ts +++ b/src/actions/Clear.ts @@ -9,7 +9,7 @@ import { ensureSingleEditor } from "../util/targetUtils"; import { setSelectionsAndFocusEditor } from "../util/setSelectionsAndFocusEditor"; export default class Clear implements Action { - targetPreferences: ActionPreferences[] = [{ insideOutsideType: "inside" }]; + getTargetPreferences: () => ActionPreferences[] = () => [{ insideOutsideType: "inside" }]; constructor(private graph: Graph) { this.run = this.run.bind(this); diff --git a/src/actions/CommandAction.ts b/src/actions/CommandAction.ts index 39cbc5793d..42aa2ca2e3 100644 --- a/src/actions/CommandAction.ts +++ b/src/actions/CommandAction.ts @@ -18,7 +18,7 @@ import { callFunctionAndUpdateSelections } from "../util/updateSelections"; import { ensureSingleEditor } from "../util/targetUtils"; export default class CommandAction implements Action { - targetPreferences: ActionPreferences[] = [{ insideOutsideType: "inside" }]; + getTargetPreferences: () => ActionPreferences[] = () => [{ insideOutsideType: "inside" }]; private ensureSingleEditor: boolean; constructor( diff --git a/src/actions/CopyLines.ts b/src/actions/CopyLines.ts index c3f40f8192..82d39e9cb8 100644 --- a/src/actions/CopyLines.ts +++ b/src/actions/CopyLines.ts @@ -14,7 +14,7 @@ import unifyRanges from "../util/unifyRanges"; import expandToContainingLine from "../util/expandToContainingLine"; class CopyLines implements Action { - targetPreferences: ActionPreferences[] = [{ insideOutsideType: "inside" }]; + getTargetPreferences: () => ActionPreferences[] = () => [{ insideOutsideType: "inside" }]; constructor(private graph: Graph, private isUp: boolean) { this.run = this.run.bind(this); diff --git a/src/actions/CutCopyPaste.ts b/src/actions/CutCopyPaste.ts index 396075afa3..762b43241e 100644 --- a/src/actions/CutCopyPaste.ts +++ b/src/actions/CutCopyPaste.ts @@ -12,7 +12,7 @@ import { getOutsideOverflow } from "../util/targetUtils"; import { zip } from "lodash"; export class Cut implements Action { - targetPreferences: ActionPreferences[] = [{ insideOutsideType: null }]; + getTargetPreferences: () => ActionPreferences[] = () => [{ insideOutsideType: null }]; constructor(private graph: Graph) { this.run = this.run.bind(this); diff --git a/src/actions/Delete.ts b/src/actions/Delete.ts index 5190370c33..cd02f2b61e 100644 --- a/src/actions/Delete.ts +++ b/src/actions/Delete.ts @@ -11,7 +11,7 @@ import { flatten } from "lodash"; import { performEditsAndUpdateSelections } from "../util/updateSelections"; export default class Delete implements Action { - targetPreferences: ActionPreferences[] = [{ insideOutsideType: "outside" }]; + getTargetPreferences: () => ActionPreferences[] = () => [{ insideOutsideType: "outside" }]; constructor(private graph: Graph) { this.run = this.run.bind(this); diff --git a/src/actions/EditNewLine.ts b/src/actions/EditNewLine.ts index 2505cdcad4..9616de7360 100644 --- a/src/actions/EditNewLine.ts +++ b/src/actions/EditNewLine.ts @@ -8,7 +8,7 @@ import { import { commands, Selection } from "vscode"; class EditNewLine implements Action { - targetPreferences: ActionPreferences[] = [{ insideOutsideType: "inside" }]; + getTargetPreferences: () => ActionPreferences[] = () => [{ insideOutsideType: "inside" }]; constructor(private graph: Graph, private isAbove: boolean) { this.run = this.run.bind(this); diff --git a/src/actions/ExtractVariable.ts b/src/actions/ExtractVariable.ts index 2c51e737b5..f9fe28df9b 100644 --- a/src/actions/ExtractVariable.ts +++ b/src/actions/ExtractVariable.ts @@ -9,7 +9,7 @@ import { ensureSingleTarget } from "../util/targetUtils"; import { commands } from "vscode"; export default class ExtractVariable implements Action { - targetPreferences: ActionPreferences[] = [{ insideOutsideType: "inside" }]; + getTargetPreferences: () => ActionPreferences[] = () => [{ insideOutsideType: "inside" }]; constructor(private graph: Graph) { this.run = this.run.bind(this); diff --git a/src/actions/Find.ts b/src/actions/Find.ts index a8e61d02bf..26d5ad5239 100644 --- a/src/actions/Find.ts +++ b/src/actions/Find.ts @@ -9,7 +9,7 @@ import { commands } from "vscode"; import { ensureSingleTarget } from "../util/targetUtils"; export class FindInFiles implements Action { - targetPreferences: ActionPreferences[] = [{ insideOutsideType: "inside" }]; + getTargetPreferences: () => ActionPreferences[] = () => [{ insideOutsideType: "inside" }]; constructor(private graph: Graph) { this.run = this.run.bind(this); diff --git a/src/actions/Fold.ts b/src/actions/Fold.ts index 1448b02ddd..622e78536c 100644 --- a/src/actions/Fold.ts +++ b/src/actions/Fold.ts @@ -9,7 +9,7 @@ import { import { ensureSingleEditor } from "../util/targetUtils"; class FoldAction implements Action { - targetPreferences: ActionPreferences[] = [{ insideOutsideType: "outside" }]; + getTargetPreferences: () => ActionPreferences[] = () => [{ insideOutsideType: "outside" }]; constructor(private command: string) { this.run = this.run.bind(this); diff --git a/src/actions/GetText.ts b/src/actions/GetText.ts index d071929a8a..f06f1c5077 100644 --- a/src/actions/GetText.ts +++ b/src/actions/GetText.ts @@ -9,7 +9,7 @@ import displayPendingEditDecorations from "../util/editDisplayUtils"; import { ensureSingleTarget } from "../util/targetUtils"; export default class GetText implements Action { - targetPreferences: ActionPreferences[] = [{ insideOutsideType: "inside" }]; + getTargetPreferences: () => ActionPreferences[] = () => [{ insideOutsideType: "inside" }]; constructor(private graph: Graph) { this.run = this.run.bind(this); diff --git a/src/actions/InsertEmptyLines.ts b/src/actions/InsertEmptyLines.ts index 7e61ae9514..248088edba 100644 --- a/src/actions/InsertEmptyLines.ts +++ b/src/actions/InsertEmptyLines.ts @@ -12,7 +12,7 @@ import { performEditsAndUpdateSelections } from "../util/updateSelections"; import { flatten } from "lodash"; class InsertEmptyLines implements Action { - targetPreferences: ActionPreferences[] = [{ insideOutsideType: "inside" }]; + getTargetPreferences: () => ActionPreferences[] = () => [{ insideOutsideType: "inside" }]; constructor( private graph: Graph, diff --git a/src/actions/Replace.ts b/src/actions/Replace.ts index 0812552486..95a3490cb5 100644 --- a/src/actions/Replace.ts +++ b/src/actions/Replace.ts @@ -14,7 +14,7 @@ import { performEditsAndUpdateSelections } from "../util/updateSelections"; type RangeGenerator = { start: number }; export default class implements Action { - targetPreferences: ActionPreferences[] = [{ insideOutsideType: null }]; + getTargetPreferences: () => ActionPreferences[] = () => [{ insideOutsideType: null }]; constructor(private graph: Graph) { this.run = this.run.bind(this); diff --git a/src/actions/Scroll.ts b/src/actions/Scroll.ts index 901c9e4f29..3684276587 100644 --- a/src/actions/Scroll.ts +++ b/src/actions/Scroll.ts @@ -11,7 +11,7 @@ import { commands, window, workspace } from "vscode"; import { focusEditor } from "../util/setSelectionsAndFocusEditor"; class Scroll implements Action { - targetPreferences: ActionPreferences[] = [{ insideOutsideType: "inside" }]; + getTargetPreferences: () => ActionPreferences[] = () => [{ insideOutsideType: "inside" }]; constructor(private graph: Graph, private at: string) { this.run = this.run.bind(this); diff --git a/src/actions/SetBreakpoint.ts b/src/actions/SetBreakpoint.ts index 160d83da15..82db116266 100644 --- a/src/actions/SetBreakpoint.ts +++ b/src/actions/SetBreakpoint.ts @@ -25,7 +25,7 @@ function getBreakpoints(uri: Uri, range: Range) { } export default class SetBreakpoint implements Action { - targetPreferences: ActionPreferences[] = [ + getTargetPreferences: () => ActionPreferences[] = () => [ { insideOutsideType: "inside", selectionType: "line" }, ]; diff --git a/src/actions/SetSelection.ts b/src/actions/SetSelection.ts index 41337a2f2e..64f4014792 100644 --- a/src/actions/SetSelection.ts +++ b/src/actions/SetSelection.ts @@ -10,7 +10,7 @@ import { Selection } from "vscode"; import { setSelectionsAndFocusEditor } from "../util/setSelectionsAndFocusEditor"; export class SetSelection implements Action { - targetPreferences: ActionPreferences[] = [{ insideOutsideType: "inside" }]; + getTargetPreferences: () => ActionPreferences[] = () => [{ insideOutsideType: "inside" }]; constructor(private graph: Graph) { this.run = this.run.bind(this); diff --git a/src/actions/Sort.ts b/src/actions/Sort.ts index b70d405b97..28fe9588f9 100644 --- a/src/actions/Sort.ts +++ b/src/actions/Sort.ts @@ -7,7 +7,7 @@ import { } from "../typings/Types"; export class Sort implements Action { - targetPreferences: ActionPreferences[] = [{ insideOutsideType: "inside" }]; + getTargetPreferences: () => ActionPreferences[] = () => [{ insideOutsideType: "inside" }]; constructor(private graph: Graph) { this.run = this.run.bind(this); diff --git a/src/actions/Wrap.ts b/src/actions/Wrap.ts index cdf26f22da..a062bc53db 100644 --- a/src/actions/Wrap.ts +++ b/src/actions/Wrap.ts @@ -14,7 +14,9 @@ import { performEditsAndUpdateSelections } from "../util/updateSelections"; import { selectionWithEditorFromPositions } from "../util/selectionUtils"; export default class Wrap implements Action { - targetPreferences: ActionPreferences[] = [{ insideOutsideType: "inside" }]; + getTargetPreferences: () => ActionPreferences[] = () => [ + { insideOutsideType: "inside" }, + ]; constructor(private graph: Graph) { this.run = this.run.bind(this); diff --git a/src/actions/WrapWithSnippet.ts b/src/actions/WrapWithSnippet.ts new file mode 100644 index 0000000000..91d366c12f --- /dev/null +++ b/src/actions/WrapWithSnippet.ts @@ -0,0 +1,190 @@ +import { commands } from "vscode"; +import { SnippetDefinition } from "../typings/snippet"; +import { + Action, + ActionPreferences, + ActionReturnValue, + Graph, + TypedSelection, +} from "../typings/Types"; +import displayPendingEditDecorations from "../util/editDisplayUtils"; +import { ensureSingleEditor } from "../util/targetUtils"; +import { callFunctionAndUpdateSelections } from "../util/updateSelections"; +import { + Placeholder, + SnippetParser, + TextmateSnippet, + Variable, +} from "../vendor/snippet/snippetParser"; +import { KnownSnippetVariableNames } from "../vendor/snippet/snippetVariables"; + +export default class WrapWithSnippet implements Action { + private snippetParser = new SnippetParser(); + + getTargetPreferences(snippetLocation: string): ActionPreferences[] { + const [snippetName, placeholderName] = + parseSnippetLocation(snippetLocation); + + const snippet = this.graph.snippets.getSnippet(snippetName); + + if (snippet == null) { + throw new Error(`Couldn't find snippet ${snippetName}`); + } + + const variables = snippet.variables ?? {}; + const defaultScopeType = variables[placeholderName]?.wrapperScopeType; + + return [ + { + insideOutsideType: "inside", + modifier: + defaultScopeType == null + ? undefined + : { + type: "containingScope", + scopeType: defaultScopeType, + includeSiblings: false, + }, + }, + ]; + } + + constructor(private graph: Graph) { + this.run = this.run.bind(this); + } + + async run( + [targets]: [TypedSelection[]], + snippetLocation: string + ): Promise { + const [snippetName, placeholderName] = + parseSnippetLocation(snippetLocation); + + const snippet = this.graph.snippets.getSnippet(snippetName)!; + + const editor = ensureSingleEditor(targets); + + // Find snippet definition matching context. + // NB: We only look at the first target to create our context. This means + // that if there are two snippets that match two different contexts, and + // the two targets match those two different contexts, we will just use the + // snippet that matches the first context for both targets + const definition = findMatchingSnippetDefinition( + targets[0], + snippet.definitions + ); + + if (definition == null) { + throw new Error("Couldn't find matching snippet definition"); + } + + const parsedSnippet = this.snippetParser.parse(definition.body.join("\n")); + + transformSnippetVariables(parsedSnippet, placeholderName); + + const snippetString = parsedSnippet.toTextmateString(); + + await displayPendingEditDecorations( + targets, + this.graph.editStyles.pendingModification0 + ); + + const targetSelections = targets.map( + (target) => target.selection.selection + ); + + await this.graph.actions.setSelection.run([targets]); + + // NB: We used the command "editor.action.insertSnippet" instead of calling editor.insertSnippet + // because the latter doesn't support special variables like CLIPBOARD + const [updatedTargetSelections] = await callFunctionAndUpdateSelections( + () => + commands.executeCommand("editor.action.insertSnippet", { + snippet: snippetString, + }), + editor, + [targetSelections] + ); + + return { + thatMark: updatedTargetSelections.map((selection) => ({ + editor, + selection, + })), + }; + } +} + +/** + * Replaces the snippet variable with name `placeholderName` with TM_SELECTED_TEXT + * + * Also replaces any unknown variables with placeholders. We do this so it's + * easier to leave one of the placeholders blank. We may make it so that you + * can disable this with a setting in the future + * @param parsedSnippet The parsed textmate snippet to operate on + * @param placeholderName The variable name to replace with TM_SELECTED_TEXT + */ +function transformSnippetVariables( + parsedSnippet: TextmateSnippet, + placeholderName: string +) { + var placeholderIndex = getMaxPlaceholderIndex(parsedSnippet) + 1; + + parsedSnippet.walk((candidate) => { + if (candidate instanceof Variable) { + if (candidate.name === placeholderName) { + candidate.name = "TM_SELECTED_TEXT"; + } else if (!KnownSnippetVariableNames[candidate.name]) { + const placeholder = new Placeholder(placeholderIndex++); + candidate.children.forEach((child) => placeholder.appendChild(child)); + candidate.parent.replace(candidate, [placeholder]); + } + } + return true; + }); +} + +function getMaxPlaceholderIndex(parsedSnippet: TextmateSnippet) { + var placeholderIndex = 0; + parsedSnippet.walk((candidate) => { + if (candidate instanceof Placeholder) { + placeholderIndex = Math.max(placeholderIndex, candidate.index); + } + return true; + }); + return placeholderIndex; +} + +function parseSnippetLocation(snippetLocation: string): [string, string] { + const [snippetName, placeholderName] = snippetLocation.split("."); + if (snippetName == null || placeholderName == null) { + throw new Error("Snippet location missing '.'"); + } + return [snippetName, placeholderName]; +} + +function findMatchingSnippetDefinition( + typedSelection: TypedSelection, + definitions: SnippetDefinition[] +) { + const languageId = typedSelection.selection.editor.document.languageId; + + return definitions.find(({ scope }) => { + if (scope == null) { + return true; + } + + const { langIds, scopeType } = scope; + + if (langIds != null && !langIds.includes(languageId)) { + return false; + } + + if (scopeType != null) { + // TODO: Implement scope types by refactoring code out of processScopeType + throw new Error("Scope types not yet implemented"); + } + + return true; + }); +} diff --git a/src/actions/index.ts b/src/actions/index.ts index 3428a6c159..07659408ec 100644 --- a/src/actions/index.ts +++ b/src/actions/index.ts @@ -27,6 +27,7 @@ import { CopyLinesUp, CopyLinesDown } from "./CopyLines"; import SetBreakpoint from "./SetBreakpoint"; import { Sort, Reverse } from "./Sort"; import Call from "./Call"; +import WrapWithSnippet from "./WrapWithSnippet"; class Actions implements ActionRecord { constructor(private graph: Graph) {} @@ -66,6 +67,7 @@ class Actions implements ActionRecord { toggleLineComment = new CommentLines(this.graph); unfoldRegion = new Unfold(this.graph); wrapWithPairedDelimiter = new Wrap(this.graph); + wrapWithSnippet = new WrapWithSnippet(this.graph); } export default Actions; diff --git a/src/core/Snippets.ts b/src/core/Snippets.ts new file mode 100644 index 0000000000..12457bcad7 --- /dev/null +++ b/src/core/Snippets.ts @@ -0,0 +1,187 @@ +import { readFile, stat } from "fs/promises"; +import { cloneDeep, max, merge } from "lodash"; +import { join } from "path"; +import { workspace } from "vscode"; +import { walkFiles } from "../testUtil/walkAsync"; +import { Snippet, SnippetMap } from "../typings/snippet"; +import { Graph } from "../typings/Types"; +import { mergeStrict } from "../util/object"; + +const SNIPPET_DIR_REFRESH_INTERVAL_MS = 1000; + +/** + * Handles all cursorless snippets, including core, third-party and + * user-defined. Merges these collections and allows looking up snippets by + * name. + */ +export class Snippets { + private coreSnippets!: SnippetMap; + private thirdPartySnippets: Record = {}; + private userSnippets!: SnippetMap; + + private mergedSnippets!: SnippetMap; + + private userSnippetsDir?: string; + + /** + * The maximum modification time of any snippet in user snippets dir. + * + * This variable will be set to -1 if no user snippets have yet been read or + * if the user snippets path has changed. + * + * This variable will be set to 0 if the user has no snippets dir configured and + * we've already set userSnippets to {}. + */ + private maxSnippetMtimeMs: number = -1; + + constructor(private graph: Graph) { + this.updateUserSnippetsPath(); + + this.updateUserSnippets = this.updateUserSnippets.bind(this); + this.registerThirdPartySnippets = + this.registerThirdPartySnippets.bind(this); + + const timer = setInterval( + this.updateUserSnippets, + SNIPPET_DIR_REFRESH_INTERVAL_MS + ); + + graph.extensionContext.subscriptions.push( + workspace.onDidChangeConfiguration(() => { + if (this.updateUserSnippetsPath()) { + this.updateUserSnippets(); + } + }), + { + dispose() { + clearInterval(timer); + }, + } + ); + } + + async init() { + const extensionPath = this.graph.extensionContext.extensionPath; + const snippetsDir = join(extensionPath, "cursorless-snippets"); + const snippetFiles = await walkFiles(snippetsDir); + this.coreSnippets = mergeStrict( + ...(await Promise.all( + snippetFiles.map(async (path) => + JSON.parse(await readFile(path, "utf8")) + ) + )) + ); + await this.updateUserSnippets(); + } + + /** + * Updates the userSnippetsDir field if it has change, returning a boolean + * indicating whether there was an update. If there was an update, resets the + * maxSnippetMtime to -1 to ensure snippet update. + * @returns Boolean indicating whether path has changed + */ + private updateUserSnippetsPath(): boolean { + const newUserSnippetsDir = workspace + .getConfiguration("cursorless.experimental") + .get("snippetsDir"); + + if (newUserSnippetsDir === this.userSnippetsDir) { + return false; + } + + // Reset mtime to -1 so that next time we'll update the snippets + this.maxSnippetMtimeMs = -1; + + this.userSnippetsDir = newUserSnippetsDir; + + return true; + } + + async updateUserSnippets() { + const snippetFiles = this.userSnippetsDir + ? await walkFiles(this.userSnippetsDir) + : []; + + const maxSnippetMtime = + max( + (await Promise.all(snippetFiles.map((file) => stat(file)))).map( + (stat) => stat.mtimeMs + ) + ) ?? 0; + + if (maxSnippetMtime <= this.maxSnippetMtimeMs) { + return; + } + + this.maxSnippetMtimeMs = maxSnippetMtime; + + this.userSnippets = mergeStrict( + ...(await Promise.all( + snippetFiles.map(async (path) => + JSON.parse(await readFile(path, "utf8")) + ) + )) + ); + + this.mergeSnippets(); + } + + /** + * Allows extensions to register third-party snippets. Calling this function + * twice with the same extensionId will replace the older snippets. + * + * Note that third-party snippets take precedence over core snippets, but + * user snippets take precedence over both. + * @param extensionId The id of the extension registering the snippets. + * @param snippets The snippets to be registered. + */ + registerThirdPartySnippets(extensionId: string, snippets: SnippetMap) { + this.thirdPartySnippets[extensionId] = snippets; + this.mergeSnippets(); + } + + /** + * Merge core, third-party, and user snippets, with precedence user > third + * party > core. + */ + private mergeSnippets() { + this.mergedSnippets = {}; + + // We make a list of all entries from all sources, in order of increasing + // precedence: user > third party > core. + const entries = [ + ...Object.entries(cloneDeep(this.coreSnippets)), + ...Object.values(this.thirdPartySnippets).flatMap((snippets) => + Object.entries(cloneDeep(snippets)) + ), + ...Object.entries(cloneDeep(this.userSnippets)), + ]; + + entries.forEach(([key, value]) => { + if (this.mergedSnippets.hasOwnProperty(key)) { + const { definitions, ...rest } = value; + const mergedSnippet = this.mergedSnippets[key]; + + // NB: We make sure that the new definitions appear before the previous + // ones so that they take precedence + mergedSnippet.definitions = definitions.concat( + ...mergedSnippet.definitions + ); + + merge(mergedSnippet, rest); + } else { + this.mergedSnippets[key] = value; + } + }); + } + + /** + * Looks in merged collection of snippets for a snippet with key + * `snippetName` + * @param snippetName The name of the snippet to look up + * @returns The named snippet, or undefined if not found + */ + getSnippet(snippetName: string): Snippet | undefined { + return this.mergedSnippets[snippetName]; + } +} diff --git a/src/core/inferFullTargets.ts b/src/core/inferFullTargets.ts index 30bb24821f..3a113f172a 100644 --- a/src/core/inferFullTargets.ts +++ b/src/core/inferFullTargets.ts @@ -115,7 +115,9 @@ function inferPrimitiveTarget( "contents"; const selectionType = - maybeSelectionType ?? actionPreferences.selectionType ?? "token"; + maybeSelectionType ?? + (target.modifier == null ? actionPreferences.selectionType : null) ?? + "token"; const insideOutsideType = target.insideOutsideType ?? @@ -123,7 +125,8 @@ function inferPrimitiveTarget( actionPreferences.insideOutsideType; const modifier = target.modifier ?? - getPreviousAttribute(previousTargetsForAttributes, "modifier") ?? { + getPreviousAttribute(previousTargetsForAttributes, "modifier") ?? + (target.selectionType == null ? actionPreferences.modifier : null) ?? { type: "identity", }; diff --git a/src/extension.ts b/src/extension.ts index 8cf689afc2..a7965b94d2 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -2,23 +2,24 @@ import * as vscode from "vscode"; import { addDecorationsToEditors } from "./util/addDecorationsToEditor"; import { DEBOUNCE_DELAY } from "./core/constants"; import Decorations from "./core/Decorations"; -import graphConstructors from "./util/graphConstructors"; +import graphFactories from "./util/graphFactories"; import inferFullTargets from "./core/inferFullTargets"; import processTargets from "./processTargets"; import FontMeasurements from "./core/FontMeasurements"; import { ActionType, + Graph, PartialTarget, ProcessedTargetsContext, } from "./typings/Types"; -import makeGraph from "./util/makeGraph"; +import makeGraph, { FactoryMap } from "./util/makeGraph"; import { logBranchTypes } from "./util/debug"; import { TestCase } from "./testUtil/TestCase"; import { ThatMark } from "./core/ThatMark"; import { TestCaseRecorder } from "./testUtil/TestCaseRecorder"; import { getParseTreeApi } from "./util/getExtensionApi"; -import { canonicalizeAndValidateCommand } from "./canonicalizeAndValidateCommand"; -import canonicalizeActionName from "./canonicalizeActionName"; +import { canonicalizeAndValidateCommand } from "./util/canonicalizeAndValidateCommand"; +import canonicalizeActionName from "./util/canonicalizeActionName"; export async function activate(context: vscode.ExtensionContext) { const fontMeasurements = new FontMeasurements(context); @@ -76,7 +77,11 @@ export async function activate(context: vscode.ExtensionContext) { } ); - const graph = makeGraph(graphConstructors); + const graph = makeGraph({ + ...graphFactories, + extensionContext: () => context, + } as FactoryMap); + graph.snippets.init(); const thatMark = new ThatMark(); const sourceMark = new ThatMark(); const testCaseRecorder = new TestCaseRecorder(context); @@ -128,7 +133,7 @@ export async function activate(context: vscode.ExtensionContext) { const targets = inferFullTargets( partialTargets, - action.targetPreferences + action.getTargetPreferences(...extraArgs) ); const processedTargetsContext: ProcessedTargetsContext = { @@ -280,6 +285,9 @@ export async function activate(context: vscode.ExtensionContext) { thatMark, sourceMark, addDecorations, + experimental: { + registerThirdPartySnippets: graph.snippets.registerThirdPartySnippets, + }, }; } diff --git a/src/scripts/transformRecordedTests.ts b/src/scripts/transformRecordedTests.ts index 2f82f7ca8f..7c87f1cac8 100644 --- a/src/scripts/transformRecordedTests.ts +++ b/src/scripts/transformRecordedTests.ts @@ -7,7 +7,7 @@ import { TestCaseFixture } from "../testUtil/TestCase"; import { walkFilesSync } from "../testUtil/walkSync"; import serialize from "../testUtil/serialize"; -import canonicalizeActionName from "../canonicalizeActionName"; +import canonicalizeActionName from "../util/canonicalizeActionName"; /** * The transformation to run on all recorded test fixtures. Change this diff --git a/src/test/suite/fixtures/recorded/inference/bringOddToLine.yml b/src/test/suite/fixtures/recorded/inference/bringOddToLine.yml index e6561cfad0..5e02da4c70 100644 --- a/src/test/suite/fixtures/recorded/inference/bringOddToLine.yml +++ b/src/test/suite/fixtures/recorded/inference/bringOddToLine.yml @@ -4,51 +4,33 @@ command: actionName: replaceWithTarget partialTargets: - type: primitive - mark: { type: decoratedSymbol, symbolColor: default, character: o } - - { type: primitive, selectionType: line } + mark: {type: decoratedSymbol, symbolColor: default, character: o} + - {type: primitive, selectionType: line} extraArgs: [] marks: default.o: - start: { line: 0, character: 0 } - end: { line: 0, character: 5 } + start: {line: 0, character: 0} + end: {line: 0, character: 5} initialState: documentContents: |- const foo = "hello"; const bar = "hello"; selections: - - anchor: { line: 2, character: 18 } - active: { line: 2, character: 18 } + - anchor: {line: 2, character: 18} + active: {line: 2, character: 18} finalState: documentContents: |- const foo = "hello"; const selections: - - anchor: { line: 2, character: 5 } - active: { line: 2, character: 5 } + - anchor: {line: 2, character: 5} + active: {line: 2, character: 5} thatMark: - - anchor: { line: 2, character: 0 } - active: { line: 2, character: 5 } + - anchor: {line: 2, character: 0} + active: {line: 2, character: 5} sourceMark: - - anchor: { line: 0, character: 0 } - active: { line: 0, character: 5 } -fullTargets: - [ - { - type: primitive, - mark: { type: decoratedSymbol, symbolColor: default, character: o }, - selectionType: token, - position: contents, - insideOutsideType: null, - modifier: { type: identity }, - }, - { - type: primitive, - mark: { type: cursor }, - selectionType: line, - position: contents, - insideOutsideType: null, - modifier: { type: identity }, - }, - ] + - anchor: {line: 0, character: 0} + active: {line: 0, character: 5} +fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: o}, selectionType: token, position: contents, insideOutsideType: null, modifier: {type: identity}}, {type: primitive, mark: {type: cursor}, selectionType: line, position: contents, insideOutsideType: null, modifier: {type: identity}}] diff --git a/src/test/suite/fixtures/recorded/inference/bringOddToState.yml b/src/test/suite/fixtures/recorded/inference/bringOddToState.yml index ebbd5b66e3..48b8b02800 100644 --- a/src/test/suite/fixtures/recorded/inference/bringOddToState.yml +++ b/src/test/suite/fixtures/recorded/inference/bringOddToState.yml @@ -4,54 +4,34 @@ command: actionName: replaceWithTarget partialTargets: - type: primitive - mark: { type: decoratedSymbol, symbolColor: default, character: o } + mark: {type: decoratedSymbol, symbolColor: default, character: o} - type: primitive - modifier: - { type: containingScope, scopeType: statement, includeSiblings: false } + modifier: {type: containingScope, scopeType: statement, includeSiblings: false} extraArgs: [] marks: default.o: - start: { line: 0, character: 0 } - end: { line: 0, character: 5 } + start: {line: 0, character: 0} + end: {line: 0, character: 5} initialState: documentContents: |- const foo = "hello"; const bar = "hello"; selections: - - anchor: { line: 2, character: 18 } - active: { line: 2, character: 18 } + - anchor: {line: 2, character: 18} + active: {line: 2, character: 18} finalState: documentContents: |- const foo = "hello"; const selections: - - anchor: { line: 2, character: 5 } - active: { line: 2, character: 5 } + - anchor: {line: 2, character: 5} + active: {line: 2, character: 5} thatMark: - - anchor: { line: 2, character: 0 } - active: { line: 2, character: 5 } + - anchor: {line: 2, character: 0} + active: {line: 2, character: 5} sourceMark: - - anchor: { line: 0, character: 0 } - active: { line: 0, character: 5 } -fullTargets: - [ - { - type: primitive, - mark: { type: decoratedSymbol, symbolColor: default, character: o }, - selectionType: token, - position: contents, - insideOutsideType: null, - modifier: { type: identity }, - }, - { - type: primitive, - mark: { type: cursor }, - selectionType: token, - position: contents, - insideOutsideType: null, - modifier: - { type: containingScope, scopeType: statement, includeSiblings: false }, - }, - ] + - anchor: {line: 0, character: 0} + active: {line: 0, character: 5} +fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: o}, selectionType: token, position: contents, insideOutsideType: null, modifier: {type: identity}}, {type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: null, modifier: {type: containingScope, scopeType: statement, includeSiblings: false}}] diff --git a/src/test/suite/fixtures/recorded/inference/bringOddToToken.yml b/src/test/suite/fixtures/recorded/inference/bringOddToToken.yml index d832d75a0b..6d827359e5 100644 --- a/src/test/suite/fixtures/recorded/inference/bringOddToToken.yml +++ b/src/test/suite/fixtures/recorded/inference/bringOddToToken.yml @@ -4,51 +4,33 @@ command: actionName: replaceWithTarget partialTargets: - type: primitive - mark: { type: decoratedSymbol, symbolColor: default, character: o } - - { type: primitive, selectionType: token } + mark: {type: decoratedSymbol, symbolColor: default, character: o} + - {type: primitive, selectionType: token} extraArgs: [] marks: default.o: - start: { line: 0, character: 0 } - end: { line: 0, character: 5 } + start: {line: 0, character: 0} + end: {line: 0, character: 5} initialState: documentContents: |- const foo = "hello"; const bar = "hello"; selections: - - anchor: { line: 2, character: 18 } - active: { line: 2, character: 18 } + - anchor: {line: 2, character: 18} + active: {line: 2, character: 18} finalState: documentContents: |- const foo = "hello"; const bar = "const"; selections: - - anchor: { line: 2, character: 18 } - active: { line: 2, character: 18 } + - anchor: {line: 2, character: 18} + active: {line: 2, character: 18} thatMark: - - anchor: { line: 2, character: 13 } - active: { line: 2, character: 18 } + - anchor: {line: 2, character: 13} + active: {line: 2, character: 18} sourceMark: - - anchor: { line: 0, character: 0 } - active: { line: 0, character: 5 } -fullTargets: - [ - { - type: primitive, - mark: { type: decoratedSymbol, symbolColor: default, character: o }, - selectionType: token, - position: contents, - insideOutsideType: null, - modifier: { type: identity }, - }, - { - type: primitive, - mark: { type: cursorToken }, - selectionType: token, - position: contents, - insideOutsideType: null, - modifier: { type: identity }, - }, - ] + - anchor: {line: 0, character: 0} + active: {line: 0, character: 5} +fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: o}, selectionType: token, position: contents, insideOutsideType: null, modifier: {type: identity}}, {type: primitive, mark: {type: cursorToken}, selectionType: token, position: contents, insideOutsideType: null, modifier: {type: identity}}] diff --git a/src/test/suite/fixtures/recorded/inference/ifWrapTokenFine.yml b/src/test/suite/fixtures/recorded/inference/ifWrapTokenFine.yml new file mode 100644 index 0000000000..59c491081b --- /dev/null +++ b/src/test/suite/fixtures/recorded/inference/ifWrapTokenFine.yml @@ -0,0 +1,31 @@ +spokenForm: if wrap token fine +languageId: typescript +command: + actionName: wrapWithSnippet + partialTargets: + - type: primitive + selectionType: token + mark: {type: decoratedSymbol, symbolColor: default, character: f} + extraArgs: [ifStatement.consequence] +marks: + default.f: + start: {line: 0, character: 6} + end: {line: 0, character: 9} +initialState: + documentContents: | + const foo = "hello"; + selections: + - anchor: {line: 1, character: 0} + active: {line: 1, character: 0} +finalState: + documentContents: | + const if () { + foo + } = "hello"; + selections: + - anchor: {line: 0, character: 10} + active: {line: 0, character: 10} + thatMark: + - anchor: {line: 0, character: 6} + active: {line: 2, character: 1} +fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: f}, selectionType: token, position: contents, insideOutsideType: null, modifier: {type: identity}}] diff --git a/src/test/suite/fixtures/recorded/inference/takeOddPastEndOfState.yml b/src/test/suite/fixtures/recorded/inference/takeOddPastEndOfState.yml index 7c381cbc4e..f0387c468d 100644 --- a/src/test/suite/fixtures/recorded/inference/takeOddPastEndOfState.yml +++ b/src/test/suite/fixtures/recorded/inference/takeOddPastEndOfState.yml @@ -6,71 +6,36 @@ command: - type: range start: type: primitive - mark: { type: decoratedSymbol, symbolColor: default, character: o } + mark: {type: decoratedSymbol, symbolColor: default, character: o} end: type: primitive position: after insideOutsideType: inside - modifier: - { - type: containingScope, - scopeType: statement, - includeSiblings: false, - } + modifier: {type: containingScope, scopeType: statement, includeSiblings: false} excludeStart: false excludeEnd: false extraArgs: [] marks: default.o: - start: { line: 0, character: 0 } - end: { line: 0, character: 5 } + start: {line: 0, character: 0} + end: {line: 0, character: 5} initialState: documentContents: |- const foo = "hello"; const bar = "hello"; selections: - - anchor: { line: 2, character: 18 } - active: { line: 2, character: 18 } + - anchor: {line: 2, character: 18} + active: {line: 2, character: 18} finalState: documentContents: |- const foo = "hello"; const bar = "hello"; selections: - - anchor: { line: 0, character: 0 } - active: { line: 0, character: 20 } + - anchor: {line: 0, character: 0} + active: {line: 0, character: 20} thatMark: - - anchor: { line: 0, character: 0 } - active: { line: 0, character: 20 } -fullTargets: - [ - { - type: range, - excludeAnchor: false, - excludeActive: false, - anchor: - { - type: primitive, - mark: { type: decoratedSymbol, symbolColor: default, character: o }, - selectionType: token, - position: contents, - insideOutsideType: inside, - modifier: { type: identity }, - }, - active: - { - type: primitive, - mark: { type: decoratedSymbol, symbolColor: default, character: o }, - selectionType: token, - position: after, - insideOutsideType: inside, - modifier: - { - type: containingScope, - scopeType: statement, - includeSiblings: false, - }, - }, - }, - ] + - anchor: {line: 0, character: 0} + active: {line: 0, character: 20} +fullTargets: [{type: range, excludeAnchor: false, excludeActive: false, anchor: {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: o}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: identity}}, active: {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: o}, selectionType: token, position: after, insideOutsideType: inside, modifier: {type: containingScope, scopeType: statement, includeSiblings: false}}}] diff --git a/src/test/suite/fixtures/recorded/inference/takeOddPastLine.yml b/src/test/suite/fixtures/recorded/inference/takeOddPastLine.yml index e32c69a870..ea955fcf7d 100644 --- a/src/test/suite/fixtures/recorded/inference/takeOddPastLine.yml +++ b/src/test/suite/fixtures/recorded/inference/takeOddPastLine.yml @@ -6,57 +6,32 @@ command: - type: range start: type: primitive - mark: { type: decoratedSymbol, symbolColor: default, character: o } - end: { type: primitive, selectionType: line } + mark: {type: decoratedSymbol, symbolColor: default, character: o} + end: {type: primitive, selectionType: line} excludeStart: false excludeEnd: false extraArgs: [] marks: default.o: - start: { line: 0, character: 0 } - end: { line: 0, character: 5 } + start: {line: 0, character: 0} + end: {line: 0, character: 5} initialState: documentContents: |- const foo = "hello"; const bar = "hello"; selections: - - anchor: { line: 2, character: 18 } - active: { line: 2, character: 18 } + - anchor: {line: 2, character: 18} + active: {line: 2, character: 18} finalState: documentContents: |- const foo = "hello"; const bar = "hello"; selections: - - anchor: { line: 0, character: 0 } - active: { line: 2, character: 20 } + - anchor: {line: 0, character: 0} + active: {line: 2, character: 20} thatMark: - - anchor: { line: 0, character: 0 } - active: { line: 2, character: 20 } -fullTargets: - [ - { - type: range, - excludeAnchor: false, - excludeActive: false, - anchor: - { - type: primitive, - mark: { type: decoratedSymbol, symbolColor: default, character: o }, - selectionType: token, - position: contents, - insideOutsideType: inside, - modifier: { type: identity }, - }, - active: - { - type: primitive, - mark: { type: cursor }, - selectionType: line, - position: contents, - insideOutsideType: inside, - modifier: { type: identity }, - }, - }, - ] + - anchor: {line: 0, character: 0} + active: {line: 2, character: 20} +fullTargets: [{type: range, excludeAnchor: false, excludeActive: false, anchor: {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: o}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: identity}}, active: {type: primitive, mark: {type: cursor}, selectionType: line, position: contents, insideOutsideType: inside, modifier: {type: identity}}}] diff --git a/src/test/suite/fixtures/recorded/inference/takeOddPastState.yml b/src/test/suite/fixtures/recorded/inference/takeOddPastState.yml index 7d34dbaf64..0680bddeae 100644 --- a/src/test/suite/fixtures/recorded/inference/takeOddPastState.yml +++ b/src/test/suite/fixtures/recorded/inference/takeOddPastState.yml @@ -6,69 +6,34 @@ command: - type: range start: type: primitive - mark: { type: decoratedSymbol, symbolColor: default, character: o } + mark: {type: decoratedSymbol, symbolColor: default, character: o} end: type: primitive - modifier: - { - type: containingScope, - scopeType: statement, - includeSiblings: false, - } + modifier: {type: containingScope, scopeType: statement, includeSiblings: false} excludeStart: false excludeEnd: false extraArgs: [] marks: default.o: - start: { line: 0, character: 0 } - end: { line: 0, character: 5 } + start: {line: 0, character: 0} + end: {line: 0, character: 5} initialState: documentContents: |- const foo = "hello"; const bar = "hello"; selections: - - anchor: { line: 2, character: 18 } - active: { line: 2, character: 18 } + - anchor: {line: 2, character: 18} + active: {line: 2, character: 18} finalState: documentContents: |- const foo = "hello"; const bar = "hello"; selections: - - anchor: { line: 0, character: 0 } - active: { line: 2, character: 20 } + - anchor: {line: 0, character: 0} + active: {line: 2, character: 20} thatMark: - - anchor: { line: 0, character: 0 } - active: { line: 2, character: 20 } -fullTargets: - [ - { - type: range, - excludeAnchor: false, - excludeActive: false, - anchor: - { - type: primitive, - mark: { type: decoratedSymbol, symbolColor: default, character: o }, - selectionType: token, - position: contents, - insideOutsideType: inside, - modifier: { type: identity }, - }, - active: - { - type: primitive, - mark: { type: cursor }, - selectionType: token, - position: contents, - insideOutsideType: inside, - modifier: - { - type: containingScope, - scopeType: statement, - includeSiblings: false, - }, - }, - }, - ] + - anchor: {line: 0, character: 0} + active: {line: 2, character: 20} +fullTargets: [{type: range, excludeAnchor: false, excludeActive: false, anchor: {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: o}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: identity}}, active: {type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: containingScope, scopeType: statement, includeSiblings: false}}}] diff --git a/src/test/suite/fixtures/recorded/inference/takeOddPastToken.yml b/src/test/suite/fixtures/recorded/inference/takeOddPastToken.yml index 88ab411deb..717de5f597 100644 --- a/src/test/suite/fixtures/recorded/inference/takeOddPastToken.yml +++ b/src/test/suite/fixtures/recorded/inference/takeOddPastToken.yml @@ -6,57 +6,32 @@ command: - type: range start: type: primitive - mark: { type: decoratedSymbol, symbolColor: default, character: o } - end: { type: primitive, selectionType: token } + mark: {type: decoratedSymbol, symbolColor: default, character: o} + end: {type: primitive, selectionType: token} excludeStart: false excludeEnd: false extraArgs: [] marks: default.o: - start: { line: 0, character: 0 } - end: { line: 0, character: 5 } + start: {line: 0, character: 0} + end: {line: 0, character: 5} initialState: documentContents: |- const foo = "hello"; const bar = "hello"; selections: - - anchor: { line: 2, character: 18 } - active: { line: 2, character: 18 } + - anchor: {line: 2, character: 18} + active: {line: 2, character: 18} finalState: documentContents: |- const foo = "hello"; const bar = "hello"; selections: - - anchor: { line: 0, character: 0 } - active: { line: 2, character: 18 } + - anchor: {line: 0, character: 0} + active: {line: 2, character: 18} thatMark: - - anchor: { line: 0, character: 0 } - active: { line: 2, character: 18 } -fullTargets: - [ - { - type: range, - excludeAnchor: false, - excludeActive: false, - anchor: - { - type: primitive, - mark: { type: decoratedSymbol, symbolColor: default, character: o }, - selectionType: token, - position: contents, - insideOutsideType: inside, - modifier: { type: identity }, - }, - active: - { - type: primitive, - mark: { type: cursorToken }, - selectionType: token, - position: contents, - insideOutsideType: inside, - modifier: { type: identity }, - }, - }, - ] + - anchor: {line: 0, character: 0} + active: {line: 2, character: 18} +fullTargets: [{type: range, excludeAnchor: false, excludeActive: false, anchor: {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: o}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: identity}}, active: {type: primitive, mark: {type: cursorToken}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: identity}}}] diff --git a/src/test/suite/fixtures/recorded/languages/cpp/elseStateWrapThis.yml b/src/test/suite/fixtures/recorded/languages/cpp/elseStateWrapThis.yml new file mode 100644 index 0000000000..67f03b5748 --- /dev/null +++ b/src/test/suite/fixtures/recorded/languages/cpp/elseStateWrapThis.yml @@ -0,0 +1,28 @@ +spokenForm: else state wrap this +languageId: cpp +command: + actionName: wrapWithSnippet + partialTargets: + - type: primitive + mark: {type: cursor} + extraArgs: [ifElseStatement.alternative] +marks: {} +initialState: + documentContents: int foo = 0; + selections: + - anchor: {line: 0, character: 6} + active: {line: 0, character: 6} +finalState: + documentContents: |- + if () { + + } else { + int foo = 0; + } + selections: + - anchor: {line: 0, character: 4} + active: {line: 0, character: 4} + thatMark: + - anchor: {line: 0, character: 0} + active: {line: 4, character: 1} +fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: containingScope, scopeType: statement, includeSiblings: false}}] diff --git a/src/test/suite/fixtures/recorded/languages/cpp/ifElseWrapThis.yml b/src/test/suite/fixtures/recorded/languages/cpp/ifElseWrapThis.yml new file mode 100644 index 0000000000..09f561f114 --- /dev/null +++ b/src/test/suite/fixtures/recorded/languages/cpp/ifElseWrapThis.yml @@ -0,0 +1,28 @@ +spokenForm: if else wrap this +languageId: cpp +command: + actionName: wrapWithSnippet + partialTargets: + - type: primitive + mark: {type: cursor} + extraArgs: [ifElseStatement.consequence] +marks: {} +initialState: + documentContents: int foo = 0; + selections: + - anchor: {line: 0, character: 6} + active: {line: 0, character: 6} +finalState: + documentContents: |- + if () { + int foo = 0; + } else { + + } + selections: + - anchor: {line: 0, character: 4} + active: {line: 0, character: 4} + thatMark: + - anchor: {line: 0, character: 0} + active: {line: 4, character: 1} +fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: containingScope, scopeType: statement, includeSiblings: false}}] diff --git a/src/test/suite/fixtures/recorded/languages/cpp/ifStateWrapThis.yml b/src/test/suite/fixtures/recorded/languages/cpp/ifStateWrapThis.yml new file mode 100644 index 0000000000..9ed0d478c3 --- /dev/null +++ b/src/test/suite/fixtures/recorded/languages/cpp/ifStateWrapThis.yml @@ -0,0 +1,26 @@ +spokenForm: if state wrap this +languageId: cpp +command: + actionName: wrapWithSnippet + partialTargets: + - type: primitive + mark: {type: cursor} + extraArgs: [ifStatement.consequence] +marks: {} +initialState: + documentContents: int foo = 0; + selections: + - anchor: {line: 0, character: 6} + active: {line: 0, character: 6} +finalState: + documentContents: |- + if () { + int foo = 0; + } + selections: + - anchor: {line: 0, character: 4} + active: {line: 0, character: 4} + thatMark: + - anchor: {line: 0, character: 0} + active: {line: 2, character: 1} +fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: containingScope, scopeType: statement, includeSiblings: false}}] diff --git a/src/test/suite/fixtures/recorded/languages/cpp/tryCatchWrapThis.yml b/src/test/suite/fixtures/recorded/languages/cpp/tryCatchWrapThis.yml new file mode 100644 index 0000000000..9ca7b8195c --- /dev/null +++ b/src/test/suite/fixtures/recorded/languages/cpp/tryCatchWrapThis.yml @@ -0,0 +1,28 @@ +spokenForm: try catch wrap this +languageId: cpp +command: + actionName: wrapWithSnippet + partialTargets: + - type: primitive + mark: {type: cursor} + extraArgs: [tryCatchStatement.body] +marks: {} +initialState: + documentContents: int foo = 0; + selections: + - anchor: {line: 0, character: 6} + active: {line: 0, character: 6} +finalState: + documentContents: |- + try { + int foo = 0; + } catch () { + + } + selections: + - anchor: {line: 2, character: 9} + active: {line: 2, character: 9} + thatMark: + - anchor: {line: 0, character: 0} + active: {line: 4, character: 1} +fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: containingScope, scopeType: statement, includeSiblings: false}}] diff --git a/src/test/suite/fixtures/recorded/languages/cpp/tryCatchWrapThis2.yml b/src/test/suite/fixtures/recorded/languages/cpp/tryCatchWrapThis2.yml new file mode 100644 index 0000000000..1454f71b44 --- /dev/null +++ b/src/test/suite/fixtures/recorded/languages/cpp/tryCatchWrapThis2.yml @@ -0,0 +1,47 @@ +spokenForm: try catch wrap this +languageId: cpp +command: + actionName: wrapWithSnippet + partialTargets: + - type: primitive + mark: {type: cursor} + extraArgs: [tryCatchStatement.body] +marks: {} +initialState: + documentContents: |- + if (true) { + int foo = 0; + } + + int bar = 1; + selections: + - anchor: {line: 4, character: 0} + active: {line: 4, character: 0} + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} +finalState: + documentContents: |- + try { + if (true) { + int foo = 0; + } + } catch () { + + } + + try { + int bar = 1; + } catch () { + + } + selections: + - anchor: {line: 10, character: 9} + active: {line: 10, character: 9} + - anchor: {line: 4, character: 9} + active: {line: 4, character: 9} + thatMark: + - anchor: {line: 8, character: 0} + active: {line: 12, character: 1} + - anchor: {line: 0, character: 0} + active: {line: 6, character: 1} +fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: containingScope, scopeType: statement, includeSiblings: false}}] diff --git a/src/test/suite/fixtures/recorded/languages/csharp/elseStateWrapThis.yml b/src/test/suite/fixtures/recorded/languages/csharp/elseStateWrapThis.yml new file mode 100644 index 0000000000..8c0f9779d1 --- /dev/null +++ b/src/test/suite/fixtures/recorded/languages/csharp/elseStateWrapThis.yml @@ -0,0 +1,28 @@ +spokenForm: else state wrap this +languageId: csharp +command: + actionName: wrapWithSnippet + partialTargets: + - type: primitive + mark: {type: cursor} + extraArgs: [ifElseStatement.alternative] +marks: {} +initialState: + documentContents: int foo = 0; + selections: + - anchor: {line: 0, character: 6} + active: {line: 0, character: 6} +finalState: + documentContents: |- + if () { + + } else { + int foo = 0; + } + selections: + - anchor: {line: 0, character: 4} + active: {line: 0, character: 4} + thatMark: + - anchor: {line: 0, character: 0} + active: {line: 4, character: 1} +fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: containingScope, scopeType: statement, includeSiblings: false}}] diff --git a/src/test/suite/fixtures/recorded/languages/csharp/ifElseWrapThis.yml b/src/test/suite/fixtures/recorded/languages/csharp/ifElseWrapThis.yml new file mode 100644 index 0000000000..99efae39cd --- /dev/null +++ b/src/test/suite/fixtures/recorded/languages/csharp/ifElseWrapThis.yml @@ -0,0 +1,28 @@ +spokenForm: if else wrap this +languageId: csharp +command: + actionName: wrapWithSnippet + partialTargets: + - type: primitive + mark: {type: cursor} + extraArgs: [ifElseStatement.consequence] +marks: {} +initialState: + documentContents: int foo = 0; + selections: + - anchor: {line: 0, character: 6} + active: {line: 0, character: 6} +finalState: + documentContents: |- + if () { + int foo = 0; + } else { + + } + selections: + - anchor: {line: 0, character: 4} + active: {line: 0, character: 4} + thatMark: + - anchor: {line: 0, character: 0} + active: {line: 4, character: 1} +fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: containingScope, scopeType: statement, includeSiblings: false}}] diff --git a/src/test/suite/fixtures/recorded/languages/csharp/ifStateWrapThis.yml b/src/test/suite/fixtures/recorded/languages/csharp/ifStateWrapThis.yml new file mode 100644 index 0000000000..aa22745e98 --- /dev/null +++ b/src/test/suite/fixtures/recorded/languages/csharp/ifStateWrapThis.yml @@ -0,0 +1,26 @@ +spokenForm: if state wrap this +languageId: csharp +command: + actionName: wrapWithSnippet + partialTargets: + - type: primitive + mark: {type: cursor} + extraArgs: [ifStatement.consequence] +marks: {} +initialState: + documentContents: int foo = 0; + selections: + - anchor: {line: 0, character: 6} + active: {line: 0, character: 6} +finalState: + documentContents: |- + if () { + int foo = 0; + } + selections: + - anchor: {line: 0, character: 4} + active: {line: 0, character: 4} + thatMark: + - anchor: {line: 0, character: 0} + active: {line: 2, character: 1} +fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: containingScope, scopeType: statement, includeSiblings: false}}] diff --git a/src/test/suite/fixtures/recorded/languages/csharp/tryCatchWrapThis.yml b/src/test/suite/fixtures/recorded/languages/csharp/tryCatchWrapThis.yml new file mode 100644 index 0000000000..31f11c84c0 --- /dev/null +++ b/src/test/suite/fixtures/recorded/languages/csharp/tryCatchWrapThis.yml @@ -0,0 +1,28 @@ +spokenForm: try catch wrap this +languageId: csharp +command: + actionName: wrapWithSnippet + partialTargets: + - type: primitive + mark: {type: cursor} + extraArgs: [tryCatchStatement.body] +marks: {} +initialState: + documentContents: int foo = 0; + selections: + - anchor: {line: 0, character: 6} + active: {line: 0, character: 6} +finalState: + documentContents: |- + try { + int foo = 0; + } catch () { + + } + selections: + - anchor: {line: 2, character: 9} + active: {line: 2, character: 9} + thatMark: + - anchor: {line: 0, character: 0} + active: {line: 4, character: 1} +fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: containingScope, scopeType: statement, includeSiblings: false}}] diff --git a/src/test/suite/fixtures/recorded/languages/csharp/tryCatchWrapThis2.yml b/src/test/suite/fixtures/recorded/languages/csharp/tryCatchWrapThis2.yml new file mode 100644 index 0000000000..cc88d423e5 --- /dev/null +++ b/src/test/suite/fixtures/recorded/languages/csharp/tryCatchWrapThis2.yml @@ -0,0 +1,47 @@ +spokenForm: try catch wrap this +languageId: csharp +command: + actionName: wrapWithSnippet + partialTargets: + - type: primitive + mark: {type: cursor} + extraArgs: [tryCatchStatement.body] +marks: {} +initialState: + documentContents: |- + if (true) { + int foo = 0; + } + + int bar = 1; + selections: + - anchor: {line: 4, character: 0} + active: {line: 4, character: 0} + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} +finalState: + documentContents: |- + try { + if (true) { + int foo = 0; + } + } catch () { + + } + + try { + int bar = 1; + } catch () { + + } + selections: + - anchor: {line: 10, character: 9} + active: {line: 10, character: 9} + - anchor: {line: 4, character: 9} + active: {line: 4, character: 9} + thatMark: + - anchor: {line: 8, character: 0} + active: {line: 12, character: 1} + - anchor: {line: 0, character: 0} + active: {line: 6, character: 1} +fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: containingScope, scopeType: statement, includeSiblings: false}}] diff --git a/src/test/suite/fixtures/recorded/languages/java/elseStateWrapThis.yml b/src/test/suite/fixtures/recorded/languages/java/elseStateWrapThis.yml new file mode 100644 index 0000000000..69843ecff1 --- /dev/null +++ b/src/test/suite/fixtures/recorded/languages/java/elseStateWrapThis.yml @@ -0,0 +1,28 @@ +spokenForm: else state wrap this +languageId: java +command: + actionName: wrapWithSnippet + partialTargets: + - type: primitive + mark: {type: cursor} + extraArgs: [ifElseStatement.alternative] +marks: {} +initialState: + documentContents: int foo = 0; + selections: + - anchor: {line: 0, character: 6} + active: {line: 0, character: 6} +finalState: + documentContents: |- + if () { + + } else { + int foo = 0; + } + selections: + - anchor: {line: 0, character: 4} + active: {line: 0, character: 4} + thatMark: + - anchor: {line: 0, character: 0} + active: {line: 4, character: 1} +fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: containingScope, scopeType: statement, includeSiblings: false}}] diff --git a/src/test/suite/fixtures/recorded/languages/java/ifElseWrapThis.yml b/src/test/suite/fixtures/recorded/languages/java/ifElseWrapThis.yml new file mode 100644 index 0000000000..d43167eeb7 --- /dev/null +++ b/src/test/suite/fixtures/recorded/languages/java/ifElseWrapThis.yml @@ -0,0 +1,28 @@ +spokenForm: if else wrap this +languageId: java +command: + actionName: wrapWithSnippet + partialTargets: + - type: primitive + mark: {type: cursor} + extraArgs: [ifElseStatement.consequence] +marks: {} +initialState: + documentContents: int foo = 0; + selections: + - anchor: {line: 0, character: 6} + active: {line: 0, character: 6} +finalState: + documentContents: |- + if () { + int foo = 0; + } else { + + } + selections: + - anchor: {line: 0, character: 4} + active: {line: 0, character: 4} + thatMark: + - anchor: {line: 0, character: 0} + active: {line: 4, character: 1} +fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: containingScope, scopeType: statement, includeSiblings: false}}] diff --git a/src/test/suite/fixtures/recorded/languages/java/ifStateWrapThis.yml b/src/test/suite/fixtures/recorded/languages/java/ifStateWrapThis.yml new file mode 100644 index 0000000000..191f56cf68 --- /dev/null +++ b/src/test/suite/fixtures/recorded/languages/java/ifStateWrapThis.yml @@ -0,0 +1,26 @@ +spokenForm: if state wrap this +languageId: java +command: + actionName: wrapWithSnippet + partialTargets: + - type: primitive + mark: {type: cursor} + extraArgs: [ifStatement.consequence] +marks: {} +initialState: + documentContents: int foo = 0; + selections: + - anchor: {line: 0, character: 6} + active: {line: 0, character: 6} +finalState: + documentContents: |- + if () { + int foo = 0; + } + selections: + - anchor: {line: 0, character: 4} + active: {line: 0, character: 4} + thatMark: + - anchor: {line: 0, character: 0} + active: {line: 2, character: 1} +fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: containingScope, scopeType: statement, includeSiblings: false}}] diff --git a/src/test/suite/fixtures/recorded/languages/java/tryCatchWrapThis.yml b/src/test/suite/fixtures/recorded/languages/java/tryCatchWrapThis.yml new file mode 100644 index 0000000000..9b95e3f83d --- /dev/null +++ b/src/test/suite/fixtures/recorded/languages/java/tryCatchWrapThis.yml @@ -0,0 +1,28 @@ +spokenForm: try catch wrap this +languageId: java +command: + actionName: wrapWithSnippet + partialTargets: + - type: primitive + mark: {type: cursor} + extraArgs: [tryCatchStatement.body] +marks: {} +initialState: + documentContents: int foo = 0; + selections: + - anchor: {line: 0, character: 6} + active: {line: 0, character: 6} +finalState: + documentContents: |- + try { + int foo = 0; + } catch () { + + } + selections: + - anchor: {line: 2, character: 9} + active: {line: 2, character: 9} + thatMark: + - anchor: {line: 0, character: 0} + active: {line: 4, character: 1} +fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: containingScope, scopeType: statement, includeSiblings: false}}] diff --git a/src/test/suite/fixtures/recorded/languages/java/tryCatchWrapThis2.yml b/src/test/suite/fixtures/recorded/languages/java/tryCatchWrapThis2.yml new file mode 100644 index 0000000000..832fba247d --- /dev/null +++ b/src/test/suite/fixtures/recorded/languages/java/tryCatchWrapThis2.yml @@ -0,0 +1,47 @@ +spokenForm: try catch wrap this +languageId: java +command: + actionName: wrapWithSnippet + partialTargets: + - type: primitive + mark: {type: cursor} + extraArgs: [tryCatchStatement.body] +marks: {} +initialState: + documentContents: |- + if (true) { + int foo = 0; + } + + int bar = 1; + selections: + - anchor: {line: 4, character: 0} + active: {line: 4, character: 0} + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} +finalState: + documentContents: |- + try { + if (true) { + int foo = 0; + } + } catch () { + + } + + try { + int bar = 1; + } catch () { + + } + selections: + - anchor: {line: 10, character: 9} + active: {line: 10, character: 9} + - anchor: {line: 4, character: 9} + active: {line: 4, character: 9} + thatMark: + - anchor: {line: 8, character: 0} + active: {line: 12, character: 1} + - anchor: {line: 0, character: 0} + active: {line: 6, character: 1} +fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: containingScope, scopeType: statement, includeSiblings: false}}] diff --git a/src/test/suite/fixtures/recorded/languages/python/elseStateWrapThis.yml b/src/test/suite/fixtures/recorded/languages/python/elseStateWrapThis.yml new file mode 100644 index 0000000000..14cdf0ec83 --- /dev/null +++ b/src/test/suite/fixtures/recorded/languages/python/elseStateWrapThis.yml @@ -0,0 +1,27 @@ +spokenForm: else state wrap this +languageId: python +command: + actionName: wrapWithSnippet + partialTargets: + - type: primitive + mark: {type: cursor} + extraArgs: [ifElseStatement.alternative] +marks: {} +initialState: + documentContents: foo = "hello" + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} +finalState: + documentContents: |- + if : + + else: + foo = "hello" + selections: + - anchor: {line: 0, character: 3} + active: {line: 0, character: 3} + thatMark: + - anchor: {line: 0, character: 0} + active: {line: 3, character: 17} +fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: containingScope, scopeType: statement, includeSiblings: false}}] diff --git a/src/test/suite/fixtures/recorded/languages/python/ifElseWrapThis.yml b/src/test/suite/fixtures/recorded/languages/python/ifElseWrapThis.yml new file mode 100644 index 0000000000..8a63c3439d --- /dev/null +++ b/src/test/suite/fixtures/recorded/languages/python/ifElseWrapThis.yml @@ -0,0 +1,27 @@ +spokenForm: if else wrap this +languageId: python +command: + actionName: wrapWithSnippet + partialTargets: + - type: primitive + mark: {type: cursor} + extraArgs: [ifElseStatement.consequence] +marks: {} +initialState: + documentContents: foo = "hello" + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} +finalState: + documentContents: |- + if : + foo = "hello" + else: + + selections: + - anchor: {line: 0, character: 3} + active: {line: 0, character: 3} + thatMark: + - anchor: {line: 0, character: 0} + active: {line: 3, character: 4} +fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: containingScope, scopeType: statement, includeSiblings: false}}] diff --git a/src/test/suite/fixtures/recorded/languages/python/ifStateWrapThis.yml b/src/test/suite/fixtures/recorded/languages/python/ifStateWrapThis.yml new file mode 100644 index 0000000000..a2b66e465c --- /dev/null +++ b/src/test/suite/fixtures/recorded/languages/python/ifStateWrapThis.yml @@ -0,0 +1,25 @@ +spokenForm: if state wrap this +languageId: python +command: + actionName: wrapWithSnippet + partialTargets: + - type: primitive + mark: {type: cursor} + extraArgs: [ifStatement.consequence] +marks: {} +initialState: + documentContents: foo = "hello" + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} +finalState: + documentContents: |- + if : + foo = "hello" + selections: + - anchor: {line: 0, character: 3} + active: {line: 0, character: 3} + thatMark: + - anchor: {line: 0, character: 0} + active: {line: 1, character: 17} +fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: containingScope, scopeType: statement, includeSiblings: false}}] diff --git a/src/test/suite/fixtures/recorded/languages/python/tryCatchWrapThis.yml b/src/test/suite/fixtures/recorded/languages/python/tryCatchWrapThis.yml new file mode 100644 index 0000000000..788878ea3d --- /dev/null +++ b/src/test/suite/fixtures/recorded/languages/python/tryCatchWrapThis.yml @@ -0,0 +1,27 @@ +spokenForm: try catch wrap this +languageId: python +command: + actionName: wrapWithSnippet + partialTargets: + - type: primitive + mark: {type: cursor} + extraArgs: [tryCatchStatement.body] +marks: {} +initialState: + documentContents: foo = "hello" + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} +finalState: + documentContents: |- + try: + foo = "hello" + except : + + selections: + - anchor: {line: 2, character: 7} + active: {line: 2, character: 7} + thatMark: + - anchor: {line: 0, character: 0} + active: {line: 3, character: 4} +fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: containingScope, scopeType: statement, includeSiblings: false}}] diff --git a/src/test/suite/fixtures/recorded/languages/python/tryCatchWrapThis2.yml b/src/test/suite/fixtures/recorded/languages/python/tryCatchWrapThis2.yml new file mode 100644 index 0000000000..6fd5886ec2 --- /dev/null +++ b/src/test/suite/fixtures/recorded/languages/python/tryCatchWrapThis2.yml @@ -0,0 +1,43 @@ +spokenForm: try catch wrap this +languageId: python +command: + actionName: wrapWithSnippet + partialTargets: + - type: primitive + mark: {type: cursor} + extraArgs: [tryCatchStatement.body] +marks: {} +initialState: + documentContents: |- + if True: + foo = "hello" + + bar = "hello" + selections: + - anchor: {line: 3, character: 0} + active: {line: 3, character: 0} + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} +finalState: + documentContents: |- + try: + if True: + foo = "hello" + except : + + + try: + bar = "hello" + except : + + selections: + - anchor: {line: 8, character: 7} + active: {line: 8, character: 7} + - anchor: {line: 3, character: 7} + active: {line: 3, character: 7} + thatMark: + - anchor: {line: 6, character: 0} + active: {line: 9, character: 4} + - anchor: {line: 0, character: 0} + active: {line: 4, character: 4} +fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: containingScope, scopeType: statement, includeSiblings: false}}] diff --git a/src/test/suite/fixtures/recorded/languages/typescript/elseStateWrapThis.yml b/src/test/suite/fixtures/recorded/languages/typescript/elseStateWrapThis.yml new file mode 100644 index 0000000000..75def3f5b0 --- /dev/null +++ b/src/test/suite/fixtures/recorded/languages/typescript/elseStateWrapThis.yml @@ -0,0 +1,28 @@ +spokenForm: else state wrap this +languageId: typescript +command: + actionName: wrapWithSnippet + partialTargets: + - type: primitive + mark: {type: cursor} + extraArgs: [ifElseStatement.alternative] +marks: {} +initialState: + documentContents: const foo = "hello"; + selections: + - anchor: {line: 0, character: 6} + active: {line: 0, character: 6} +finalState: + documentContents: |- + if () { + + } else { + const foo = "hello"; + } + selections: + - anchor: {line: 0, character: 4} + active: {line: 0, character: 4} + thatMark: + - anchor: {line: 0, character: 0} + active: {line: 4, character: 1} +fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: containingScope, scopeType: statement, includeSiblings: false}}] diff --git a/src/test/suite/fixtures/recorded/languages/typescript/ifElseWrapThis.yml b/src/test/suite/fixtures/recorded/languages/typescript/ifElseWrapThis.yml new file mode 100644 index 0000000000..2b8fae5fe0 --- /dev/null +++ b/src/test/suite/fixtures/recorded/languages/typescript/ifElseWrapThis.yml @@ -0,0 +1,28 @@ +spokenForm: if else wrap this +languageId: typescript +command: + actionName: wrapWithSnippet + partialTargets: + - type: primitive + mark: {type: cursor} + extraArgs: [ifElseStatement.consequence] +marks: {} +initialState: + documentContents: const foo = "hello"; + selections: + - anchor: {line: 0, character: 6} + active: {line: 0, character: 6} +finalState: + documentContents: |- + if () { + const foo = "hello"; + } else { + + } + selections: + - anchor: {line: 0, character: 4} + active: {line: 0, character: 4} + thatMark: + - anchor: {line: 0, character: 0} + active: {line: 4, character: 1} +fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: containingScope, scopeType: statement, includeSiblings: false}}] diff --git a/src/test/suite/fixtures/recorded/languages/typescript/ifStateWrapThis.yml b/src/test/suite/fixtures/recorded/languages/typescript/ifStateWrapThis.yml new file mode 100644 index 0000000000..8f84ac19a5 --- /dev/null +++ b/src/test/suite/fixtures/recorded/languages/typescript/ifStateWrapThis.yml @@ -0,0 +1,26 @@ +spokenForm: if state wrap this +languageId: typescript +command: + actionName: wrapWithSnippet + partialTargets: + - type: primitive + mark: {type: cursor} + extraArgs: [ifStatement.consequence] +marks: {} +initialState: + documentContents: const foo = "hello"; + selections: + - anchor: {line: 0, character: 6} + active: {line: 0, character: 6} +finalState: + documentContents: |- + if () { + const foo = "hello"; + } + selections: + - anchor: {line: 0, character: 4} + active: {line: 0, character: 4} + thatMark: + - anchor: {line: 0, character: 0} + active: {line: 2, character: 1} +fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: containingScope, scopeType: statement, includeSiblings: false}}] diff --git a/src/test/suite/fixtures/recorded/languages/typescript/tryCatchWrapThis.yml b/src/test/suite/fixtures/recorded/languages/typescript/tryCatchWrapThis.yml new file mode 100644 index 0000000000..e74db810c6 --- /dev/null +++ b/src/test/suite/fixtures/recorded/languages/typescript/tryCatchWrapThis.yml @@ -0,0 +1,28 @@ +spokenForm: try catch wrap this +languageId: typescript +command: + actionName: wrapWithSnippet + partialTargets: + - type: primitive + mark: {type: cursor} + extraArgs: [tryCatchStatement.body] +marks: {} +initialState: + documentContents: const foo = "hello"; + selections: + - anchor: {line: 0, character: 6} + active: {line: 0, character: 6} +finalState: + documentContents: |- + try { + const foo = "hello"; + } catch () { + + } + selections: + - anchor: {line: 2, character: 9} + active: {line: 2, character: 9} + thatMark: + - anchor: {line: 0, character: 0} + active: {line: 4, character: 1} +fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: containingScope, scopeType: statement, includeSiblings: false}}] diff --git a/src/test/suite/fixtures/recorded/languages/typescript/tryCatchWrapThis2.yml b/src/test/suite/fixtures/recorded/languages/typescript/tryCatchWrapThis2.yml new file mode 100644 index 0000000000..51b89b697d --- /dev/null +++ b/src/test/suite/fixtures/recorded/languages/typescript/tryCatchWrapThis2.yml @@ -0,0 +1,47 @@ +spokenForm: try catch wrap this +languageId: typescript +command: + actionName: wrapWithSnippet + partialTargets: + - type: primitive + mark: {type: cursor} + extraArgs: [tryCatchStatement.body] +marks: {} +initialState: + documentContents: |- + if (true) { + const foo = "hello"; + } + + const bar = "hello"; + selections: + - anchor: {line: 4, character: 0} + active: {line: 4, character: 0} + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} +finalState: + documentContents: |- + try { + if (true) { + const foo = "hello"; + } + } catch () { + + } + + try { + const bar = "hello"; + } catch () { + + } + selections: + - anchor: {line: 10, character: 9} + active: {line: 10, character: 9} + - anchor: {line: 4, character: 9} + active: {line: 4, character: 9} + thatMark: + - anchor: {line: 8, character: 0} + active: {line: 12, character: 1} + - anchor: {line: 0, character: 0} + active: {line: 6, character: 1} +fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: containingScope, scopeType: statement, includeSiblings: false}}] diff --git a/src/test/suite/recorded.test.ts b/src/test/suite/recorded.test.ts index 9b7d9ddc13..1aba830eca 100644 --- a/src/test/suite/recorded.test.ts +++ b/src/test/suite/recorded.test.ts @@ -1,4 +1,5 @@ import * as assert from "assert"; +import serialize from "../../testUtil/serialize"; import { promises as fsp } from "fs"; import * as path from "path"; import * as yaml from "js-yaml"; @@ -59,6 +60,12 @@ async function runTest(file: string) { }); const editor = await vscode.window.showTextDocument(document); + if (!fixture.initialState.documentContents.includes("\n")) { + await editor.edit((editBuilder) => { + editBuilder.setEndOfLine(vscode.EndOfLine.LF); + }); + } + await parseTreeApi.loadLanguage(document.languageId); editor.selections = fixture.initialState.selections.map(createSelection); @@ -118,15 +125,20 @@ async function runTest(file: string) { excludeFields ); - assert.deepStrictEqual( - resultState, - fixture.finalState, - "Unexpected final state" - ); + if (process.env.CURSORLESS_TEST_UPDATE_FIXTURES === "true") { + const outputFixture = { ...fixture, finalState: resultState, returnValue }; + await fsp.writeFile(file, serialize(outputFixture)); + } else { + assert.deepStrictEqual( + resultState, + fixture.finalState, + "Unexpected final state" + ); - assert.deepStrictEqual( - returnValue, - fixture.returnValue, - "Unexpected return value" - ); + assert.deepStrictEqual( + returnValue, + fixture.returnValue, + "Unexpected return value" + ); + } } diff --git a/src/testUtil/walkAsync.ts b/src/testUtil/walkAsync.ts new file mode 100644 index 0000000000..ff80c4df48 --- /dev/null +++ b/src/testUtil/walkAsync.ts @@ -0,0 +1,23 @@ +import * as path from "path"; +import { readdir } from "fs/promises"; +import { flatten } from "lodash"; + +/** + * Note: Returns full paths + * Based on https://gist.github.com/kethinov/6658166#gistcomment-1941504 + * @param dir + * @param filelist + * @returns + */ +export const walkFiles = async (dir: string): Promise => { + const dirEntries = await readdir(dir, { withFileTypes: true }); + + return flatten( + await Promise.all( + dirEntries.map(async (dirent) => { + const filePath = path.join(dir, dirent.name); + return dirent.isDirectory() ? await walkFiles(filePath) : [filePath]; + }) + ) + ); +}; diff --git a/src/typings/Types.ts b/src/typings/Types.ts index c582d98f49..7715fb28ef 100644 --- a/src/typings/Types.ts +++ b/src/typings/Types.ts @@ -1,9 +1,10 @@ import { SyntaxNode } from "web-tree-sitter"; import * as vscode from "vscode"; -import { Location } from "vscode"; +import { ExtensionContext, Location, Selection } from "vscode"; import { HatStyleName } from "../core/constants"; import { EditStyles } from "../core/editStyles"; import NavigationMap from "../core/NavigationMap"; +import { Snippets } from "../core/Snippets"; /** * A token within a text editor, including the current display line of the token @@ -258,6 +259,7 @@ export interface ActionPreferences { position?: Position; insideOutsideType: InsideOutsideType; selectionType?: SelectionType; + modifier?: Modifier; } export interface SelectionWithContext { @@ -273,7 +275,12 @@ export interface ActionReturnValue { export interface Action { run(targets: TypedSelection[][], ...args: any[]): Promise; - targetPreferences: ActionPreferences[]; + + /** + * Used to define default values for parts of target during inference. + * @param args Extra args to command + */ + getTargetPreferences(...args: any[]): ActionPreferences[]; } export type ActionType = @@ -311,7 +318,8 @@ export type ActionType = | "toggleLineBreakpoint" | "toggleLineComment" | "unfoldRegion" - | "wrapWithPairedDelimiter"; + | "wrapWithPairedDelimiter" + | "wrapWithSnippet"; export type ActionRecord = Record; @@ -319,6 +327,8 @@ export interface Graph { readonly actions: ActionRecord; readonly editStyles: EditStyles; readonly navigationMap: NavigationMap; + readonly extensionContext: ExtensionContext; + readonly snippets: Snippets; } export type NodeMatcherValue = { diff --git a/src/typings/snippet.ts b/src/typings/snippet.ts new file mode 100644 index 0000000000..4eb7ef3c08 --- /dev/null +++ b/src/typings/snippet.ts @@ -0,0 +1,49 @@ +import { ScopeType } from "./Types"; + +export interface SnippetScope { + langIds?: string[]; + scopeType?: ScopeType; +} + +export type SnippetBody = string[]; + +export interface SnippetDefinition { + body: SnippetBody; + + /** + * Scopes where this snippet is active + */ + scope?: SnippetScope; +} + +export interface SnippetVariable { + /** + * Default to this scope type when wrapping a target without scope type + * specified. + */ + wrapperScopeType?: ScopeType; + + /** + * Description of the snippet variable + */ + description?: string; +} + +export interface Snippet { + /** + * List of possible definitions for this snippet + */ + definitions: SnippetDefinition[]; + + /** + * For each named variable in the snippet, provides extra information about the variable. + */ + variables?: Record; + + /** + * Description of the snippet + */ + description?: string; +} + +export type SnippetMap = Record; diff --git a/src/canonicalizeActionName.ts b/src/util/canonicalizeActionName.ts similarity index 95% rename from src/canonicalizeActionName.ts rename to src/util/canonicalizeActionName.ts index eff7ba1935..983ce9c332 100755 --- a/src/canonicalizeActionName.ts +++ b/src/util/canonicalizeActionName.ts @@ -1,4 +1,4 @@ -import { ActionType } from "./typings/Types"; +import { ActionType } from "../typings/Types"; const actionAliasToCanonicalName: Record = { bring: "replaceWithTarget", diff --git a/src/canonicalizeAndValidateCommand.ts b/src/util/canonicalizeAndValidateCommand.ts similarity index 89% rename from src/canonicalizeAndValidateCommand.ts rename to src/util/canonicalizeAndValidateCommand.ts index dd3f754c03..3f2b90741d 100644 --- a/src/canonicalizeAndValidateCommand.ts +++ b/src/util/canonicalizeAndValidateCommand.ts @@ -1,7 +1,7 @@ import canonicalizeActionName from "./canonicalizeActionName"; import canonicalizeTargets from "./canonicalizeTargets"; -import { ActionType, PartialTarget, SelectionType } from "./typings/Types"; -import { getPrimitiveTargets } from "./util/targetUtils"; +import { ActionType, PartialTarget, SelectionType } from "../typings/Types"; +import { getPrimitiveTargets } from "./targetUtils"; export function canonicalizeAndValidateCommand( inputActionName: string, diff --git a/src/canonicalizeTargets.ts b/src/util/canonicalizeTargets.ts similarity index 90% rename from src/canonicalizeTargets.ts rename to src/util/canonicalizeTargets.ts index 10d8d554fd..d7183cbf21 100755 --- a/src/canonicalizeTargets.ts +++ b/src/util/canonicalizeTargets.ts @@ -2,10 +2,10 @@ import { PartialPrimitiveTarget, PartialTarget, ScopeType, -} from "./typings/Types"; +} from "../typings/Types"; import update from "immutability-helper"; -import { transformPrimitiveTargets } from "./util/targetUtils"; -import { HatStyleName } from "./core/constants"; +import { transformPrimitiveTargets } from "./targetUtils"; +import { HatStyleName } from "../core/constants"; import { flow } from "lodash"; const SCOPE_TYPE_CANONICALIZATION_MAPPING: Record = { diff --git a/src/util/graphConstructors.ts b/src/util/graphConstructors.ts deleted file mode 100644 index 74d0948b67..0000000000 --- a/src/util/graphConstructors.ts +++ /dev/null @@ -1,13 +0,0 @@ -import Actions from "../actions"; -import { EditStyles } from "../core/editStyles"; -import { Graph } from "../typings/Types"; -import { ConstructorMap } from "./makeGraph"; -import NavigationMap from "../core/NavigationMap"; - -const graphConstructors: ConstructorMap = { - actions: Actions, - editStyles: EditStyles, - navigationMap: NavigationMap, -}; - -export default graphConstructors; diff --git a/src/util/graphFactories.ts b/src/util/graphFactories.ts new file mode 100644 index 0000000000..0781bb69f7 --- /dev/null +++ b/src/util/graphFactories.ts @@ -0,0 +1,15 @@ +import Actions from "../actions"; +import { EditStyles } from "../core/editStyles"; +import { Graph } from "../typings/Types"; +import { FactoryMap } from "./makeGraph"; +import NavigationMap from "../core/NavigationMap"; +import { Snippets } from "../core/Snippets"; + +const graphFactories: Partial> = { + actions: (graph: Graph) => new Actions(graph), + editStyles: () => new EditStyles(), + navigationMap: () => new NavigationMap(), + snippets: (graph: Graph) => new Snippets(graph), +}; + +export default graphFactories; diff --git a/src/util/makeGraph.ts b/src/util/makeGraph.ts index 01b1d400c5..202845b9aa 100644 --- a/src/util/makeGraph.ts +++ b/src/util/makeGraph.ts @@ -1,21 +1,19 @@ -export type ConstructorMap = { - [P in keyof T]: new (t: T) => T[P]; +export type FactoryMap = { + [P in keyof T]: (t: T) => T[P]; }; function makeGetter( graph: GraphType, components: Partial, - constructorMap: ConstructorMap, + factoryMap: FactoryMap, key: K ): () => GraphType[K] { return () => { var returnValue: GraphType[K]; if (components[key] == null) { - const constructor = constructorMap[key] as new ( - graph: GraphType - ) => GraphType[K]; - returnValue = new constructor(graph); + const factory = factoryMap[key] as (graph: GraphType) => GraphType[K]; + returnValue = factory(graph); components[key] = returnValue; } else { returnValue = components[key] as GraphType[K]; @@ -26,17 +24,17 @@ function makeGetter( } export default function makeGraph( - constructorMap: ConstructorMap + factoryMap: FactoryMap ) { const components: Partial = {}; const graph: Partial = {}; - Object.keys(constructorMap).forEach((key: keyof GraphType | PropertyKey) => { + Object.keys(factoryMap).forEach((key: keyof GraphType | PropertyKey) => { Object.defineProperty(graph, key, { get: makeGetter( graph as GraphType, components, - constructorMap, + factoryMap, key as keyof GraphType ), }); diff --git a/src/util/object.ts b/src/util/object.ts new file mode 100644 index 0000000000..4f0b507bd9 --- /dev/null +++ b/src/util/object.ts @@ -0,0 +1,22 @@ +/** + * Merges all objects passed in by key, raising an exception if any two objects share a key + * @param objects The objects to merge + * @returns The merged object + */ +export function mergeStrict( + ...objects: Record[] +): Record { + const returnValue: Record = {}; + + objects.forEach((object: object) => { + for (const [key, value] of Object.entries(object)) { + if (returnValue.hasOwnProperty(key)) { + throw new Error(`Found duplicate property ${key}`); + } + + returnValue[key] = value; + } + }); + + return returnValue; +} diff --git a/src/util/updateSelections.ts b/src/util/updateSelections.ts index 3e3d6e1531..14309d6066 100644 --- a/src/util/updateSelections.ts +++ b/src/util/updateSelections.ts @@ -148,7 +148,7 @@ class SelectionUpdater { * @returns The initial selections updated based upon what happened in the function */ export async function callFunctionAndUpdateSelections( - func: () => Thenable, + func: () => Thenable, editor: TextEditor, selectionMatrix: Selection[][] ): Promise { diff --git a/src/vendor/snippet/charCode.ts b/src/vendor/snippet/charCode.ts new file mode 100755 index 0000000000..0e69131699 --- /dev/null +++ b/src/vendor/snippet/charCode.ts @@ -0,0 +1,436 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See https://github.com/microsoft/vscode/blob/d31496c866683bdbccfc85bc11a3107d6c789b52/LICENSE.txt + *--------------------------------------------------------------------------------------------*/ +// From https://raw.githubusercontent.com/microsoft/vscode/d31496c866683bdbccfc85bc11a3107d6c789b52/src/vs/base/common/charCode.ts + +// Names from https://blog.codinghorror.com/ascii-pronunciation-rules-for-programmers/ + +/** + * An inlined enum containing useful character codes (to be used with String.charCodeAt). + * Please leave the const keyword such that it gets inlined when compiled to JavaScript! + */ +export const enum CharCode { + Null = 0, + /** + * The `\b` character. + */ + Backspace = 8, + /** + * The `\t` character. + */ + Tab = 9, + /** + * The `\n` character. + */ + LineFeed = 10, + /** + * The `\r` character. + */ + CarriageReturn = 13, + Space = 32, + /** + * The `!` character. + */ + ExclamationMark = 33, + /** + * The `"` character. + */ + DoubleQuote = 34, + /** + * The `#` character. + */ + Hash = 35, + /** + * The `$` character. + */ + DollarSign = 36, + /** + * The `%` character. + */ + PercentSign = 37, + /** + * The `&` character. + */ + Ampersand = 38, + /** + * The `'` character. + */ + SingleQuote = 39, + /** + * The `(` character. + */ + OpenParen = 40, + /** + * The `)` character. + */ + CloseParen = 41, + /** + * The `*` character. + */ + Asterisk = 42, + /** + * The `+` character. + */ + Plus = 43, + /** + * The `,` character. + */ + Comma = 44, + /** + * The `-` character. + */ + Dash = 45, + /** + * The `.` character. + */ + Period = 46, + /** + * The `/` character. + */ + Slash = 47, + + Digit0 = 48, + Digit1 = 49, + Digit2 = 50, + Digit3 = 51, + Digit4 = 52, + Digit5 = 53, + Digit6 = 54, + Digit7 = 55, + Digit8 = 56, + Digit9 = 57, + + /** + * The `:` character. + */ + Colon = 58, + /** + * The `;` character. + */ + Semicolon = 59, + /** + * The `<` character. + */ + LessThan = 60, + /** + * The `=` character. + */ + Equals = 61, + /** + * The `>` character. + */ + GreaterThan = 62, + /** + * The `?` character. + */ + QuestionMark = 63, + /** + * The `@` character. + */ + AtSign = 64, + + A = 65, + B = 66, + C = 67, + D = 68, + E = 69, + F = 70, + G = 71, + H = 72, + I = 73, + J = 74, + K = 75, + L = 76, + M = 77, + N = 78, + O = 79, + P = 80, + Q = 81, + R = 82, + S = 83, + T = 84, + U = 85, + V = 86, + W = 87, + X = 88, + Y = 89, + Z = 90, + + /** + * The `[` character. + */ + OpenSquareBracket = 91, + /** + * The `\` character. + */ + Backslash = 92, + /** + * The `]` character. + */ + CloseSquareBracket = 93, + /** + * The `^` character. + */ + Caret = 94, + /** + * The `_` character. + */ + Underline = 95, + /** + * The ``(`)`` character. + */ + BackTick = 96, + + a = 97, + b = 98, + c = 99, + d = 100, + e = 101, + f = 102, + g = 103, + h = 104, + i = 105, + j = 106, + k = 107, + l = 108, + m = 109, + n = 110, + o = 111, + p = 112, + q = 113, + r = 114, + s = 115, + t = 116, + u = 117, + v = 118, + w = 119, + x = 120, + y = 121, + z = 122, + + /** + * The `{` character. + */ + OpenCurlyBrace = 123, + /** + * The `|` character. + */ + Pipe = 124, + /** + * The `}` character. + */ + CloseCurlyBrace = 125, + /** + * The `~` character. + */ + Tilde = 126, + + U_Combining_Grave_Accent = 0x0300, // U+0300 Combining Grave Accent + U_Combining_Acute_Accent = 0x0301, // U+0301 Combining Acute Accent + U_Combining_Circumflex_Accent = 0x0302, // U+0302 Combining Circumflex Accent + U_Combining_Tilde = 0x0303, // U+0303 Combining Tilde + U_Combining_Macron = 0x0304, // U+0304 Combining Macron + U_Combining_Overline = 0x0305, // U+0305 Combining Overline + U_Combining_Breve = 0x0306, // U+0306 Combining Breve + U_Combining_Dot_Above = 0x0307, // U+0307 Combining Dot Above + U_Combining_Diaeresis = 0x0308, // U+0308 Combining Diaeresis + U_Combining_Hook_Above = 0x0309, // U+0309 Combining Hook Above + U_Combining_Ring_Above = 0x030A, // U+030A Combining Ring Above + U_Combining_Double_Acute_Accent = 0x030B, // U+030B Combining Double Acute Accent + U_Combining_Caron = 0x030C, // U+030C Combining Caron + U_Combining_Vertical_Line_Above = 0x030D, // U+030D Combining Vertical Line Above + U_Combining_Double_Vertical_Line_Above = 0x030E, // U+030E Combining Double Vertical Line Above + U_Combining_Double_Grave_Accent = 0x030F, // U+030F Combining Double Grave Accent + U_Combining_Candrabindu = 0x0310, // U+0310 Combining Candrabindu + U_Combining_Inverted_Breve = 0x0311, // U+0311 Combining Inverted Breve + U_Combining_Turned_Comma_Above = 0x0312, // U+0312 Combining Turned Comma Above + U_Combining_Comma_Above = 0x0313, // U+0313 Combining Comma Above + U_Combining_Reversed_Comma_Above = 0x0314, // U+0314 Combining Reversed Comma Above + U_Combining_Comma_Above_Right = 0x0315, // U+0315 Combining Comma Above Right + U_Combining_Grave_Accent_Below = 0x0316, // U+0316 Combining Grave Accent Below + U_Combining_Acute_Accent_Below = 0x0317, // U+0317 Combining Acute Accent Below + U_Combining_Left_Tack_Below = 0x0318, // U+0318 Combining Left Tack Below + U_Combining_Right_Tack_Below = 0x0319, // U+0319 Combining Right Tack Below + U_Combining_Left_Angle_Above = 0x031A, // U+031A Combining Left Angle Above + U_Combining_Horn = 0x031B, // U+031B Combining Horn + U_Combining_Left_Half_Ring_Below = 0x031C, // U+031C Combining Left Half Ring Below + U_Combining_Up_Tack_Below = 0x031D, // U+031D Combining Up Tack Below + U_Combining_Down_Tack_Below = 0x031E, // U+031E Combining Down Tack Below + U_Combining_Plus_Sign_Below = 0x031F, // U+031F Combining Plus Sign Below + U_Combining_Minus_Sign_Below = 0x0320, // U+0320 Combining Minus Sign Below + U_Combining_Palatalized_Hook_Below = 0x0321, // U+0321 Combining Palatalized Hook Below + U_Combining_Retroflex_Hook_Below = 0x0322, // U+0322 Combining Retroflex Hook Below + U_Combining_Dot_Below = 0x0323, // U+0323 Combining Dot Below + U_Combining_Diaeresis_Below = 0x0324, // U+0324 Combining Diaeresis Below + U_Combining_Ring_Below = 0x0325, // U+0325 Combining Ring Below + U_Combining_Comma_Below = 0x0326, // U+0326 Combining Comma Below + U_Combining_Cedilla = 0x0327, // U+0327 Combining Cedilla + U_Combining_Ogonek = 0x0328, // U+0328 Combining Ogonek + U_Combining_Vertical_Line_Below = 0x0329, // U+0329 Combining Vertical Line Below + U_Combining_Bridge_Below = 0x032A, // U+032A Combining Bridge Below + U_Combining_Inverted_Double_Arch_Below = 0x032B, // U+032B Combining Inverted Double Arch Below + U_Combining_Caron_Below = 0x032C, // U+032C Combining Caron Below + U_Combining_Circumflex_Accent_Below = 0x032D, // U+032D Combining Circumflex Accent Below + U_Combining_Breve_Below = 0x032E, // U+032E Combining Breve Below + U_Combining_Inverted_Breve_Below = 0x032F, // U+032F Combining Inverted Breve Below + U_Combining_Tilde_Below = 0x0330, // U+0330 Combining Tilde Below + U_Combining_Macron_Below = 0x0331, // U+0331 Combining Macron Below + U_Combining_Low_Line = 0x0332, // U+0332 Combining Low Line + U_Combining_Double_Low_Line = 0x0333, // U+0333 Combining Double Low Line + U_Combining_Tilde_Overlay = 0x0334, // U+0334 Combining Tilde Overlay + U_Combining_Short_Stroke_Overlay = 0x0335, // U+0335 Combining Short Stroke Overlay + U_Combining_Long_Stroke_Overlay = 0x0336, // U+0336 Combining Long Stroke Overlay + U_Combining_Short_Solidus_Overlay = 0x0337, // U+0337 Combining Short Solidus Overlay + U_Combining_Long_Solidus_Overlay = 0x0338, // U+0338 Combining Long Solidus Overlay + U_Combining_Right_Half_Ring_Below = 0x0339, // U+0339 Combining Right Half Ring Below + U_Combining_Inverted_Bridge_Below = 0x033A, // U+033A Combining Inverted Bridge Below + U_Combining_Square_Below = 0x033B, // U+033B Combining Square Below + U_Combining_Seagull_Below = 0x033C, // U+033C Combining Seagull Below + U_Combining_X_Above = 0x033D, // U+033D Combining X Above + U_Combining_Vertical_Tilde = 0x033E, // U+033E Combining Vertical Tilde + U_Combining_Double_Overline = 0x033F, // U+033F Combining Double Overline + U_Combining_Grave_Tone_Mark = 0x0340, // U+0340 Combining Grave Tone Mark + U_Combining_Acute_Tone_Mark = 0x0341, // U+0341 Combining Acute Tone Mark + U_Combining_Greek_Perispomeni = 0x0342, // U+0342 Combining Greek Perispomeni + U_Combining_Greek_Koronis = 0x0343, // U+0343 Combining Greek Koronis + U_Combining_Greek_Dialytika_Tonos = 0x0344, // U+0344 Combining Greek Dialytika Tonos + U_Combining_Greek_Ypogegrammeni = 0x0345, // U+0345 Combining Greek Ypogegrammeni + U_Combining_Bridge_Above = 0x0346, // U+0346 Combining Bridge Above + U_Combining_Equals_Sign_Below = 0x0347, // U+0347 Combining Equals Sign Below + U_Combining_Double_Vertical_Line_Below = 0x0348, // U+0348 Combining Double Vertical Line Below + U_Combining_Left_Angle_Below = 0x0349, // U+0349 Combining Left Angle Below + U_Combining_Not_Tilde_Above = 0x034A, // U+034A Combining Not Tilde Above + U_Combining_Homothetic_Above = 0x034B, // U+034B Combining Homothetic Above + U_Combining_Almost_Equal_To_Above = 0x034C, // U+034C Combining Almost Equal To Above + U_Combining_Left_Right_Arrow_Below = 0x034D, // U+034D Combining Left Right Arrow Below + U_Combining_Upwards_Arrow_Below = 0x034E, // U+034E Combining Upwards Arrow Below + U_Combining_Grapheme_Joiner = 0x034F, // U+034F Combining Grapheme Joiner + U_Combining_Right_Arrowhead_Above = 0x0350, // U+0350 Combining Right Arrowhead Above + U_Combining_Left_Half_Ring_Above = 0x0351, // U+0351 Combining Left Half Ring Above + U_Combining_Fermata = 0x0352, // U+0352 Combining Fermata + U_Combining_X_Below = 0x0353, // U+0353 Combining X Below + U_Combining_Left_Arrowhead_Below = 0x0354, // U+0354 Combining Left Arrowhead Below + U_Combining_Right_Arrowhead_Below = 0x0355, // U+0355 Combining Right Arrowhead Below + U_Combining_Right_Arrowhead_And_Up_Arrowhead_Below = 0x0356, // U+0356 Combining Right Arrowhead And Up Arrowhead Below + U_Combining_Right_Half_Ring_Above = 0x0357, // U+0357 Combining Right Half Ring Above + U_Combining_Dot_Above_Right = 0x0358, // U+0358 Combining Dot Above Right + U_Combining_Asterisk_Below = 0x0359, // U+0359 Combining Asterisk Below + U_Combining_Double_Ring_Below = 0x035A, // U+035A Combining Double Ring Below + U_Combining_Zigzag_Above = 0x035B, // U+035B Combining Zigzag Above + U_Combining_Double_Breve_Below = 0x035C, // U+035C Combining Double Breve Below + U_Combining_Double_Breve = 0x035D, // U+035D Combining Double Breve + U_Combining_Double_Macron = 0x035E, // U+035E Combining Double Macron + U_Combining_Double_Macron_Below = 0x035F, // U+035F Combining Double Macron Below + U_Combining_Double_Tilde = 0x0360, // U+0360 Combining Double Tilde + U_Combining_Double_Inverted_Breve = 0x0361, // U+0361 Combining Double Inverted Breve + U_Combining_Double_Rightwards_Arrow_Below = 0x0362, // U+0362 Combining Double Rightwards Arrow Below + U_Combining_Latin_Small_Letter_A = 0x0363, // U+0363 Combining Latin Small Letter A + U_Combining_Latin_Small_Letter_E = 0x0364, // U+0364 Combining Latin Small Letter E + U_Combining_Latin_Small_Letter_I = 0x0365, // U+0365 Combining Latin Small Letter I + U_Combining_Latin_Small_Letter_O = 0x0366, // U+0366 Combining Latin Small Letter O + U_Combining_Latin_Small_Letter_U = 0x0367, // U+0367 Combining Latin Small Letter U + U_Combining_Latin_Small_Letter_C = 0x0368, // U+0368 Combining Latin Small Letter C + U_Combining_Latin_Small_Letter_D = 0x0369, // U+0369 Combining Latin Small Letter D + U_Combining_Latin_Small_Letter_H = 0x036A, // U+036A Combining Latin Small Letter H + U_Combining_Latin_Small_Letter_M = 0x036B, // U+036B Combining Latin Small Letter M + U_Combining_Latin_Small_Letter_R = 0x036C, // U+036C Combining Latin Small Letter R + U_Combining_Latin_Small_Letter_T = 0x036D, // U+036D Combining Latin Small Letter T + U_Combining_Latin_Small_Letter_V = 0x036E, // U+036E Combining Latin Small Letter V + U_Combining_Latin_Small_Letter_X = 0x036F, // U+036F Combining Latin Small Letter X + + /** + * Unicode Character 'LINE SEPARATOR' (U+2028) + * http://www.fileformat.info/info/unicode/char/2028/index.htm + */ + LINE_SEPARATOR = 0x2028, + /** + * Unicode Character 'PARAGRAPH SEPARATOR' (U+2029) + * http://www.fileformat.info/info/unicode/char/2029/index.htm + */ + PARAGRAPH_SEPARATOR = 0x2029, + /** + * Unicode Character 'NEXT LINE' (U+0085) + * http://www.fileformat.info/info/unicode/char/0085/index.htm + */ + NEXT_LINE = 0x0085, + + // http://www.fileformat.info/info/unicode/category/Sk/list.htm + U_CIRCUMFLEX = 0x005E, // U+005E CIRCUMFLEX + U_GRAVE_ACCENT = 0x0060, // U+0060 GRAVE ACCENT + U_DIAERESIS = 0x00A8, // U+00A8 DIAERESIS + U_MACRON = 0x00AF, // U+00AF MACRON + U_ACUTE_ACCENT = 0x00B4, // U+00B4 ACUTE ACCENT + U_CEDILLA = 0x00B8, // U+00B8 CEDILLA + U_MODIFIER_LETTER_LEFT_ARROWHEAD = 0x02C2, // U+02C2 MODIFIER LETTER LEFT ARROWHEAD + U_MODIFIER_LETTER_RIGHT_ARROWHEAD = 0x02C3, // U+02C3 MODIFIER LETTER RIGHT ARROWHEAD + U_MODIFIER_LETTER_UP_ARROWHEAD = 0x02C4, // U+02C4 MODIFIER LETTER UP ARROWHEAD + U_MODIFIER_LETTER_DOWN_ARROWHEAD = 0x02C5, // U+02C5 MODIFIER LETTER DOWN ARROWHEAD + U_MODIFIER_LETTER_CENTRED_RIGHT_HALF_RING = 0x02D2, // U+02D2 MODIFIER LETTER CENTRED RIGHT HALF RING + U_MODIFIER_LETTER_CENTRED_LEFT_HALF_RING = 0x02D3, // U+02D3 MODIFIER LETTER CENTRED LEFT HALF RING + U_MODIFIER_LETTER_UP_TACK = 0x02D4, // U+02D4 MODIFIER LETTER UP TACK + U_MODIFIER_LETTER_DOWN_TACK = 0x02D5, // U+02D5 MODIFIER LETTER DOWN TACK + U_MODIFIER_LETTER_PLUS_SIGN = 0x02D6, // U+02D6 MODIFIER LETTER PLUS SIGN + U_MODIFIER_LETTER_MINUS_SIGN = 0x02D7, // U+02D7 MODIFIER LETTER MINUS SIGN + U_BREVE = 0x02D8, // U+02D8 BREVE + U_DOT_ABOVE = 0x02D9, // U+02D9 DOT ABOVE + U_RING_ABOVE = 0x02DA, // U+02DA RING ABOVE + U_OGONEK = 0x02DB, // U+02DB OGONEK + U_SMALL_TILDE = 0x02DC, // U+02DC SMALL TILDE + U_DOUBLE_ACUTE_ACCENT = 0x02DD, // U+02DD DOUBLE ACUTE ACCENT + U_MODIFIER_LETTER_RHOTIC_HOOK = 0x02DE, // U+02DE MODIFIER LETTER RHOTIC HOOK + U_MODIFIER_LETTER_CROSS_ACCENT = 0x02DF, // U+02DF MODIFIER LETTER CROSS ACCENT + U_MODIFIER_LETTER_EXTRA_HIGH_TONE_BAR = 0x02E5, // U+02E5 MODIFIER LETTER EXTRA-HIGH TONE BAR + U_MODIFIER_LETTER_HIGH_TONE_BAR = 0x02E6, // U+02E6 MODIFIER LETTER HIGH TONE BAR + U_MODIFIER_LETTER_MID_TONE_BAR = 0x02E7, // U+02E7 MODIFIER LETTER MID TONE BAR + U_MODIFIER_LETTER_LOW_TONE_BAR = 0x02E8, // U+02E8 MODIFIER LETTER LOW TONE BAR + U_MODIFIER_LETTER_EXTRA_LOW_TONE_BAR = 0x02E9, // U+02E9 MODIFIER LETTER EXTRA-LOW TONE BAR + U_MODIFIER_LETTER_YIN_DEPARTING_TONE_MARK = 0x02EA, // U+02EA MODIFIER LETTER YIN DEPARTING TONE MARK + U_MODIFIER_LETTER_YANG_DEPARTING_TONE_MARK = 0x02EB, // U+02EB MODIFIER LETTER YANG DEPARTING TONE MARK + U_MODIFIER_LETTER_UNASPIRATED = 0x02ED, // U+02ED MODIFIER LETTER UNASPIRATED + U_MODIFIER_LETTER_LOW_DOWN_ARROWHEAD = 0x02EF, // U+02EF MODIFIER LETTER LOW DOWN ARROWHEAD + U_MODIFIER_LETTER_LOW_UP_ARROWHEAD = 0x02F0, // U+02F0 MODIFIER LETTER LOW UP ARROWHEAD + U_MODIFIER_LETTER_LOW_LEFT_ARROWHEAD = 0x02F1, // U+02F1 MODIFIER LETTER LOW LEFT ARROWHEAD + U_MODIFIER_LETTER_LOW_RIGHT_ARROWHEAD = 0x02F2, // U+02F2 MODIFIER LETTER LOW RIGHT ARROWHEAD + U_MODIFIER_LETTER_LOW_RING = 0x02F3, // U+02F3 MODIFIER LETTER LOW RING + U_MODIFIER_LETTER_MIDDLE_GRAVE_ACCENT = 0x02F4, // U+02F4 MODIFIER LETTER MIDDLE GRAVE ACCENT + U_MODIFIER_LETTER_MIDDLE_DOUBLE_GRAVE_ACCENT = 0x02F5, // U+02F5 MODIFIER LETTER MIDDLE DOUBLE GRAVE ACCENT + U_MODIFIER_LETTER_MIDDLE_DOUBLE_ACUTE_ACCENT = 0x02F6, // U+02F6 MODIFIER LETTER MIDDLE DOUBLE ACUTE ACCENT + U_MODIFIER_LETTER_LOW_TILDE = 0x02F7, // U+02F7 MODIFIER LETTER LOW TILDE + U_MODIFIER_LETTER_RAISED_COLON = 0x02F8, // U+02F8 MODIFIER LETTER RAISED COLON + U_MODIFIER_LETTER_BEGIN_HIGH_TONE = 0x02F9, // U+02F9 MODIFIER LETTER BEGIN HIGH TONE + U_MODIFIER_LETTER_END_HIGH_TONE = 0x02FA, // U+02FA MODIFIER LETTER END HIGH TONE + U_MODIFIER_LETTER_BEGIN_LOW_TONE = 0x02FB, // U+02FB MODIFIER LETTER BEGIN LOW TONE + U_MODIFIER_LETTER_END_LOW_TONE = 0x02FC, // U+02FC MODIFIER LETTER END LOW TONE + U_MODIFIER_LETTER_SHELF = 0x02FD, // U+02FD MODIFIER LETTER SHELF + U_MODIFIER_LETTER_OPEN_SHELF = 0x02FE, // U+02FE MODIFIER LETTER OPEN SHELF + U_MODIFIER_LETTER_LOW_LEFT_ARROW = 0x02FF, // U+02FF MODIFIER LETTER LOW LEFT ARROW + U_GREEK_LOWER_NUMERAL_SIGN = 0x0375, // U+0375 GREEK LOWER NUMERAL SIGN + U_GREEK_TONOS = 0x0384, // U+0384 GREEK TONOS + U_GREEK_DIALYTIKA_TONOS = 0x0385, // U+0385 GREEK DIALYTIKA TONOS + U_GREEK_KORONIS = 0x1FBD, // U+1FBD GREEK KORONIS + U_GREEK_PSILI = 0x1FBF, // U+1FBF GREEK PSILI + U_GREEK_PERISPOMENI = 0x1FC0, // U+1FC0 GREEK PERISPOMENI + U_GREEK_DIALYTIKA_AND_PERISPOMENI = 0x1FC1, // U+1FC1 GREEK DIALYTIKA AND PERISPOMENI + U_GREEK_PSILI_AND_VARIA = 0x1FCD, // U+1FCD GREEK PSILI AND VARIA + U_GREEK_PSILI_AND_OXIA = 0x1FCE, // U+1FCE GREEK PSILI AND OXIA + U_GREEK_PSILI_AND_PERISPOMENI = 0x1FCF, // U+1FCF GREEK PSILI AND PERISPOMENI + U_GREEK_DASIA_AND_VARIA = 0x1FDD, // U+1FDD GREEK DASIA AND VARIA + U_GREEK_DASIA_AND_OXIA = 0x1FDE, // U+1FDE GREEK DASIA AND OXIA + U_GREEK_DASIA_AND_PERISPOMENI = 0x1FDF, // U+1FDF GREEK DASIA AND PERISPOMENI + U_GREEK_DIALYTIKA_AND_VARIA = 0x1FED, // U+1FED GREEK DIALYTIKA AND VARIA + U_GREEK_DIALYTIKA_AND_OXIA = 0x1FEE, // U+1FEE GREEK DIALYTIKA AND OXIA + U_GREEK_VARIA = 0x1FEF, // U+1FEF GREEK VARIA + U_GREEK_OXIA = 0x1FFD, // U+1FFD GREEK OXIA + U_GREEK_DASIA = 0x1FFE, // U+1FFE GREEK DASIA + + + U_OVERLINE = 0x203E, // Unicode Character 'OVERLINE' + + /** + * UTF-8 BOM + * Unicode Character 'ZERO WIDTH NO-BREAK SPACE' (U+FEFF) + * http://www.fileformat.info/info/unicode/char/feff/index.htm + */ + UTF8_BOM = 65279 +} diff --git a/src/vendor/snippet/snippetParser.ts b/src/vendor/snippet/snippetParser.ts new file mode 100644 index 0000000000..350da2d931 --- /dev/null +++ b/src/vendor/snippet/snippetParser.ts @@ -0,0 +1,1083 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + * Licensed under the MIT License. See https://github.com/microsoft/vscode/blob/d31496c866683bdbccfc85bc11a3107d6c789b52/LICENSE.txt + *--------------------------------------------------------------------------------------------*/ + // From https://raw.githubusercontent.com/microsoft/vscode/d31496c866683bdbccfc85bc11a3107d6c789b52/src/vs/editor/contrib/snippet/snippetParser.ts + +import { CharCode } from './charCode'; + +export const enum TokenType { + Dollar, + Colon, + Comma, + CurlyOpen, + CurlyClose, + Backslash, + Forwardslash, + Pipe, + Int, + VariableName, + Format, + Plus, + Dash, + QuestionMark, + EOF +} + +export interface Token { + type: TokenType; + pos: number; + len: number; +} + + +export class Scanner { + + private static _table: { [ch: number]: TokenType } = { + [CharCode.DollarSign]: TokenType.Dollar, + [CharCode.Colon]: TokenType.Colon, + [CharCode.Comma]: TokenType.Comma, + [CharCode.OpenCurlyBrace]: TokenType.CurlyOpen, + [CharCode.CloseCurlyBrace]: TokenType.CurlyClose, + [CharCode.Backslash]: TokenType.Backslash, + [CharCode.Slash]: TokenType.Forwardslash, + [CharCode.Pipe]: TokenType.Pipe, + [CharCode.Plus]: TokenType.Plus, + [CharCode.Dash]: TokenType.Dash, + [CharCode.QuestionMark]: TokenType.QuestionMark, + }; + + static isDigitCharacter(ch: number): boolean { + return ch >= CharCode.Digit0 && ch <= CharCode.Digit9; + } + + static isVariableCharacter(ch: number): boolean { + return ch === CharCode.Underline + || (ch >= CharCode.a && ch <= CharCode.z) + || (ch >= CharCode.A && ch <= CharCode.Z); + } + + value: string = ''; + pos: number = 0; + + text(value: string) { + this.value = value; + this.pos = 0; + } + + tokenText(token: Token): string { + return this.value.substr(token.pos, token.len); + } + + next(): Token { + + if (this.pos >= this.value.length) { + return { type: TokenType.EOF, pos: this.pos, len: 0 }; + } + + let pos = this.pos; + let len = 0; + let ch = this.value.charCodeAt(pos); + let type: TokenType; + + // static types + type = Scanner._table[ch]; + if (typeof type === 'number') { + this.pos += 1; + return { type, pos, len: 1 }; + } + + // number + if (Scanner.isDigitCharacter(ch)) { + type = TokenType.Int; + do { + len += 1; + ch = this.value.charCodeAt(pos + len); + } while (Scanner.isDigitCharacter(ch)); + + this.pos += len; + return { type, pos, len }; + } + + // variable name + if (Scanner.isVariableCharacter(ch)) { + type = TokenType.VariableName; + do { + ch = this.value.charCodeAt(pos + (++len)); + } while (Scanner.isVariableCharacter(ch) || Scanner.isDigitCharacter(ch)); + + this.pos += len; + return { type, pos, len }; + } + + + // format + type = TokenType.Format; + do { + len += 1; + ch = this.value.charCodeAt(pos + len); + } while ( + !isNaN(ch) + && typeof Scanner._table[ch] === 'undefined' // not static token + && !Scanner.isDigitCharacter(ch) // not number + && !Scanner.isVariableCharacter(ch) // not variable + ); + + this.pos += len; + return { type, pos, len }; + } +} + +export abstract class Marker { + + readonly _markerBrand: any; + + public parent!: Marker; + protected _children: Marker[] = []; + + appendChild(child: Marker): this { + if (child instanceof Text && this._children[this._children.length - 1] instanceof Text) { + // this and previous child are text -> merge them + (this._children[this._children.length - 1]).value += child.value; + } else { + // normal adoption of child + child.parent = this; + this._children.push(child); + } + return this; + } + + replace(child: Marker, others: Marker[]): void { + const { parent } = child; + const idx = parent.children.indexOf(child); + const newChildren = parent.children.slice(0); + newChildren.splice(idx, 1, ...others); + parent._children = newChildren; + + (function _fixParent(children: Marker[], parent: Marker) { + for (const child of children) { + child.parent = parent; + _fixParent(child.children, child); + } + })(others, parent); + } + + get children(): Marker[] { + return this._children; + } + + get snippet(): TextmateSnippet | undefined { + let candidate: Marker = this; + while (true) { + if (!candidate) { + return undefined; + } + if (candidate instanceof TextmateSnippet) { + return candidate; + } + candidate = candidate.parent; + } + } + + toString(): string { + return this.children.reduce((prev, cur) => prev + cur.toString(), ''); + } + + abstract toTextmateString(): string; + + len(): number { + return 0; + } + + abstract clone(): Marker; +} + +export class Text extends Marker { + + static escape(value: string): string { + return value.replace(/\$|}|\\/g, '\\$&'); + } + + constructor(public value: string) { + super(); + } + override toString() { + return this.value; + } + toTextmateString(): string { + return Text.escape(this.value); + } + override len(): number { + return this.value.length; + } + clone(): Text { + return new Text(this.value); + } +} + +export abstract class TransformableMarker extends Marker { + public transform?: Transform; +} + +export class Placeholder extends TransformableMarker { + static compareByIndex(a: Placeholder, b: Placeholder): number { + if (a.index === b.index) { + return 0; + } else if (a.isFinalTabstop) { + return 1; + } else if (b.isFinalTabstop) { + return -1; + } else if (a.index < b.index) { + return -1; + } else if (a.index > b.index) { + return 1; + } else { + return 0; + } + } + + constructor(public index: number) { + super(); + } + + get isFinalTabstop() { + return this.index === 0; + } + + get choice(): Choice | undefined { + return this._children.length === 1 && this._children[0] instanceof Choice + ? this._children[0] as Choice + : undefined; + } + + toTextmateString(): string { + let transformString = ''; + if (this.transform) { + transformString = this.transform.toTextmateString(); + } + if (this.children.length === 0 && !this.transform) { + return `\$${this.index}`; + } else if (this.children.length === 0) { + return `\${${this.index}${transformString}}`; + } else if (this.choice) { + return `\${${this.index}|${this.choice.toTextmateString()}|${transformString}}`; + } else { + return `\${${this.index}:${this.children.map(child => child.toTextmateString()).join('')}${transformString}}`; + } + } + + clone(): Placeholder { + let ret = new Placeholder(this.index); + if (this.transform) { + ret.transform = this.transform.clone(); + } + ret._children = this.children.map(child => child.clone()); + return ret; + } +} + +export class Choice extends Marker { + + readonly options: Text[] = []; + + override appendChild(marker: Marker): this { + if (marker instanceof Text) { + marker.parent = this; + this.options.push(marker); + } + return this; + } + + override toString() { + return this.options[0].value; + } + + toTextmateString(): string { + return this.options + .map(option => option.value.replace(/\||,/g, '\\$&')) + .join(','); + } + + override len(): number { + return this.options[0].len(); + } + + clone(): Choice { + let ret = new Choice(); + this.options.forEach(ret.appendChild, ret); + return ret; + } +} + +export class Transform extends Marker { + + regexp: RegExp = new RegExp(''); + + resolve(value: string): string { + const _this = this; + let didMatch = false; + let ret = value.replace(this.regexp, function () { + didMatch = true; + return _this._replace(Array.prototype.slice.call(arguments, 0, -2)); + }); + // when the regex didn't match and when the transform has + // else branches, then run those + if (!didMatch && this._children.some(child => child instanceof FormatString && Boolean(child.elseValue))) { + ret = this._replace([]); + } + return ret; + } + + private _replace(groups: string[]): string { + let ret = ''; + for (const marker of this._children) { + if (marker instanceof FormatString) { + let value = groups[marker.index] || ''; + value = marker.resolve(value); + ret += value; + } else { + ret += marker.toString(); + } + } + return ret; + } + + override toString(): string { + return ''; + } + + toTextmateString(): string { + return `/${this.regexp.source}/${this.children.map(c => c.toTextmateString())}/${(this.regexp.ignoreCase ? 'i' : '') + (this.regexp.global ? 'g' : '')}`; + } + + clone(): Transform { + let ret = new Transform(); + ret.regexp = new RegExp(this.regexp.source, '' + (this.regexp.ignoreCase ? 'i' : '') + (this.regexp.global ? 'g' : '')); + ret._children = this.children.map(child => child.clone()); + return ret; + } + +} + +export class FormatString extends Marker { + + constructor( + readonly index: number, + readonly shorthandName?: string, + readonly ifValue?: string, + readonly elseValue?: string, + ) { + super(); + } + + resolve(value?: string): string { + if (this.shorthandName === 'upcase') { + return !value ? '' : value.toLocaleUpperCase(); + } else if (this.shorthandName === 'downcase') { + return !value ? '' : value.toLocaleLowerCase(); + } else if (this.shorthandName === 'capitalize') { + return !value ? '' : (value[0].toLocaleUpperCase() + value.substr(1)); + } else if (this.shorthandName === 'pascalcase') { + return !value ? '' : this._toPascalCase(value); + } else if (this.shorthandName === 'camelcase') { + return !value ? '' : this._toCamelCase(value); + } else if (Boolean(value) && typeof this.ifValue === 'string') { + return this.ifValue; + } else if (!Boolean(value) && typeof this.elseValue === 'string') { + return this.elseValue; + } else { + return value || ''; + } + } + + private _toPascalCase(value: string): string { + const match = value.match(/[a-z0-9]+/gi); + if (!match) { + return value; + } + return match.map(word => { + return word.charAt(0).toUpperCase() + + word.substr(1).toLowerCase(); + }) + .join(''); + } + + private _toCamelCase(value: string): string { + const match = value.match(/[a-z0-9]+/gi); + if (!match) { + return value; + } + return match.map((word, index) => { + if (index === 0) { + return word.toLowerCase(); + } else { + return word.charAt(0).toUpperCase() + + word.substr(1).toLowerCase(); + } + }) + .join(''); + } + + toTextmateString(): string { + let value = '${'; + value += this.index; + if (this.shorthandName) { + value += `:/${this.shorthandName}`; + + } else if (this.ifValue && this.elseValue) { + value += `:?${this.ifValue}:${this.elseValue}`; + } else if (this.ifValue) { + value += `:+${this.ifValue}`; + } else if (this.elseValue) { + value += `:-${this.elseValue}`; + } + value += '}'; + return value; + } + + clone(): FormatString { + let ret = new FormatString(this.index, this.shorthandName, this.ifValue, this.elseValue); + return ret; + } +} + +export class Variable extends TransformableMarker { + + constructor(public name: string) { + super(); + } + + resolve(resolver: VariableResolver): boolean { + let value = resolver.resolve(this); + if (this.transform) { + value = this.transform.resolve(value || ''); + } + if (value !== undefined) { + this._children = [new Text(value)]; + return true; + } + return false; + } + + toTextmateString(): string { + let transformString = ''; + if (this.transform) { + transformString = this.transform.toTextmateString(); + } + if (this.children.length === 0) { + return `\${${this.name}${transformString}}`; + } else { + return `\${${this.name}:${this.children.map(child => child.toTextmateString()).join('')}${transformString}}`; + } + } + + clone(): Variable { + const ret = new Variable(this.name); + if (this.transform) { + ret.transform = this.transform.clone(); + } + ret._children = this.children.map(child => child.clone()); + return ret; + } +} + +export interface VariableResolver { + resolve(variable: Variable): string | undefined; +} + +function walk(marker: Marker[], visitor: (marker: Marker) => boolean): void { + const stack = [...marker]; + while (stack.length > 0) { + const marker = stack.shift()!; + const recurse = visitor(marker); + if (!recurse) { + break; + } + stack.unshift(...marker.children); + } +} + +export class TextmateSnippet extends Marker { + + private _placeholders?: { all: Placeholder[], last?: Placeholder }; + + get placeholderInfo() { + if (!this._placeholders) { + // fill in placeholders + let all: Placeholder[] = []; + let last: Placeholder | undefined; + this.walk(function (candidate) { + if (candidate instanceof Placeholder) { + all.push(candidate); + last = !last || last.index < candidate.index ? candidate : last; + } + return true; + }); + this._placeholders = { all, last }; + } + return this._placeholders; + } + + get placeholders(): Placeholder[] { + const { all } = this.placeholderInfo; + return all; + } + + offset(marker: Marker): number { + let pos = 0; + let found = false; + this.walk(candidate => { + if (candidate === marker) { + found = true; + return false; + } + pos += candidate.len(); + return true; + }); + + if (!found) { + return -1; + } + return pos; + } + + fullLen(marker: Marker): number { + let ret = 0; + walk([marker], marker => { + ret += marker.len(); + return true; + }); + return ret; + } + + enclosingPlaceholders(placeholder: Placeholder): Placeholder[] { + let ret: Placeholder[] = []; + let { parent } = placeholder; + while (parent) { + if (parent instanceof Placeholder) { + ret.push(parent); + } + parent = parent.parent; + } + return ret; + } + + resolveVariables(resolver: VariableResolver): this { + this.walk(candidate => { + if (candidate instanceof Variable) { + if (candidate.resolve(resolver)) { + this._placeholders = undefined; + } + } + return true; + }); + return this; + } + + override appendChild(child: Marker) { + this._placeholders = undefined; + return super.appendChild(child); + } + + override replace(child: Marker, others: Marker[]): void { + this._placeholders = undefined; + return super.replace(child, others); + } + + toTextmateString(): string { + return this.children.reduce((prev, cur) => prev + cur.toTextmateString(), ''); + } + + clone(): TextmateSnippet { + let ret = new TextmateSnippet(); + this._children = this.children.map(child => child.clone()); + return ret; + } + + walk(visitor: (marker: Marker) => boolean): void { + walk(this.children, visitor); + } +} + +export class SnippetParser { + + static escape(value: string): string { + return value.replace(/\$|}|\\/g, '\\$&'); + } + + static guessNeedsClipboard(template: string): boolean { + return /\${?CLIPBOARD/.test(template); + } + + private _scanner: Scanner = new Scanner(); + private _token: Token = { type: TokenType.EOF, pos: 0, len: 0 }; + + text(value: string): string { + return this.parse(value).toString(); + } + + parse(value: string, insertFinalTabstop?: boolean, enforceFinalTabstop?: boolean): TextmateSnippet { + + this._scanner.text(value); + this._token = this._scanner.next(); + + const snippet = new TextmateSnippet(); + while (this._parse(snippet)) { + // nothing + } + + // fill in values for placeholders. the first placeholder of an index + // that has a value defines the value for all placeholders with that index + const placeholderDefaultValues = new Map(); + const incompletePlaceholders: Placeholder[] = []; + let placeholderCount = 0; + snippet.walk(marker => { + if (marker instanceof Placeholder) { + placeholderCount += 1; + if (marker.isFinalTabstop) { + placeholderDefaultValues.set(0, undefined); + } else if (!placeholderDefaultValues.has(marker.index) && marker.children.length > 0) { + placeholderDefaultValues.set(marker.index, marker.children); + } else { + incompletePlaceholders.push(marker); + } + } + return true; + }); + for (const placeholder of incompletePlaceholders) { + const defaultValues = placeholderDefaultValues.get(placeholder.index); + if (defaultValues) { + const clone = new Placeholder(placeholder.index); + clone.transform = placeholder.transform; + for (const child of defaultValues) { + clone.appendChild(child.clone()); + } + snippet.replace(placeholder, [clone]); + } + } + + if (!enforceFinalTabstop) { + enforceFinalTabstop = placeholderCount > 0 && insertFinalTabstop; + } + + if (!placeholderDefaultValues.has(0) && enforceFinalTabstop) { + // the snippet uses placeholders but has no + // final tabstop defined -> insert at the end + snippet.appendChild(new Placeholder(0)); + } + + return snippet; + } + + private _accept(type?: TokenType): boolean; + private _accept(type: TokenType | undefined, value: true): string; + private _accept(type: TokenType, value?: boolean): boolean | string { + if (type === undefined || this._token.type === type) { + let ret = !value ? true : this._scanner.tokenText(this._token); + this._token = this._scanner.next(); + return ret; + } + return false; + } + + private _backTo(token: Token): false { + this._scanner.pos = token.pos + token.len; + this._token = token; + return false; + } + + private _until(type: TokenType): false | string { + const start = this._token; + while (this._token.type !== type) { + if (this._token.type === TokenType.EOF) { + return false; + } else if (this._token.type === TokenType.Backslash) { + const nextToken = this._scanner.next(); + if (nextToken.type !== TokenType.Dollar + && nextToken.type !== TokenType.CurlyClose + && nextToken.type !== TokenType.Backslash) { + return false; + } + } + this._token = this._scanner.next(); + } + const value = this._scanner.value.substring(start.pos, this._token.pos).replace(/\\(\$|}|\\)/g, '$1'); + this._token = this._scanner.next(); + return value; + } + + private _parse(marker: Marker): boolean { + return this._parseEscaped(marker) + || this._parseTabstopOrVariableName(marker) + || this._parseComplexPlaceholder(marker) + || this._parseComplexVariable(marker) + || this._parseAnything(marker); + } + + // \$, \\, \} -> just text + private _parseEscaped(marker: Marker): boolean { + let value: string; + if (value = this._accept(TokenType.Backslash, true)) { + // saw a backslash, append escaped token or that backslash + value = this._accept(TokenType.Dollar, true) + || this._accept(TokenType.CurlyClose, true) + || this._accept(TokenType.Backslash, true) + || value; + + marker.appendChild(new Text(value)); + return true; + } + return false; + } + + // $foo -> variable, $1 -> tabstop + private _parseTabstopOrVariableName(parent: Marker): boolean { + let value: string; + const token = this._token; + const match = this._accept(TokenType.Dollar) + && (value = this._accept(TokenType.VariableName, true) || this._accept(TokenType.Int, true)); + + if (!match) { + return this._backTo(token); + } + + parent.appendChild(/^\d+$/.test(value!) + ? new Placeholder(Number(value!)) + : new Variable(value!) + ); + return true; + } + + // ${1:}, ${1} -> placeholder + private _parseComplexPlaceholder(parent: Marker): boolean { + let index: string; + const token = this._token; + const match = this._accept(TokenType.Dollar) + && this._accept(TokenType.CurlyOpen) + && (index = this._accept(TokenType.Int, true)); + + if (!match) { + return this._backTo(token); + } + + const placeholder = new Placeholder(Number(index!)); + + if (this._accept(TokenType.Colon)) { + // ${1:} + while (true) { + + // ...} -> done + if (this._accept(TokenType.CurlyClose)) { + parent.appendChild(placeholder); + return true; + } + + if (this._parse(placeholder)) { + continue; + } + + // fallback + parent.appendChild(new Text('${' + index! + ':')); + placeholder.children.forEach(parent.appendChild, parent); + return true; + } + } else if (placeholder.index > 0 && this._accept(TokenType.Pipe)) { + // ${1|one,two,three|} + const choice = new Choice(); + + while (true) { + if (this._parseChoiceElement(choice)) { + + if (this._accept(TokenType.Comma)) { + // opt, -> more + continue; + } + + if (this._accept(TokenType.Pipe)) { + placeholder.appendChild(choice); + if (this._accept(TokenType.CurlyClose)) { + // ..|} -> done + parent.appendChild(placeholder); + return true; + } + } + } + + this._backTo(token); + return false; + } + + } else if (this._accept(TokenType.Forwardslash)) { + // ${1///} + if (this._parseTransform(placeholder)) { + parent.appendChild(placeholder); + return true; + } + + this._backTo(token); + return false; + + } else if (this._accept(TokenType.CurlyClose)) { + // ${1} + parent.appendChild(placeholder); + return true; + + } else { + // ${1 <- missing curly or colon + return this._backTo(token); + } + } + + private _parseChoiceElement(parent: Choice): boolean { + const token = this._token; + const values: string[] = []; + + while (true) { + if (this._token.type === TokenType.Comma || this._token.type === TokenType.Pipe) { + break; + } + let value: string; + if (value = this._accept(TokenType.Backslash, true)) { + // \, \|, or \\ + value = this._accept(TokenType.Comma, true) + || this._accept(TokenType.Pipe, true) + || this._accept(TokenType.Backslash, true) + || value; + } else { + value = this._accept(undefined, true); + } + if (!value) { + // EOF + this._backTo(token); + return false; + } + values.push(value); + } + + if (values.length === 0) { + this._backTo(token); + return false; + } + + parent.appendChild(new Text(values.join(''))); + return true; + } + + // ${foo:}, ${foo} -> variable + private _parseComplexVariable(parent: Marker): boolean { + let name: string; + const token = this._token; + const match = this._accept(TokenType.Dollar) + && this._accept(TokenType.CurlyOpen) + && (name = this._accept(TokenType.VariableName, true)); + + if (!match) { + return this._backTo(token); + } + + const variable = new Variable(name!); + + if (this._accept(TokenType.Colon)) { + // ${foo:} + while (true) { + + // ...} -> done + if (this._accept(TokenType.CurlyClose)) { + parent.appendChild(variable); + return true; + } + + if (this._parse(variable)) { + continue; + } + + // fallback + parent.appendChild(new Text('${' + name! + ':')); + variable.children.forEach(parent.appendChild, parent); + return true; + } + + } else if (this._accept(TokenType.Forwardslash)) { + // ${foo///} + if (this._parseTransform(variable)) { + parent.appendChild(variable); + return true; + } + + this._backTo(token); + return false; + + } else if (this._accept(TokenType.CurlyClose)) { + // ${foo} + parent.appendChild(variable); + return true; + + } else { + // ${foo <- missing curly or colon + return this._backTo(token); + } + } + + private _parseTransform(parent: TransformableMarker): boolean { + // ...//} + + let transform = new Transform(); + let regexValue = ''; + let regexOptions = ''; + + // (1) /regex + while (true) { + if (this._accept(TokenType.Forwardslash)) { + break; + } + + let escaped: string; + if (escaped = this._accept(TokenType.Backslash, true)) { + escaped = this._accept(TokenType.Forwardslash, true) || escaped; + regexValue += escaped; + continue; + } + + if (this._token.type !== TokenType.EOF) { + regexValue += this._accept(undefined, true); + continue; + } + return false; + } + + // (2) /format + while (true) { + if (this._accept(TokenType.Forwardslash)) { + break; + } + + let escaped: string; + if (escaped = this._accept(TokenType.Backslash, true)) { + escaped = this._accept(TokenType.Backslash, true) || this._accept(TokenType.Forwardslash, true) || escaped; + transform.appendChild(new Text(escaped)); + continue; + } + + if (this._parseFormatString(transform) || this._parseAnything(transform)) { + continue; + } + return false; + } + + // (3) /option + while (true) { + if (this._accept(TokenType.CurlyClose)) { + break; + } + if (this._token.type !== TokenType.EOF) { + regexOptions += this._accept(undefined, true); + continue; + } + return false; + } + + try { + transform.regexp = new RegExp(regexValue, regexOptions); + } catch (e) { + // invalid regexp + return false; + } + + parent.transform = transform; + return true; + } + + private _parseFormatString(parent: Transform): boolean { + + const token = this._token; + if (!this._accept(TokenType.Dollar)) { + return false; + } + + let complex = false; + if (this._accept(TokenType.CurlyOpen)) { + complex = true; + } + + let index = this._accept(TokenType.Int, true); + + if (!index) { + this._backTo(token); + return false; + + } else if (!complex) { + // $1 + parent.appendChild(new FormatString(Number(index))); + return true; + + } else if (this._accept(TokenType.CurlyClose)) { + // ${1} + parent.appendChild(new FormatString(Number(index))); + return true; + + } else if (!this._accept(TokenType.Colon)) { + this._backTo(token); + return false; + } + + if (this._accept(TokenType.Forwardslash)) { + // ${1:/upcase} + let shorthand = this._accept(TokenType.VariableName, true); + if (!shorthand || !this._accept(TokenType.CurlyClose)) { + this._backTo(token); + return false; + } else { + parent.appendChild(new FormatString(Number(index), shorthand)); + return true; + } + + } else if (this._accept(TokenType.Plus)) { + // ${1:+} + let ifValue = this._until(TokenType.CurlyClose); + if (ifValue) { + parent.appendChild(new FormatString(Number(index), undefined, ifValue, undefined)); + return true; + } + + } else if (this._accept(TokenType.Dash)) { + // ${2:-} + let elseValue = this._until(TokenType.CurlyClose); + if (elseValue) { + parent.appendChild(new FormatString(Number(index), undefined, undefined, elseValue)); + return true; + } + + } else if (this._accept(TokenType.QuestionMark)) { + // ${2:?:} + let ifValue = this._until(TokenType.Colon); + if (ifValue) { + let elseValue = this._until(TokenType.CurlyClose); + if (elseValue) { + parent.appendChild(new FormatString(Number(index), undefined, ifValue, elseValue)); + return true; + } + } + + } else { + // ${1:} + let elseValue = this._until(TokenType.CurlyClose); + if (elseValue) { + parent.appendChild(new FormatString(Number(index), undefined, undefined, elseValue)); + return true; + } + } + + this._backTo(token); + return false; + } + + private _parseAnything(marker: Marker): boolean { + if (this._token.type !== TokenType.EOF) { + marker.appendChild(new Text(this._scanner.tokenText(this._token))); + this._accept(undefined); + return true; + } + return false; + } +} diff --git a/src/vendor/snippet/snippetVariables.ts b/src/vendor/snippet/snippetVariables.ts new file mode 100644 index 0000000000..5fa0ab7723 --- /dev/null +++ b/src/vendor/snippet/snippetVariables.ts @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See https://github.com/microsoft/vscode/blob/d31496c866683bdbccfc85bc11a3107d6c789b52/LICENSE.txt + *--------------------------------------------------------------------------------------------*/ +// From https://github.com/microsoft/vscode/blob/d31496c866683bdbccfc85bc11a3107d6c789b52/src/vs/editor/contrib/snippet/snippetVariables.ts + +export const KnownSnippetVariableNames: { [key: string]: true } = Object.freeze({ + 'CURRENT_YEAR': true, + 'CURRENT_YEAR_SHORT': true, + 'CURRENT_MONTH': true, + 'CURRENT_DATE': true, + 'CURRENT_HOUR': true, + 'CURRENT_MINUTE': true, + 'CURRENT_SECOND': true, + 'CURRENT_DAY_NAME': true, + 'CURRENT_DAY_NAME_SHORT': true, + 'CURRENT_MONTH_NAME': true, + 'CURRENT_MONTH_NAME_SHORT': true, + 'CURRENT_SECONDS_UNIX': true, + 'SELECTION': true, + 'CLIPBOARD': true, + 'TM_SELECTED_TEXT': true, + 'TM_CURRENT_LINE': true, + 'TM_CURRENT_WORD': true, + 'TM_LINE_INDEX': true, + 'TM_LINE_NUMBER': true, + 'TM_FILENAME': true, + 'TM_FILENAME_BASE': true, + 'TM_DIRECTORY': true, + 'TM_FILEPATH': true, + 'RELATIVE_FILEPATH': true, + 'BLOCK_COMMENT_START': true, + 'BLOCK_COMMENT_END': true, + 'LINE_COMMENT': true, + 'WORKSPACE_NAME': true, + 'WORKSPACE_FOLDER': true, + 'RANDOM': true, + 'RANDOM_HEX': true, + 'UUID': true +}); \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 6535d82cd7..66e7cd1762 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -16,6 +16,7 @@ }, "exclude": [ "node_modules", + "src/vendor", ".vscode-test" ] } \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index b44493b364..51a863035f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -90,7 +90,7 @@ "@tootallnate/once@1": version "1.1.2" - resolved "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== "@types/glob@^7.1.3": @@ -126,11 +126,16 @@ resolved "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.0.tgz" integrity sha512-/Sge3BymXo4lKc31C8OINJgXLaw+7vL1/L1pGiBNpGrBiT8FQiaFpSYV0uhTaG4y78vcMBTMFsWaHDvuD+xGzQ== -"@types/node@*", "@types/node@^12.11.7": +"@types/node@*": version "12.20.0" resolved "https://registry.npmjs.org/@types/node/-/node-12.20.0.tgz" integrity sha512-0/41wHcurotvSOTHQUFkgL702c3pyWR1mToSrrX3pGPvGfpHTv3Ksx0M4UVuU5VJfjVb62Eyr1eKO1tWNUCg2Q== +"@types/node@^16.11.3": + version "16.11.3" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.3.tgz#fad0b069ec205b0e81429c805d306d2c12e26be1" + integrity sha512-aIYL9Eemcecs1y77XzFGiSc+FdfN58k4J23UEe6+hynf4Wd9g4DzQPwIKL080vSMuubFqy2hWwOzCtJdc6vFKw== + "@types/sinon@^10.0.2": version "10.0.2" resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-10.0.2.tgz#f360d2f189c0fd433d14aeb97b9d705d7e4cc0e4" @@ -230,7 +235,7 @@ acorn@^7.4.0: agent-base@6: version "6.0.2" - resolved "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== dependencies: debug "4" @@ -315,14 +320,14 @@ astral-regex@^2.0.0: integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== balanced-match@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz" - integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== big-integer@^1.6.17: - version "1.6.48" - resolved "https://registry.npmjs.org/big-integer/-/big-integer-1.6.48.tgz" - integrity sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w== + version "1.6.50" + resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.50.tgz#299a4be8bd441c73dcc492ed46b7169c34e92e70" + integrity sha512-+O2uoQWFRo8ysZNo/rjtri2jIwjr3XfeAgRjAUADRqGG+ZITvyn8J1kvXLTaKVr3hhGXk+f23tKfdzmklVM9vQ== binary-extensions@^2.0.0: version "2.2.0" @@ -331,7 +336,7 @@ binary-extensions@^2.0.0: binary@~0.3.0: version "0.3.0" - resolved "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz" + resolved "https://registry.yarnpkg.com/binary/-/binary-0.3.0.tgz#9f60553bc5ce8c3386f3b553cff47462adecaa79" integrity sha1-n2BVO8XOjDOG87VTz/R0Yq3sqnk= dependencies: buffers "~0.1.1" @@ -339,12 +344,12 @@ binary@~0.3.0: bluebird@~3.4.1: version "3.4.7" - resolved "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.4.7.tgz#f72d760be09b7f76d08ed8fae98b289a8d05fab3" integrity sha1-9y12C+Cbf3bQjtj66Ysomo0F+rM= brace-expansion@^1.1.7: version "1.1.11" - resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== dependencies: balanced-match "^1.0.0" @@ -364,12 +369,12 @@ browser-stdout@1.3.1: buffer-indexof-polyfill@~1.0.0: version "1.0.2" - resolved "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz#d2732135c5999c64b277fcf9b1abe3498254729c" integrity sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A== buffers@~0.1.1: version "0.1.1" - resolved "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz" + resolved "https://registry.yarnpkg.com/buffers/-/buffers-0.1.1.tgz#b24579c3bed4d6d396aeee6d9a8ae7f5482ab7bb" integrity sha1-skV5w77U1tOWru5tmorn9Ugqt7s= callsites@^3.0.0: @@ -384,7 +389,7 @@ camelcase@^6.0.0: chainsaw@~0.1.0: version "0.1.0" - resolved "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz" + resolved "https://registry.yarnpkg.com/chainsaw/-/chainsaw-0.1.0.tgz#5eab50b28afe58074d0d58291388828b5e5fbc98" integrity sha1-XqtQsor+WAdNDVgpE4iCi15fvJg= dependencies: traverse ">=0.3.0 <0.4" @@ -456,13 +461,13 @@ color-name@~1.1.4: concat-map@0.0.1: version "0.0.1" - resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= core-util-is@~1.0.0: - version "1.0.2" - resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" - integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + version "1.0.3" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== cross-spawn@^7.0.2: version "7.0.3" @@ -473,7 +478,14 @@ cross-spawn@^7.0.2: shebang-command "^2.0.0" which "^2.0.1" -debug@4, debug@4.3.1, debug@^4.0.1, debug@^4.1.1: +debug@4: + version "4.3.2" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" + integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== + dependencies: + ms "2.1.2" + +debug@4.3.1, debug@^4.0.1, debug@^4.1.1: version "4.3.1" resolved "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz" integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== @@ -511,7 +523,7 @@ doctrine@^3.0.0: duplexer2@~0.1.4: version "0.1.4" - resolved "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz" + resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" integrity sha1-ixLauHjA1p4+eJEFFmKjL8a93ME= dependencies: readable-stream "^2.0.2" @@ -742,7 +754,7 @@ flatted@^3.1.0: fs.realpath@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= fsevents@~2.3.1: @@ -752,7 +764,7 @@ fsevents@~2.3.1: fstream@^1.0.12: version "1.0.12" - resolved "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz" + resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.12.tgz#4e8ba8ee2d48be4f7d0de505455548eae5932045" integrity sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg== dependencies: graceful-fs "^4.1.2" @@ -777,7 +789,7 @@ glob-parent@^5.0.0, glob-parent@^5.1.0, glob-parent@~5.1.0: dependencies: is-glob "^4.0.1" -glob@7.1.6, glob@^7.1.3: +glob@7.1.6: version "7.1.6" resolved "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz" integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== @@ -789,6 +801,18 @@ glob@7.1.6, glob@^7.1.3: once "^1.3.0" path-is-absolute "^1.0.0" +glob@^7.1.3: + version "7.2.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" + integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + glob@^7.1.7: version "7.1.7" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" @@ -821,9 +845,9 @@ globby@^11.0.1: slash "^3.0.0" graceful-fs@^4.1.2, graceful-fs@^4.2.2: - version "4.2.6" - resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz" - integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== + version "4.2.8" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" + integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg== growl@1.10.5: version "1.10.5" @@ -847,7 +871,7 @@ he@1.2.0: http-proxy-agent@^4.0.1: version "4.0.1" - resolved "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== dependencies: "@tootallnate/once" "1" @@ -856,7 +880,7 @@ http-proxy-agent@^4.0.1: https-proxy-agent@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== dependencies: agent-base "6" @@ -892,7 +916,7 @@ imurmurhash@^0.1.4: inflight@^1.0.4: version "1.0.6" - resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= dependencies: once "^1.3.0" @@ -900,7 +924,7 @@ inflight@^1.0.4: inherits@2, inherits@~2.0.0, inherits@~2.0.3: version "2.0.4" - resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== is-binary-path@~2.1.0: @@ -949,7 +973,7 @@ isarray@0.0.1: isarray@~1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= isexe@^2.0.0: @@ -1014,7 +1038,7 @@ levn@^0.4.1: listenercount@~1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/listenercount/-/listenercount-1.0.1.tgz#84c8a72ab59c4725321480c975e6508342e70937" integrity sha1-hMinKrWcRyUyFIDJdeZQg0LnCTc= locate-path@^6.0.0: @@ -1063,19 +1087,19 @@ micromatch@^4.0.2: minimatch@3.0.4, minimatch@^3.0.4: version "3.0.4" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== dependencies: brace-expansion "^1.1.7" minimist@^1.2.5: version "1.2.5" - resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== "mkdirp@>=0.5 0": version "0.5.5" - resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== dependencies: minimist "^1.2.5" @@ -1113,7 +1137,7 @@ mocha@^8.1.3: ms@2.1.2: version "2.1.2" - resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== ms@2.1.3: @@ -1149,7 +1173,7 @@ normalize-path@^3.0.0, normalize-path@~3.0.0: once@^1.3.0: version "1.4.0" - resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= dependencies: wrappy "1" @@ -1194,7 +1218,7 @@ path-exists@^4.0.0: path-is-absolute@^1.0.0: version "1.0.1" - resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= path-key@^3.1.0: @@ -1226,7 +1250,7 @@ prelude-ls@^1.2.1: process-nextick-args@~2.0.0: version "2.0.1" - resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== progress@^2.0.0: @@ -1253,7 +1277,7 @@ randombytes@^2.1.0: readable-stream@^2.0.2, readable-stream@~2.3.6: version "2.3.7" - resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== dependencies: core-util-is "~1.0.0" @@ -1298,14 +1322,14 @@ reusify@^1.0.4: rimraf@2: version "2.7.1" - resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== dependencies: glob "^7.1.3" rimraf@^3.0.2: version "3.0.2" - resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== dependencies: glob "^7.1.3" @@ -1324,7 +1348,7 @@ safe-buffer@^5.1.0: safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== semver@^7.2.1, semver@^7.3.2: @@ -1343,7 +1367,7 @@ serialize-javascript@5.0.1: setimmediate@~1.0.4: version "1.0.5" - resolved "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= shebang-command@^2.0.0: @@ -1408,7 +1432,7 @@ string-width@^4.1.0, string-width@^4.2.0: string_decoder@~1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== dependencies: safe-buffer "~5.1.0" @@ -1482,7 +1506,7 @@ to-regex-range@^5.0.1: "traverse@>=0.3.0 <0.4": version "0.3.9" - resolved "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz" + resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.3.9.tgz#717b8f220cc0bb7b44e40514c22b2e8bbc70d8b9" integrity sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk= tslib@^1.8.1: @@ -1514,14 +1538,14 @@ type-fest@^0.8.1: resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== -typescript@^4.1.2: - version "4.1.5" - resolved "https://registry.npmjs.org/typescript/-/typescript-4.1.5.tgz" - integrity sha512-6OSu9PTIzmn9TCDiovULTnET6BgXtDYL4Gg4szY+cGsc3JP1dQL8qvE8kShTRx1NIw4Q9IBHlwODjkjWEtMUyA== +typescript@^4.4.4: + version "4.4.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.4.4.tgz#2cd01a1a1f160704d3101fd5a58ff0f9fcb8030c" + integrity sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA== unzipper@^0.10.11: version "0.10.11" - resolved "https://registry.npmjs.org/unzipper/-/unzipper-0.10.11.tgz" + resolved "https://registry.yarnpkg.com/unzipper/-/unzipper-0.10.11.tgz#0b4991446472cbdb92ee7403909f26c2419c782e" integrity sha512-+BrAq2oFqWod5IESRjL3S8baohbevGcVA+teAIOYWM3pDVdseogqbzhhvvmiyQrUNKFUnDMtELW3X8ykbyDCJw== dependencies: big-integer "^1.6.17" @@ -1544,7 +1568,7 @@ uri-js@^4.2.2: util-deprecate@~1.0.1: version "1.0.2" - resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= v8-compile-cache@^2.0.3: @@ -1553,9 +1577,9 @@ v8-compile-cache@^2.0.3: integrity sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q== vscode-test@^1.4.1: - version "1.5.0" - resolved "https://registry.npmjs.org/vscode-test/-/vscode-test-1.5.0.tgz" - integrity sha512-svwE/mhBBqrB77C1U7pkUKfUmxnkzg0dLGi1vEmitsleu88oNsqZEhG3ANZrL/Ia4m0CW0oYEKRw2EojpFxLlQ== + version "1.6.1" + resolved "https://registry.yarnpkg.com/vscode-test/-/vscode-test-1.6.1.tgz#44254c67036de92b00fdd72f6ace5f1854e1a563" + integrity sha512-086q88T2ca1k95mUzffvbzb7esqQNvJgiwY4h29ukPhFo8u+vXOOmelUoU5EQUHs3Of8+JuQ3oGdbVCqaxuTXA== dependencies: http-proxy-agent "^4.0.1" https-proxy-agent "^5.0.0" @@ -1597,7 +1621,7 @@ wrap-ansi@^7.0.0: wrappy@1: version "1.0.2" - resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= y18n@^5.0.5: