diff --git a/.editorconfig b/.editorconfig index b4180a87dd7..e7e99b5bcb5 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,4 +1,4 @@ -# EditorConfig is awesome: http://EditorConfig.org +# EditorConfig is awesome: https://EditorConfig.org # top-most EditorConfig file root = true @@ -6,7 +6,6 @@ root = true # Tab indentation [*] indent_style = tab -indent_size = 4 trim_trailing_whitespace = true # The indent size used in the `package.json` file cannot be changed diff --git a/.github/classifier.yml b/.github/classifier.yml index 85cd5bfe8d7..57392331d24 100644 --- a/.github/classifier.yml +++ b/.github/classifier.yml @@ -12,7 +12,7 @@ cli: [], color-palette: [], config: [], - css-less-scss: [ aeschli ], + css-less-scss: [], debug-console: [], debug: { assignees: [ isidorn ], @@ -79,7 +79,7 @@ git: [ joaomoreno ], grammar: [], hot-exit: [], - html: [ aeschli ], + html: [], install-update: [], integrated-terminal: [ Tyriar ], integration-test: [], diff --git a/build/builtInExtensions.json b/build/builtInExtensions.json index 4274c3c871e..045f91a8246 100644 --- a/build/builtInExtensions.json +++ b/build/builtInExtensions.json @@ -31,7 +31,7 @@ }, { "name": "ms-vscode.references-view", - "version": "0.0.19", + "version": "0.0.21", "repo": "https://github.com/Microsoft/vscode-reference-view", "metadata": { "id": "dc489f46-520d-4556-ae85-1f9eab3c412d", diff --git a/build/gulpfile.vscode.linux.js b/build/gulpfile.vscode.linux.js index 50258a98fb3..33972ba398b 100644 --- a/build/gulpfile.vscode.linux.js +++ b/build/gulpfile.vscode.linux.js @@ -15,6 +15,9 @@ const util = require('./lib/util'); const packageJson = require('../package.json'); const product = require('../product.json'); const rpmDependencies = require('../resources/linux/rpm/dependencies.json'); +const path = require('path'); +const root = path.dirname(__dirname); +const commit = util.getVersion(root); const linuxPackageRevision = Math.floor(new Date().getTime() / 1000); @@ -197,11 +200,10 @@ function prepareSnapPackage(arch) { const snapcraft = gulp.src('resources/linux/snap/snapcraft.yaml', { base: '.' }) .pipe(replace('@@NAME@@', product.applicationName)) - .pipe(replace('@@VERSION@@', `${packageJson.version}-${linuxPackageRevision}`)) + .pipe(replace('@@VERSION@@', commit.substr(0, 8))) .pipe(rename('snap/snapcraft.yaml')); const snapUpdate = gulp.src('resources/linux/snap/snapUpdate.sh', { base: '.' }) - .pipe(replace('@@NAME@@', product.applicationName)) .pipe(rename(`usr/share/${product.applicationName}/snapUpdate.sh`)); const electronLaunch = gulp.src('resources/linux/snap/electron-launch', { base: '.' }) diff --git a/extensions/git/package.json b/extensions/git/package.json index ef4385ef3db..14ffdfe40bd 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -1231,6 +1231,7 @@ }, "git.openDiffOnClick": { "type": "boolean", + "scope": "resource", "default": true, "description": "%config.openDiffOnClick%" } diff --git a/extensions/git/resources/icons/dark/open-change.svg b/extensions/git/resources/icons/dark/open-change.svg index e43ba7616c5..6f785c26a5e 100644 --- a/extensions/git/resources/icons/dark/open-change.svg +++ b/extensions/git/resources/icons/dark/open-change.svg @@ -1 +1 @@ -Compare_16x \ No newline at end of file + \ No newline at end of file diff --git a/extensions/git/resources/icons/light/open-change.svg b/extensions/git/resources/icons/light/open-change.svg index e53964f3b81..873b93d8106 100644 --- a/extensions/git/resources/icons/light/open-change.svg +++ b/extensions/git/resources/icons/light/open-change.svg @@ -1 +1 @@ -Compare_16x \ No newline at end of file + \ No newline at end of file diff --git a/extensions/git/src/api/git.d.ts b/extensions/git/src/api/git.d.ts index 1b3ed4d2dec..eb128be6609 100644 --- a/extensions/git/src/api/git.d.ts +++ b/extensions/git/src/api/git.d.ts @@ -67,6 +67,7 @@ export const enum Status { DELETED, UNTRACKED, IGNORED, + INTENT_TO_ADD, ADDED_BY_US, ADDED_BY_THEM, diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 2e83594e45b..c07517aa21b 100755 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -340,6 +340,7 @@ export class CommandCenter { case Status.MODIFIED: case Status.UNTRACKED: case Status.IGNORED: + case Status.INTENT_TO_ADD: const repository = this.model.getRepository(resource.resourceUri); if (!repository) { diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index d188eea026e..d97fcca9e13 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -94,6 +94,7 @@ export class Resource implements SourceControlResourceState { case Status.INDEX_COPIED: return Resource.Icons[theme].Copied; case Status.UNTRACKED: return Resource.Icons[theme].Untracked; case Status.IGNORED: return Resource.Icons[theme].Ignored; + case Status.INTENT_TO_ADD: return Resource.Icons[theme].Added; case Status.BOTH_DELETED: return Resource.Icons[theme].Conflict; case Status.ADDED_BY_US: return Resource.Icons[theme].Conflict; case Status.DELETED_BY_THEM: return Resource.Icons[theme].Conflict; @@ -116,6 +117,7 @@ export class Resource implements SourceControlResourceState { case Status.INDEX_COPIED: return localize('index copied', "Index Copied"); case Status.UNTRACKED: return localize('untracked', "Untracked"); case Status.IGNORED: return localize('ignored', "Ignored"); + case Status.INTENT_TO_ADD: return localize('intent to add', "Intent to Add"); case Status.BOTH_DELETED: return localize('both deleted', "Both Deleted"); case Status.ADDED_BY_US: return localize('added by us', "Added By Us"); case Status.DELETED_BY_THEM: return localize('deleted by them', "Deleted By Them"); @@ -166,6 +168,7 @@ export class Resource implements SourceControlResourceState { case Status.MODIFIED: return 'M'; case Status.INDEX_ADDED: + case Status.INTENT_TO_ADD: return 'A'; case Status.INDEX_DELETED: case Status.DELETED: @@ -201,6 +204,7 @@ export class Resource implements SourceControlResourceState { case Status.DELETED: return new ThemeColor('gitDecoration.deletedResourceForeground'); case Status.INDEX_ADDED: + case Status.INTENT_TO_ADD: return new ThemeColor('gitDecoration.addedResourceForeground'); case Status.INDEX_RENAMED: // todo@joh - special color? case Status.UNTRACKED: @@ -1102,7 +1106,8 @@ export class Repository implements Disposable { const text = lastLine.isEmptyOrWhitespace ? `${textToAppend}\n` : `\n${textToAppend}\n`; edit.insert(document.uri, lastLine.range.end, text); - workspace.applyEdit(edit); + await workspace.applyEdit(edit); + await document.save(); }); } @@ -1211,9 +1216,27 @@ export class Repository implements Disposable { } } + private static KnownHugeFolderNames = ['node_modules']; + + private async findKnownHugeFolderPathsToIgnore(): Promise { + const folderPaths: string[] = []; + + for (const folderName of Repository.KnownHugeFolderNames) { + const folderPath = path.join(this.repository.root, folderName); + + if (await new Promise(c => fs.exists(folderPath, c))) { + folderPaths.push(folderPath); + } + } + + const ignored = await this.checkIgnore(folderPaths); + + return folderPaths.filter(p => !ignored.has(p)); + } + @throttle private async updateModelState(): Promise { - const { status, didHitLimit } = await this.repository.getStatus(); + const { status, didHitLimit } = await this.repository.getStatus(100); const config = workspace.getConfiguration('git'); const shouldIgnore = config.get('ignoreLimitWarning') === true; const useIcons = !config.get('decorations.enabled', true); @@ -1221,15 +1244,34 @@ export class Repository implements Disposable { this.isRepositoryHuge = didHitLimit; if (didHitLimit && !shouldIgnore && !this.didWarnAboutLimit) { + const knownHugeFolderPaths = await this.findKnownHugeFolderPathsToIgnore(); + const gitWarn = localize('huge', "The git repository at '{0}' has too many active changes, only a subset of Git features will be enabled.", this.repository.root); const neverAgain = { title: localize('neveragain', "Don't Show Again") }; - window.showWarningMessage(localize('huge', "The git repository at '{0}' has too many active changes, only a subset of Git features will be enabled.", this.repository.root), neverAgain).then(result => { + if (knownHugeFolderPaths.length > 0) { + const folderPath = knownHugeFolderPaths[0]; + const folderName = path.basename(folderPath); + + const addKnown = localize('add known', "Would you like to add '{0}' to .gitignore?", folderName); + const yes = { title: localize('yes', "Yes") }; + + const result = await window.showWarningMessage(`${gitWarn} ${addKnown}`, yes, neverAgain); + + if (result === neverAgain) { + config.update('ignoreLimitWarning', true, false); + this.didWarnAboutLimit = true; + } else if (result === yes) { + this.ignore([Uri.file(folderPath)]); + } + } else { + const result = await window.showWarningMessage(gitWarn, neverAgain); + if (result === neverAgain) { config.update('ignoreLimitWarning', true, false); } - }); - this.didWarnAboutLimit = true; + this.didWarnAboutLimit = true; + } } let HEAD: Branch | undefined; @@ -1287,6 +1329,7 @@ export class Repository implements Disposable { switch (raw.y) { case 'M': workingTree.push(new Resource(ResourceGroupType.WorkingTree, uri, Status.MODIFIED, useIcons, renameUri)); break; case 'D': workingTree.push(new Resource(ResourceGroupType.WorkingTree, uri, Status.DELETED, useIcons, renameUri)); break; + case 'A': workingTree.push(new Resource(ResourceGroupType.WorkingTree, uri, Status.INTENT_TO_ADD, useIcons, renameUri)); break; } return undefined; }); diff --git a/extensions/grunt/src/main.ts b/extensions/grunt/src/main.ts index 38589e8a596..09a812ac06f 100644 --- a/extensions/grunt/src/main.ts +++ b/extensions/grunt/src/main.ts @@ -59,6 +59,12 @@ function getOutputChannel(): vscode.OutputChannel { return _channel; } +function showError() { + vscode.window.showWarningMessage(localize('gulpTaskDetectError', 'Problem finding jake tasks. See the output for more information.'), + localize('jakeShowOutput', 'Go to output')).then(() => { + getOutputChannel().show(true); + }); +} interface GruntTaskDefinition extends vscode.TaskDefinition { task: string; file?: string; @@ -120,7 +126,7 @@ class FolderDetector { let { stdout, stderr } = await exec(commandLine, { cwd: rootPath }); if (stderr) { getOutputChannel().appendLine(stderr); - getOutputChannel().show(true); + showError(); } let result: vscode.Task[] = []; if (stdout) { @@ -186,7 +192,7 @@ class FolderDetector { channel.appendLine(err.stdout); } channel.appendLine(localize('execFailed', 'Auto detecting Grunt for folder {0} failed with error: {1}', this.workspaceFolder.name, err.error ? err.error.toString() : 'unknown')); - channel.show(true); + showError(); return emptyTasks; } } diff --git a/extensions/html-language-features/.vscode/settings.json b/extensions/html-language-features/.vscode/settings.json new file mode 100644 index 00000000000..e46111f13be --- /dev/null +++ b/extensions/html-language-features/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "editor.insertSpaces": false +} \ No newline at end of file diff --git a/extensions/jake/src/main.ts b/extensions/jake/src/main.ts index 938ca51bec9..d778dcedab7 100644 --- a/extensions/jake/src/main.ts +++ b/extensions/jake/src/main.ts @@ -59,6 +59,13 @@ function getOutputChannel(): vscode.OutputChannel { return _channel; } +function showError() { + vscode.window.showWarningMessage(localize('gulpTaskDetectError', 'Problem finding jake tasks. See the output for more information.'), + localize('jakeShowOutput', 'Go to output')).then(() => { + getOutputChannel().show(true); + }); +} + interface JakeTaskDefinition extends vscode.TaskDefinition { task: string; file?: string; @@ -124,7 +131,7 @@ class FolderDetector { let { stdout, stderr } = await exec(commandLine, { cwd: rootPath }); if (stderr) { getOutputChannel().appendLine(stderr); - getOutputChannel().show(true); + showError(); } let result: vscode.Task[] = []; if (stdout) { @@ -163,7 +170,7 @@ class FolderDetector { channel.appendLine(err.stdout); } channel.appendLine(localize('execFailed', 'Auto detecting Jake for folder {0} failed with error: {1}', this.workspaceFolder.name, err.error ? err.error.toString() : 'unknown')); - channel.show(true); + showError(); return emptyTasks; } } diff --git a/extensions/javascript/syntaxes/JavaScript.tmLanguage.json b/extensions/javascript/syntaxes/JavaScript.tmLanguage.json index e1a5a2ee199..d7db7014da3 100644 --- a/extensions/javascript/syntaxes/JavaScript.tmLanguage.json +++ b/extensions/javascript/syntaxes/JavaScript.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/5d44152e45a4953fc3b4a0a4e42ea4b97768efa1", + "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/de48df22ff31a01f01d28d44f7b3ea79915d8906", "name": "JavaScript (with React support)", "scopeName": "source.js", "patterns": [ @@ -2629,13 +2629,13 @@ ] }, "function-call": { - "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?)*(?\\s*)?\\()", - "end": "(?<=\\))(?!(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?)*(?\\s*)?\\()", + "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\()", + "end": "(?<=\\))(?!(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\()", "patterns": [ { "name": "meta.function-call.js", "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*))", - "end": "(?=\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?)*(?\\s*)?\\()", + "end": "(?=\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\()", "patterns": [ { "include": "#literal" @@ -3664,13 +3664,6 @@ "name": "punctuation.definition.typeparameters.end.js" } }, - "patterns": [ - { - "include": "#type-parameters-body" - } - ] - }, - "type-parameters-body": { "patterns": [ { "include": "#comment" @@ -3729,33 +3722,6 @@ } ] }, - "type-parameters-or-type-arguments": { - "patterns": [ - { - "name": "meta.type.parameters.js", - "begin": "(\\<)(?=\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)", - "beginCaptures": { - "1": { - "name": "punctuation.definition.typeparameters.begin.js" - } - }, - "end": "\\>", - "endCaptures": { - "0": { - "name": "punctuation.definition.typeparameters.end.js" - } - }, - "patterns": [ - { - "include": "#type-parameters-body" - } - ] - }, - { - "include": "#type-arguments" - } - ] - }, "type": { "patterns": [ { @@ -4307,7 +4273,7 @@ "patterns": [ { "name": "string.template.js", - "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?)*(?\\s*)`)", + "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)`)", "beginCaptures": { "1": { "name": "entity.name.function.tagged-template.js" diff --git a/extensions/javascript/syntaxes/JavaScriptReact.tmLanguage.json b/extensions/javascript/syntaxes/JavaScriptReact.tmLanguage.json index 325a2d5193d..abeae4090a9 100644 --- a/extensions/javascript/syntaxes/JavaScriptReact.tmLanguage.json +++ b/extensions/javascript/syntaxes/JavaScriptReact.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/5d44152e45a4953fc3b4a0a4e42ea4b97768efa1", + "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/de48df22ff31a01f01d28d44f7b3ea79915d8906", "name": "JavaScript (with React support)", "scopeName": "source.js.jsx", "patterns": [ @@ -2629,13 +2629,13 @@ ] }, "function-call": { - "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?)*(?\\s*)?\\()", - "end": "(?<=\\))(?!(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?)*(?\\s*)?\\()", + "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\()", + "end": "(?<=\\))(?!(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\()", "patterns": [ { "name": "meta.function-call.js.jsx", "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*))", - "end": "(?=\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?)*(?\\s*)?\\()", + "end": "(?=\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\()", "patterns": [ { "include": "#literal" @@ -3664,13 +3664,6 @@ "name": "punctuation.definition.typeparameters.end.js.jsx" } }, - "patterns": [ - { - "include": "#type-parameters-body" - } - ] - }, - "type-parameters-body": { "patterns": [ { "include": "#comment" @@ -3729,33 +3722,6 @@ } ] }, - "type-parameters-or-type-arguments": { - "patterns": [ - { - "name": "meta.type.parameters.js.jsx", - "begin": "(\\<)(?=\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)", - "beginCaptures": { - "1": { - "name": "punctuation.definition.typeparameters.begin.js.jsx" - } - }, - "end": "\\>", - "endCaptures": { - "0": { - "name": "punctuation.definition.typeparameters.end.js.jsx" - } - }, - "patterns": [ - { - "include": "#type-parameters-body" - } - ] - }, - { - "include": "#type-arguments" - } - ] - }, "type": { "patterns": [ { @@ -4307,7 +4273,7 @@ "patterns": [ { "name": "string.template.js.jsx", - "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?)*(?\\s*)`)", + "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)`)", "beginCaptures": { "1": { "name": "entity.name.function.tagged-template.js.jsx" diff --git a/extensions/markdown-language-features/package.json b/extensions/markdown-language-features/package.json index 54b289a7d23..c687b41bc5e 100644 --- a/extensions/markdown-language-features/package.json +++ b/extensions/markdown-language-features/package.json @@ -305,13 +305,13 @@ "build-preview": "webpack --mode development" }, "dependencies": { - "highlight.js": "9.12.0", - "markdown-it": "^8.4.1", + "highlight.js": "9.13.1", + "markdown-it": "^8.4.2", "vscode-extension-telemetry": "0.1.0", "vscode-nls": "^4.0.0" }, "devDependencies": { - "@types/highlight.js": "9.1.10", + "@types/highlight.js": "9.12.3", "@types/lodash.throttle": "^4.1.3", "@types/markdown-it": "0.0.2", "@types/node": "^8.10.25", diff --git a/extensions/markdown-language-features/src/security.ts b/extensions/markdown-language-features/src/security.ts index 3a62eed2dbd..7f11d65a113 100644 --- a/extensions/markdown-language-features/src/security.ts +++ b/extensions/markdown-language-features/src/security.ts @@ -149,6 +149,7 @@ export class PreviewSecuritySelector { if (selection.type === 'toggle') { this.cspArbiter.setShouldDisableSecurityWarning(!this.cspArbiter.shouldDisableSecurityWarnings()); + this.webviewManager.refresh(); return; } else { await this.cspArbiter.setSecurityLevelForResource(resource, selection.type); diff --git a/extensions/markdown-language-features/yarn.lock b/extensions/markdown-language-features/yarn.lock index 74030b70a59..071b5b8e161 100644 --- a/extensions/markdown-language-features/yarn.lock +++ b/extensions/markdown-language-features/yarn.lock @@ -7,10 +7,10 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd" integrity sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow== -"@types/highlight.js@9.1.10": - version "9.1.10" - resolved "https://registry.yarnpkg.com/@types/highlight.js/-/highlight.js-9.1.10.tgz#b621f809cd9573b80992b90cffc5788208e3069c" - integrity sha512-3uQgLVw3ukDjrgi1h2qxSgsg2W7Sp/BN/P+IBgi8D019FdCcetJzJIxk0Wp1Qfcxzy3EreUnPI7/1HXhFNCRTg== +"@types/highlight.js@9.12.3": + version "9.12.3" + resolved "https://registry.yarnpkg.com/@types/highlight.js/-/highlight.js-9.12.3.tgz#b672cfaac25cbbc634a0fd92c515f66faa18dbca" + integrity sha512-pGF/zvYOACZ/gLGWdQH8zSwteQS1epp68yRcVLJMgUck/MjEn/FBYmPub9pXT8C1e4a8YZfHo1CKyV8q1vKUnQ== "@types/lodash.throttle@^4.1.3": version "4.1.3" @@ -2933,10 +2933,10 @@ he@1.1.1: resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" integrity sha1-k0EP0hsAlzUVH4howvJx80J+I/0= -highlight.js@9.12.0: - version "9.12.0" - resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.12.0.tgz#e6d9dbe57cbefe60751f02af336195870c90c01e" - integrity sha1-5tnb5Xy+/mB1HwKvM2GVhwyQwB4= +highlight.js@9.13.1: + version "9.13.1" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.13.1.tgz#054586d53a6863311168488a0f58d6c505ce641e" + integrity sha512-Sc28JNQNDzaH6PORtRLMvif9RSn1mYuOoX3omVjnb0+HbpPygU2ALBI0R/wsiqCb4/fcp07Gdo8g+fhtFrQl6A== hmac-drbg@^1.0.0: version "1.0.1" @@ -3895,10 +3895,10 @@ map-visit@^1.0.0: dependencies: object-visit "^1.0.0" -markdown-it@^8.4.1: - version "8.4.1" - resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-8.4.1.tgz#206fe59b0e4e1b78a7c73250af9b34a4ad0aaf44" - integrity sha512-CzzqSSNkFRUf9vlWvhK1awpJreMRqdCrBvZ8DIoDWTOkESMIF741UPAhuAmbyWmdiFPA6WARNhnu2M6Nrhwa+A== +markdown-it@^8.4.2: + version "8.4.2" + resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-8.4.2.tgz#386f98998dc15a37722aa7722084f4020bdd9b54" + integrity sha512-GcRz3AWTqSUphY3vsUqQSFMbgR38a4Lh3GWlHRh/7MRwz8mcu9n2IO7HOh+bXHrR9kOPDl5RNCaEsrneb+xhHQ== dependencies: argparse "^1.0.7" entities "~1.1.1" diff --git a/extensions/typescript-basics/cgmanifest.json b/extensions/typescript-basics/cgmanifest.json index 7ff118aafb6..3a7d9edea47 100644 --- a/extensions/typescript-basics/cgmanifest.json +++ b/extensions/typescript-basics/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "TypeScript-TmLanguage", "repositoryUrl": "https://github.com/Microsoft/TypeScript-TmLanguage", - "commitHash": "5d44152e45a4953fc3b4a0a4e42ea4b97768efa1" + "commitHash": "de48df22ff31a01f01d28d44f7b3ea79915d8906" } }, "license": "MIT", diff --git a/extensions/typescript-basics/syntaxes/TypeScript.tmLanguage.json b/extensions/typescript-basics/syntaxes/TypeScript.tmLanguage.json index 3f5dc77d6e7..5d3637fa75a 100644 --- a/extensions/typescript-basics/syntaxes/TypeScript.tmLanguage.json +++ b/extensions/typescript-basics/syntaxes/TypeScript.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/5d44152e45a4953fc3b4a0a4e42ea4b97768efa1", + "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/de48df22ff31a01f01d28d44f7b3ea79915d8906", "name": "TypeScript", "scopeName": "source.ts", "patterns": [ @@ -2626,13 +2626,13 @@ ] }, "function-call": { - "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?)*(?\\s*)?\\()", - "end": "(?<=\\))(?!(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?)*(?\\s*)?\\()", + "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\()", + "end": "(?<=\\))(?!(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\()", "patterns": [ { "name": "meta.function-call.ts", "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*))", - "end": "(?=\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?)*(?\\s*)?\\()", + "end": "(?=\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\()", "patterns": [ { "include": "#literal" @@ -3698,13 +3698,6 @@ "name": "punctuation.definition.typeparameters.end.ts" } }, - "patterns": [ - { - "include": "#type-parameters-body" - } - ] - }, - "type-parameters-body": { "patterns": [ { "include": "#comment" @@ -3763,33 +3756,6 @@ } ] }, - "type-parameters-or-type-arguments": { - "patterns": [ - { - "name": "meta.type.parameters.ts", - "begin": "(\\<)(?=\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)", - "beginCaptures": { - "1": { - "name": "punctuation.definition.typeparameters.begin.ts" - } - }, - "end": "\\>", - "endCaptures": { - "0": { - "name": "punctuation.definition.typeparameters.end.ts" - } - }, - "patterns": [ - { - "include": "#type-parameters-body" - } - ] - }, - { - "include": "#type-arguments" - } - ] - }, "type": { "patterns": [ { @@ -4341,7 +4307,7 @@ "patterns": [ { "name": "string.template.ts", - "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?)*(?\\s*)`)", + "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)`)", "beginCaptures": { "1": { "name": "entity.name.function.tagged-template.ts" diff --git a/extensions/typescript-basics/syntaxes/TypeScriptReact.tmLanguage.json b/extensions/typescript-basics/syntaxes/TypeScriptReact.tmLanguage.json index 67471e8a57b..39311c06db1 100644 --- a/extensions/typescript-basics/syntaxes/TypeScriptReact.tmLanguage.json +++ b/extensions/typescript-basics/syntaxes/TypeScriptReact.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/5d44152e45a4953fc3b4a0a4e42ea4b97768efa1", + "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/de48df22ff31a01f01d28d44f7b3ea79915d8906", "name": "TypeScriptReact", "scopeName": "source.tsx", "patterns": [ @@ -2629,13 +2629,13 @@ ] }, "function-call": { - "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?)*(?\\s*)?\\()", - "end": "(?<=\\))(?!(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?)*(?\\s*)?\\()", + "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\()", + "end": "(?<=\\))(?!(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\()", "patterns": [ { "name": "meta.function-call.tsx", "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*))", - "end": "(?=\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?)*(?\\s*)?\\()", + "end": "(?=\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\()", "patterns": [ { "include": "#literal" @@ -3664,13 +3664,6 @@ "name": "punctuation.definition.typeparameters.end.tsx" } }, - "patterns": [ - { - "include": "#type-parameters-body" - } - ] - }, - "type-parameters-body": { "patterns": [ { "include": "#comment" @@ -3729,33 +3722,6 @@ } ] }, - "type-parameters-or-type-arguments": { - "patterns": [ - { - "name": "meta.type.parameters.tsx", - "begin": "(\\<)(?=\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)", - "beginCaptures": { - "1": { - "name": "punctuation.definition.typeparameters.begin.tsx" - } - }, - "end": "\\>", - "endCaptures": { - "0": { - "name": "punctuation.definition.typeparameters.end.tsx" - } - }, - "patterns": [ - { - "include": "#type-parameters-body" - } - ] - }, - { - "include": "#type-arguments" - } - ] - }, "type": { "patterns": [ { @@ -4307,7 +4273,7 @@ "patterns": [ { "name": "string.template.tsx", - "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?)*(?\\s*)`)", + "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)`)", "beginCaptures": { "1": { "name": "entity.name.function.tagged-template.tsx" diff --git a/package.json b/package.json index ae650b3bcff..ae21e5bdbba 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "vscode-ripgrep": "^1.2.5", "vscode-sqlite3": "4.0.5", "vscode-textmate": "^4.0.1", - "vscode-xterm": "3.10.0-beta5", + "vscode-xterm": "3.10.0-beta6", "winreg": "^1.2.4", "yauzl": "^2.9.1", "yazl": "^2.4.3" diff --git a/resources/linux/snap/snapUpdate.sh b/resources/linux/snap/snapUpdate.sh index d9299ca4d58..77569bfc16a 100755 --- a/resources/linux/snap/snapUpdate.sh +++ b/resources/linux/snap/snapUpdate.sh @@ -1,3 +1,3 @@ #!/bin/sh sleep 2 -@@NAME@@ \ No newline at end of file +$SNAP_NAME \ No newline at end of file diff --git a/src/main.js b/src/main.js index 7df35af8038..1d971213754 100644 --- a/src/main.js +++ b/src/main.js @@ -175,14 +175,6 @@ function resolveJSFlags(cliArgs, ...jsFlags) { jsFlags.push(`--max_old_space_size=${cliArgs['max-memory']}`); } - // Since Electron 3 we use a V8 version that comes with untrusted code mitigations - // enabled by default (https://v8.dev/docs/untrusted-code-mitigations). This comes - // at a performance cost according to the blog. Since we do not execute untrusted - // code, we disable these mitigations. - // TODO@Ben revisit this for later Electron versions, search for more usages of - // --no-untrusted-code-mitigations - jsFlags.push('--no-untrusted-code-mitigations'); - return jsFlags.length > 0 ? jsFlags.join(' ') : null; } diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index 7ea051a4d06..0a4ca61c962 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -76,6 +76,7 @@ "./vs/base/browser/ui/toolbar/toolbar.ts", "./vs/base/browser/ui/tree/abstractTree.ts", "./vs/base/browser/ui/tree/asyncDataTree.ts", + "./vs/base/browser/ui/tree/dataTree.ts", "./vs/base/browser/ui/tree/indexTree.ts", "./vs/base/browser/ui/tree/indexTreeModel.ts", "./vs/base/browser/ui/tree/objectTree.ts", @@ -98,6 +99,7 @@ "./vs/base/parts/ipc/test/node/testService.ts", "./vs/base/parts/quickopen/common/quickOpen.ts", "./vs/base/parts/quickopen/common/quickOpenScorer.ts", + "./vs/base/parts/quickopen/test/common/quickOpenScorer.test.ts", "./vs/base/test/browser/browser.test.ts", "./vs/base/test/browser/comparers.test.ts", "./vs/base/test/browser/dom.test.ts", @@ -105,46 +107,64 @@ "./vs/base/test/browser/progressBar.test.ts", "./vs/base/test/browser/ui/contextview/contextview.test.ts", "./vs/base/test/browser/ui/grid/grid.test.ts", + "./vs/base/test/browser/ui/grid/gridview.test.ts", "./vs/base/test/browser/ui/grid/util.ts", "./vs/base/test/browser/ui/list/listView.test.ts", "./vs/base/test/browser/ui/list/rangeMap.test.ts", "./vs/base/test/browser/ui/scrollbar/scrollableElement.test.ts", "./vs/base/test/browser/ui/scrollbar/scrollbarState.test.ts", + "./vs/base/test/browser/ui/splitview/splitview.test.ts", + "./vs/base/test/browser/ui/tree/asyncDataTree.test.ts", "./vs/base/test/browser/ui/tree/indexTreeModel.test.ts", "./vs/base/test/browser/ui/tree/objectTree.test.ts", "./vs/base/test/browser/ui/tree/objectTreeModel.test.ts", + "./vs/base/test/common/arrays.test.ts", "./vs/base/test/common/assert.test.ts", + "./vs/base/test/common/async.test.ts", "./vs/base/test/common/cache.test.ts", "./vs/base/test/common/cancellation.test.ts", "./vs/base/test/common/charCode.test.ts", "./vs/base/test/common/collections.test.ts", "./vs/base/test/common/color.test.ts", "./vs/base/test/common/decorators.test.ts", + "./vs/base/test/common/diff/diff.test.ts", "./vs/base/test/common/errors.test.ts", + "./vs/base/test/common/event.test.ts", "./vs/base/test/common/filters.perf.test.ts", "./vs/base/test/common/filters.test.ts", "./vs/base/test/common/hash.test.ts", + "./vs/base/test/common/history.test.ts", "./vs/base/test/common/json.test.ts", "./vs/base/test/common/jsonEdit.test.ts", "./vs/base/test/common/jsonFormatter.test.ts", + "./vs/base/test/common/labels.test.ts", "./vs/base/test/common/lifecycle.test.ts", "./vs/base/test/common/linkedList.test.ts", + "./vs/base/test/common/map.test.ts", "./vs/base/test/common/marshalling.test.ts", "./vs/base/test/common/mime.test.ts", "./vs/base/test/common/objects.test.ts", + "./vs/base/test/common/octicon.test.ts", "./vs/base/test/common/paging.test.ts", "./vs/base/test/common/paths.test.ts", "./vs/base/test/common/resources.test.ts", "./vs/base/test/common/scrollable.test.ts", + "./vs/base/test/common/strings.test.ts", "./vs/base/test/common/types.test.ts", + "./vs/base/test/common/uri.test.ts", "./vs/base/test/common/utils.ts", "./vs/base/test/common/uuid.test.ts", + "./vs/base/test/node/config.test.ts", "./vs/base/test/node/console.test.ts", "./vs/base/test/node/decoder.test.ts", "./vs/base/test/node/encoding/encoding.test.ts", + "./vs/base/test/node/extfs/extfs.test.ts", + "./vs/base/test/node/flow.test.ts", "./vs/base/test/node/id.test.ts", + "./vs/base/test/node/glob.test.ts", "./vs/base/test/node/port.test.ts", + "./vs/base/test/node/pfs.test.ts", "./vs/base/test/node/processes/fixtures/fork.ts", "./vs/base/test/node/processes/fixtures/fork_large.ts", "./vs/base/test/node/processes/processes.test.ts", @@ -177,6 +197,7 @@ "./vs/code/node/wait.ts", "./vs/code/node/windowsFinder.ts", "./vs/code/test/node/argv.test.ts", + "./vs/code/test/node/windowsFinder.test.ts", "./vs/editor/contrib/bracketMatching/bracketMatching.ts", "./vs/editor/contrib/bracketMatching/test/bracketMatching.test.ts", "./vs/editor/contrib/caretOperations/caretOperations.ts", @@ -204,6 +225,7 @@ "./vs/editor/contrib/comment/comment.ts", "./vs/editor/contrib/comment/lineCommentCommand.ts", "./vs/editor/contrib/comment/test/blockCommentCommand.test.ts", + "./vs/editor/contrib/comment/test/lineCommentCommand.test.ts", "./vs/editor/contrib/contextmenu/contextmenu.ts", "./vs/editor/contrib/cursorUndo/cursorUndo.ts", "./vs/editor/contrib/dnd/dnd.ts", @@ -259,6 +281,7 @@ "./vs/editor/contrib/message/messageController.ts", "./vs/editor/contrib/multicursor/multicursor.ts", "./vs/editor/contrib/parameterHints/parameterHints.ts", + "./vs/editor/contrib/parameterHints/parameterHintsModel.ts", "./vs/editor/contrib/parameterHints/parameterHintsWidget.ts", "./vs/editor/contrib/parameterHints/provideSignatureHelp.ts", "./vs/editor/contrib/parameterHints/test/parameterHintsModel.test.ts", @@ -377,7 +400,6 @@ "./vs/platform/diagnostics/electron-main/diagnosticsService.ts", "./vs/platform/dialogs/common/dialogs.ts", "./vs/platform/dialogs/node/dialogIpc.ts", - "./vs/platform/dialogs/node/dialogService.ts", "./vs/platform/download/common/download.ts", "./vs/platform/download/node/downloadIpc.ts", "./vs/platform/download/node/downloadService.ts", @@ -606,9 +628,13 @@ "./vs/workbench/parts/extensions/electron-browser/extensionsUtils.ts", "./vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts", "./vs/workbench/parts/extensions/test/common/extensionQuery.test.ts", + "./vs/workbench/parts/feedback/electron-browser/feedback.contribution.ts", + "./vs/workbench/parts/feedback/electron-browser/feedback.ts", + "./vs/workbench/parts/feedback/electron-browser/feedbackStatusbarItem.ts", "./vs/workbench/parts/files/common/explorerModel.ts", "./vs/workbench/parts/files/common/files.ts", "./vs/workbench/parts/files/electron-browser/views/explorerDecorationsProvider.ts", + "./vs/workbench/parts/holidays/electron-browser/holidays.contribution.ts", "./vs/workbench/parts/localizations/electron-browser/localizationsActions.ts", "./vs/workbench/parts/logs/common/logConstants.ts", "./vs/workbench/parts/logs/electron-browser/logs.contribution.ts", @@ -618,8 +644,8 @@ "./vs/workbench/parts/markers/electron-browser/markersFileDecorations.ts", "./vs/workbench/parts/markers/electron-browser/markersFilterOptions.ts", "./vs/workbench/parts/markers/electron-browser/markersModel.ts", - "./vs/workbench/parts/markers/electron-browser/messages.ts", "./vs/workbench/parts/markers/electron-browser/markersPanelActions.ts", + "./vs/workbench/parts/markers/electron-browser/messages.ts", "./vs/workbench/parts/outline/electron-browser/outline.ts", "./vs/workbench/parts/output/common/output.ts", "./vs/workbench/parts/output/common/outputLinkComputer.ts", @@ -637,6 +663,7 @@ "./vs/workbench/parts/search/common/constants.ts", "./vs/workbench/parts/search/common/queryBuilder.ts", "./vs/workbench/parts/search/common/search.ts", + "./vs/workbench/parts/search/test/browser/mockSearchTree.ts", "./vs/workbench/parts/snippets/electron-browser/configureSnippets.ts", "./vs/workbench/parts/snippets/electron-browser/snippetCompletionProvider.ts", "./vs/workbench/parts/snippets/electron-browser/snippets.contribution.ts", @@ -646,6 +673,7 @@ "./vs/workbench/parts/snippets/test/electron-browser/snippetsRewrite.test.ts", "./vs/workbench/parts/stats/node/stats.contribution.ts", "./vs/workbench/parts/stats/node/workspaceStats.ts", + "./vs/workbench/parts/stats/test/workspaceStats.test.ts", "./vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.ts", "./vs/workbench/parts/surveys/electron-browser/nps.contribution.ts", "./vs/workbench/parts/tasks/common/problemCollectors.ts", @@ -655,8 +683,13 @@ "./vs/workbench/parts/tasks/common/taskSystem.ts", "./vs/workbench/parts/tasks/common/taskTemplates.ts", "./vs/workbench/parts/tasks/common/tasks.ts", + "./vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.ts", + "./vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.ts", "./vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.ts", "./vs/workbench/parts/tasks/electron-browser/runAutomaticTasks.ts", + "./vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts", + "./vs/workbench/parts/tasks/node/processTaskSystem.ts", + "./vs/workbench/parts/tasks/node/taskConfiguration.ts", "./vs/workbench/parts/tasks/node/tasks.ts", "./vs/workbench/parts/tasks/test/common/problemMatcher.test.ts", "./vs/workbench/parts/terminal/browser/terminalFindWidget.ts", @@ -756,6 +789,7 @@ "./vs/workbench/services/keybinding/common/windowsKeyboardMapper.ts", "./vs/workbench/services/keybinding/electron-browser/keybindingService.ts", "./vs/workbench/services/keybinding/test/keyboardMapperTestUtils.ts", + "./vs/workbench/services/keybinding/test/windowsKeyboardMapper.test.ts", "./vs/workbench/services/mode/common/workbenchModeService.ts", "./vs/workbench/services/notification/common/notificationService.ts", "./vs/workbench/services/panel/common/panelService.ts", @@ -800,6 +834,7 @@ "./vs/workbench/services/title/common/titleService.ts", "./vs/workbench/services/workspace/common/workspaceEditing.ts", "./vs/workbench/test/common/editor/editorOptions.test.ts", + "./vs/workbench/test/common/notifications.test.ts", "./vs/workbench/test/electron-browser/api/mock.ts" ], "exclude": [ diff --git a/src/vs/base/browser/browser.ts b/src/vs/base/browser/browser.ts index 08d9b2b8ec8..e687780e57c 100644 --- a/src/vs/base/browser/browser.ts +++ b/src/vs/base/browser/browser.ts @@ -14,7 +14,7 @@ class WindowManager { // --- Zoom Level private _zoomLevel: number = 0; private _lastZoomLevelChangeTime: number = 0; - private readonly _onDidChangeZoomLevel: Emitter = new Emitter(); + private readonly _onDidChangeZoomLevel = new Emitter(); public readonly onDidChangeZoomLevel: Event = this._onDidChangeZoomLevel.event; public getZoomLevel(): number { @@ -58,7 +58,7 @@ class WindowManager { // --- Fullscreen private _fullscreen: boolean; - private readonly _onDidChangeFullscreen: Emitter = new Emitter(); + private readonly _onDidChangeFullscreen = new Emitter(); public readonly onDidChangeFullscreen: Event = this._onDidChangeFullscreen.event; public setFullscreen(fullscreen: boolean): void { @@ -75,7 +75,7 @@ class WindowManager { // --- Accessibility private _accessibilitySupport = platform.AccessibilitySupport.Unknown; - private readonly _onDidChangeAccessibilitySupport: Emitter = new Emitter(); + private readonly _onDidChangeAccessibilitySupport = new Emitter(); public readonly onDidChangeAccessibilitySupport: Event = this._onDidChangeAccessibilitySupport.event; public setAccessibilitySupport(accessibilitySupport: platform.AccessibilitySupport): void { diff --git a/src/vs/base/browser/ui/iconLabel/iconLabel.ts b/src/vs/base/browser/ui/iconLabel/iconLabel.ts index 752437df1ba..635e5228bf2 100644 --- a/src/vs/base/browser/ui/iconLabel/iconLabel.ts +++ b/src/vs/base/browser/ui/iconLabel/iconLabel.ts @@ -7,7 +7,7 @@ import 'vs/css!./iconlabel'; import * as dom from 'vs/base/browser/dom'; import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; import { IMatch } from 'vs/base/common/filters'; -import { IDisposable, combinedDisposable, Disposable } from 'vs/base/common/lifecycle'; +import { Disposable } from 'vs/base/common/lifecycle'; export interface IIconLabelCreationOptions { supportHighlights?: boolean; @@ -116,13 +116,7 @@ export class IconLabel extends Disposable { return this.domNode.element; } - onClick(callback: (event: MouseEvent) => void): IDisposable { - return combinedDisposable([ - dom.addDisposableListener(this.labelDescriptionContainer.element, dom.EventType.CLICK, (e: MouseEvent) => callback(e)), - ]); - } - - setValue(label?: string, description?: string, options?: IIconLabelValueOptions): void { + setLabel(label?: string, description?: string, options?: IIconLabelValueOptions): void { const classes = ['monaco-icon-label']; if (options) { if (options.extraClasses) { diff --git a/src/vs/base/browser/ui/menu/menu.ts b/src/vs/base/browser/ui/menu/menu.ts index ab2113c2cca..e9bfa3fb980 100644 --- a/src/vs/base/browser/ui/menu/menu.ts +++ b/src/vs/base/browser/ui/menu/menu.ts @@ -15,7 +15,7 @@ import { RunOnceScheduler } from 'vs/base/common/async'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { Color } from 'vs/base/common/color'; import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; -import { ScrollbarVisibility } from 'vs/base/common/scrollable'; +import { ScrollbarVisibility, ScrollEvent } from 'vs/base/common/scrollable'; import { Event, Emitter } from 'vs/base/common/event'; import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; @@ -59,6 +59,7 @@ export class Menu extends ActionBar { private menuDisposables: IDisposable[]; private scrollableElement: DomScrollableElement; private menuElement: HTMLElement; + private scrollTopHold: number; private readonly _onScroll: Emitter; @@ -121,6 +122,7 @@ export class Menu extends ActionBar { let relatedTarget = e.relatedTarget as HTMLElement; if (!isAncestor(relatedTarget, this.domNode)) { this.focusedItem = undefined; + this.scrollTopHold = this.menuElement.scrollTop; this.updateFocus(); e.stopPropagation(); } @@ -143,6 +145,7 @@ export class Menu extends ActionBar { if (hasClass(target, 'action-item')) { const lastFocusedItem = this.focusedItem; + this.scrollTopHold = this.menuElement.scrollTop; this.setFocusedItem(target); if (lastFocusedItem !== this.focusedItem) { @@ -178,7 +181,11 @@ export class Menu extends ActionBar { this._onScroll.fire(); }, this, this.menuDisposables); - this._register(addDisposableListener(this.menuElement, EventType.SCROLL, (e) => { + this._register(addDisposableListener(this.menuElement, EventType.SCROLL, (e: ScrollEvent) => { + if (this.scrollTopHold !== undefined) { + this.menuElement.scrollTop = this.scrollTopHold; + this.scrollTopHold = undefined; + } this.scrollableElement.scanDomNode(); })); diff --git a/src/vs/base/browser/ui/scrollbar/scrollableElement.ts b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts index a6256deeba9..9b31c847f70 100644 --- a/src/vs/base/browser/ui/scrollbar/scrollableElement.ts +++ b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts @@ -281,6 +281,7 @@ export abstract class AbstractScrollableElement extends Widget { let massagedOptions = resolveOptions(newOptions); this._options.handleMouseWheel = massagedOptions.handleMouseWheel; this._options.mouseWheelScrollSensitivity = massagedOptions.mouseWheelScrollSensitivity; + this._options.fastScrollSensitivity = massagedOptions.fastScrollSensitivity; this._setListeningToMouseWheel(this._options.handleMouseWheel); if (!this._options.lazyRender) { @@ -340,6 +341,12 @@ export abstract class AbstractScrollableElement extends Widget { deltaY = 0; } + if (e.browserEvent && e.browserEvent.altKey) { + // fastScrolling + deltaX = deltaX * this._options.fastScrollSensitivity; + deltaY = deltaY * this._options.fastScrollSensitivity; + } + const futureScrollPosition = this._scrollable.getFutureScrollPosition(); let desiredScrollPosition: INewScrollPosition = {}; @@ -540,6 +547,7 @@ function resolveOptions(opts: ScrollableElementCreationOptions): ScrollableEleme alwaysConsumeMouseWheel: (typeof opts.alwaysConsumeMouseWheel !== 'undefined' ? opts.alwaysConsumeMouseWheel : false), scrollYToX: (typeof opts.scrollYToX !== 'undefined' ? opts.scrollYToX : false), mouseWheelScrollSensitivity: (typeof opts.mouseWheelScrollSensitivity !== 'undefined' ? opts.mouseWheelScrollSensitivity : 1), + fastScrollSensitivity: (typeof opts.fastScrollSensitivity !== 'undefined' ? opts.fastScrollSensitivity : 5), mouseWheelSmoothScroll: (typeof opts.mouseWheelSmoothScroll !== 'undefined' ? opts.mouseWheelSmoothScroll : true), arrowSize: (typeof opts.arrowSize !== 'undefined' ? opts.arrowSize : 11), diff --git a/src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts b/src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts index f8ed7e6fb11..7073bee8cb5 100644 --- a/src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts +++ b/src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts @@ -50,6 +50,11 @@ export interface ScrollableElementCreationOptions { * Defaults to 1. */ mouseWheelScrollSensitivity?: number; + /** + * FastScrolling mulitplier speed when pressing `Alt` + * Defaults to 5. + */ + fastScrollSensitivity?: number; /** * Height for vertical arrows (top/bottom) and width for horizontal arrows (left/right). * Defaults to 11. @@ -107,6 +112,7 @@ export interface ScrollableElementCreationOptions { export interface ScrollableElementChangeOptions { handleMouseWheel?: boolean; mouseWheelScrollSensitivity?: number; + fastScrollSensitivity: number; } export interface ScrollableElementResolvedOptions { @@ -118,6 +124,7 @@ export interface ScrollableElementResolvedOptions { scrollYToX: boolean; alwaysConsumeMouseWheel: boolean; mouseWheelScrollSensitivity: number; + fastScrollSensitivity: number; mouseWheelSmoothScroll: boolean; arrowSize: number; listenOnDomNode: HTMLElement | null; diff --git a/src/vs/base/browser/ui/splitview/panelview.ts b/src/vs/base/browser/ui/splitview/panelview.ts index 590bdd9d83d..0e19843ff3c 100644 --- a/src/vs/base/browser/ui/splitview/panelview.ts +++ b/src/vs/base/browser/ui/splitview/panelview.ts @@ -122,14 +122,16 @@ export abstract class Panel implements IView { return this._expanded; } - setExpanded(expanded: boolean): void { + setExpanded(expanded: boolean): boolean { if (this._expanded === !!expanded) { - return; + return false; } this._expanded = !!expanded; this.updateHeader(); this._onDidChange.fire(expanded ? this.expandedSize : undefined); + + return true; } get headerVisible(): boolean { diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index fac68e3b949..b0970cb21bb 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -514,6 +514,16 @@ export class AsyncDataTree implements IDisposable } private setChildren(node: IAsyncDataTreeNode, childrenElements: T[], recursive: boolean): void { + let nodeChildren: Map> | undefined; + + if (this.identityProvider) { + nodeChildren = new Map(); + + for (const child of node.children!) { + nodeChildren.set(child.id!, child); + } + } + const children = childrenElements.map>>(element => { if (!this.identityProvider) { return { @@ -527,14 +537,8 @@ export class AsyncDataTree implements IDisposable }; } - const nodeChildren = new Map>(); - - for (const child of node.children!) { - nodeChildren.set(child.id!, child); - } - const id = this.identityProvider.getId(element).toString(); - const asyncDataTreeNode = nodeChildren.get(id); + const asyncDataTreeNode = nodeChildren!.get(id); if (!asyncDataTreeNode) { return { diff --git a/src/vs/base/browser/ui/tree/dataTree.ts b/src/vs/base/browser/ui/tree/dataTree.ts index 2f628f57b62..f6a4006af84 100644 --- a/src/vs/base/browser/ui/tree/dataTree.ts +++ b/src/vs/base/browser/ui/tree/dataTree.ts @@ -20,9 +20,9 @@ export interface IDataTreeViewState { readonly collapsed: string[]; } -export class DataTree extends AbstractTree { +export class DataTree extends AbstractTree { - protected model: ObjectTreeModel; + protected model: ObjectTreeModel; private input: TInput | undefined; private identityProvider: IIdentityProvider | undefined; @@ -76,7 +76,7 @@ export class DataTree extends AbstractTree extends AbstractTree 0) { - const node = queue.shift(); + const node = queue.shift()!; if (node !== root && node.collapsed) { - collapsed.push(getId(node.element)); + collapsed.push(getId(node.element!)); } queue.push(...node.children); diff --git a/src/vs/base/common/filters.ts b/src/vs/base/common/filters.ts index 6592e10209d..03306c56029 100644 --- a/src/vs/base/common/filters.ts +++ b/src/vs/base/common/filters.ts @@ -476,6 +476,10 @@ function isWhitespaceAtPos(value: string, index: number): boolean { } } +function isUpperCaseAtPos(pos: number, word: string, wordLow: string): boolean { + return word[pos] !== wordLow[pos]; +} + function isPatternInWord(patternLow: string, patternPos: number, patternLen: number, wordLow: string, wordPos: number, wordLen: number): boolean { while (patternPos < patternLen && wordPos < wordLen) { if (patternLow[patternPos] === wordLow[wordPos]) { @@ -532,18 +536,19 @@ export function fuzzyScore(pattern: string, patternLow: string, patternPos: numb for (wordPos = 1; wordPos <= wordLen; wordPos++) { let score = -1; - let lowWordChar = wordLow[wordPos - 1]; - if (patternLow[patternPos - 1] === lowWordChar) { + if (patternLow[patternPos - 1] === wordLow[wordPos - 1]) { if (wordPos === (patternPos - patternStartPos)) { // common prefix: `foobar <-> foobaz` + // ^^^^^ if (pattern[patternPos - 1] === word[wordPos - 1]) { score = 7; } else { score = 5; } - } else if (lowWordChar !== word[wordPos - 1] && (wordPos === 1 || wordLow[wordPos - 2] === word[wordPos - 2])) { + } else if (isUpperCaseAtPos(wordPos - 1, word, wordLow) && (wordPos === 1 || !isUpperCaseAtPos(wordPos - 2, word, wordLow))) { // hitting upper-case: `foo <-> forOthers` + // ^^ ^ if (pattern[patternPos - 1] === word[wordPos - 1]) { score = 7; } else { @@ -551,6 +556,7 @@ export function fuzzyScore(pattern: string, patternLow: string, patternPos: numb } } else if (isSeparatorAtPos(wordLow, wordPos - 2) || isWhitespaceAtPos(wordLow, wordPos - 2)) { // post separator: `foo <-> bar_foo` + // ^^^ score = 5; } else { diff --git a/src/vs/base/common/labels.ts b/src/vs/base/common/labels.ts index c8fcc221958..6c46a5a5fd3 100644 --- a/src/vs/base/common/labels.ts +++ b/src/vs/base/common/labels.ts @@ -366,7 +366,7 @@ export function mnemonicMenuLabel(label: string, forceDisableMnemonics?: boolean /** * Handles mnemonics for buttons. Depending on OS: - * - Windows: Supported via & character (replace && with &) + * - Windows: Supported via & character (replace && with & and & with && for escaping) * - Linux: Supported via _ character (replace && with _) * - macOS: Unsupported (replace && with empty string) */ @@ -375,7 +375,11 @@ export function mnemonicButtonLabel(label: string): string { return label.replace(/\(&&\w\)|&&/g, ''); } - return label.replace(/&&/g, isWindows ? '&' : '_'); + if (isWindows) { + return label.replace(/&&|&/g, m => m === '&' ? '&&' : '&'); + } + + return label.replace(/&&/g, '_'); } export function unmnemonicLabel(label: string): string { diff --git a/src/vs/base/node/flow.ts b/src/vs/base/node/flow.ts index 6214d738c0a..c1232c2993b 100644 --- a/src/vs/base/node/flow.ts +++ b/src/vs/base/node/flow.ts @@ -9,7 +9,7 @@ import * as assert from 'assert'; * Executes the given function (fn) over the given array of items (list) in parallel and returns the resulting errors and results as * array to the callback (callback). The resulting errors and results are evaluated by calling the provided callback function. */ -export function parallel(list: T[], fn: (item: T, callback: (err: Error, result: E) => void) => void, callback: (err: Array | null, result: E[]) => void): void { +export function parallel(list: T[], fn: (item: T, callback: (err: Error | null, result: E | null) => void) => void, callback: (err: Array | null, result: E[]) => void): void { let results = new Array(list.length); let errors = new Array(list.length); let didErrorOccur = false; diff --git a/src/vs/base/parts/ipc/node/ipc.cp.ts b/src/vs/base/parts/ipc/node/ipc.cp.ts index 99bd89dbaf1..2217c707d22 100644 --- a/src/vs/base/parts/ipc/node/ipc.cp.ts +++ b/src/vs/base/parts/ipc/node/ipc.cp.ts @@ -196,11 +196,6 @@ export class Client implements IChannelClient, IDisposable { forkOpts.execArgv = ['--nolazy', '--inspect-brk=' + this.options.debugBrk]; } - if (!Array.isArray(forkOpts.execArgv)) { - forkOpts.execArgv = []; - } - forkOpts.execArgv.push('--no-untrusted-code-mitigations'); - this.child = fork(this.modulePath, args, forkOpts); const onMessageEmitter = new Emitter(); diff --git a/src/vs/base/parts/ipc/node/ipc.net.ts b/src/vs/base/parts/ipc/node/ipc.net.ts index 9a2235a92d8..6065b5134c7 100644 --- a/src/vs/base/parts/ipc/node/ipc.net.ts +++ b/src/vs/base/parts/ipc/node/ipc.net.ts @@ -239,7 +239,7 @@ export class Client extends IPCClient { get onClose(): Event { return this.protocol.onClose; } - constructor(private protocol: Protocol, id: TContext) { + constructor(private protocol: Protocol | BufferedProtocol, id: TContext) { super(protocol, id); } @@ -276,3 +276,68 @@ export function connect(hook: any, clientId: string): Promise { socket.once('error', e); }); } + +/** + * Will ensure no messages are lost if there are no event listeners. + */ +function createBufferedEvent(source: Event): Event { + let emitter: Emitter; + let hasListeners = false; + let isDeliveringMessages = false; + let bufferedMessages: T[] = []; + + const deliverMessages = () => { + if (isDeliveringMessages) { + return; + } + isDeliveringMessages = true; + while (hasListeners && bufferedMessages.length > 0) { + emitter.fire(bufferedMessages.shift()); + } + isDeliveringMessages = false; + }; + + source((e: T) => { + bufferedMessages.push(e); + deliverMessages(); + }); + + emitter = new Emitter({ + onFirstListenerAdd: () => { + hasListeners = true; + // it is important to deliver these messages after this call, but before + // other messages have a chance to be received (to guarantee in order delivery) + // that's why we're using here nextTick and not other types of timeouts + process.nextTick(deliverMessages); + }, + onLastListenerRemove: () => { + hasListeners = false; + } + }); + + return emitter.event; +} + +/** + * Will ensure no messages are lost if there are no event listeners. + */ +export class BufferedProtocol implements IMessagePassingProtocol { + + private readonly _actual: Protocol; + public readonly onMessage: Event; + public readonly onClose: Event; + + constructor(actual: Protocol) { + this._actual = actual; + this.onMessage = createBufferedEvent(this._actual.onMessage); + this.onClose = createBufferedEvent(this._actual.onClose); + } + + public send(buffer: Buffer): void { + this._actual.send(buffer); + } + + public end(): void { + this._actual.end(); + } +} diff --git a/src/vs/base/parts/quickopen/browser/quickOpenModel.ts b/src/vs/base/parts/quickopen/browser/quickOpenModel.ts index 189f85e4841..132f1207fac 100644 --- a/src/vs/base/parts/quickopen/browser/quickOpenModel.ts +++ b/src/vs/base/parts/quickopen/browser/quickOpenModel.ts @@ -468,7 +468,7 @@ class Renderer implements IRenderer { options.title = entry.getTooltip(); options.descriptionTitle = entry.getDescriptionTooltip() || entry.getDescription(); // tooltip over description because it could overflow options.descriptionMatches = descriptionHighlights || []; - data.label.setValue(entry.getLabel(), entry.getDescription(), options); + data.label.setLabel(entry.getLabel(), entry.getDescription(), options); // Meta data.detail.set(entry.getDetail(), detailHighlights); diff --git a/src/vs/base/parts/quickopen/test/common/quickOpenScorer.test.ts b/src/vs/base/parts/quickopen/test/common/quickOpenScorer.test.ts index 57fb24a9d13..dd317811cee 100644 --- a/src/vs/base/parts/quickopen/test/common/quickOpenScorer.test.ts +++ b/src/vs/base/parts/quickopen/test/common/quickOpenScorer.test.ts @@ -29,15 +29,15 @@ const ResourceAccessor = new ResourceAccessorClass(); class NullAccessorClass implements scorer.IItemAccessor { getItemLabel(resource: URI): string { - return void 0; + return undefined!; } getItemDescription(resource: URI): string { - return void 0; + return undefined!; } getItemPath(resource: URI): string { - return void 0; + return undefined!; } } @@ -120,52 +120,52 @@ suite('Quick Open Scorer', () => { // Path Identity const identityRes = scoreItem(resource, ResourceAccessor.getItemPath(resource), true, ResourceAccessor, cache); assert.ok(identityRes.score); - assert.equal(identityRes.descriptionMatch.length, 1); - assert.equal(identityRes.labelMatch.length, 1); - assert.equal(identityRes.descriptionMatch[0].start, 0); - assert.equal(identityRes.descriptionMatch[0].end, ResourceAccessor.getItemDescription(resource).length); - assert.equal(identityRes.labelMatch[0].start, 0); - assert.equal(identityRes.labelMatch[0].end, ResourceAccessor.getItemLabel(resource).length); + assert.equal(identityRes.descriptionMatch!.length, 1); + assert.equal(identityRes.labelMatch!.length, 1); + assert.equal(identityRes.descriptionMatch![0].start, 0); + assert.equal(identityRes.descriptionMatch![0].end, ResourceAccessor.getItemDescription(resource).length); + assert.equal(identityRes.labelMatch![0].start, 0); + assert.equal(identityRes.labelMatch![0].end, ResourceAccessor.getItemLabel(resource).length); // Basename Prefix const basenamePrefixRes = scoreItem(resource, 'som', true, ResourceAccessor, cache); assert.ok(basenamePrefixRes.score); assert.ok(!basenamePrefixRes.descriptionMatch); - assert.equal(basenamePrefixRes.labelMatch.length, 1); - assert.equal(basenamePrefixRes.labelMatch[0].start, 0); - assert.equal(basenamePrefixRes.labelMatch[0].end, 'som'.length); + assert.equal(basenamePrefixRes.labelMatch!.length, 1); + assert.equal(basenamePrefixRes.labelMatch![0].start, 0); + assert.equal(basenamePrefixRes.labelMatch![0].end, 'som'.length); // Basename Camelcase const basenameCamelcaseRes = scoreItem(resource, 'sF', true, ResourceAccessor, cache); assert.ok(basenameCamelcaseRes.score); assert.ok(!basenameCamelcaseRes.descriptionMatch); - assert.equal(basenameCamelcaseRes.labelMatch.length, 2); - assert.equal(basenameCamelcaseRes.labelMatch[0].start, 0); - assert.equal(basenameCamelcaseRes.labelMatch[0].end, 1); - assert.equal(basenameCamelcaseRes.labelMatch[1].start, 4); - assert.equal(basenameCamelcaseRes.labelMatch[1].end, 5); + assert.equal(basenameCamelcaseRes.labelMatch!.length, 2); + assert.equal(basenameCamelcaseRes.labelMatch![0].start, 0); + assert.equal(basenameCamelcaseRes.labelMatch![0].end, 1); + assert.equal(basenameCamelcaseRes.labelMatch![1].start, 4); + assert.equal(basenameCamelcaseRes.labelMatch![1].end, 5); // Basename Match const basenameRes = scoreItem(resource, 'of', true, ResourceAccessor, cache); assert.ok(basenameRes.score); assert.ok(!basenameRes.descriptionMatch); - assert.equal(basenameRes.labelMatch.length, 2); - assert.equal(basenameRes.labelMatch[0].start, 1); - assert.equal(basenameRes.labelMatch[0].end, 2); - assert.equal(basenameRes.labelMatch[1].start, 4); - assert.equal(basenameRes.labelMatch[1].end, 5); + assert.equal(basenameRes.labelMatch!.length, 2); + assert.equal(basenameRes.labelMatch![0].start, 1); + assert.equal(basenameRes.labelMatch![0].end, 2); + assert.equal(basenameRes.labelMatch![1].start, 4); + assert.equal(basenameRes.labelMatch![1].end, 5); // Path Match const pathRes = scoreItem(resource, 'xyz123', true, ResourceAccessor, cache); assert.ok(pathRes.score); assert.ok(pathRes.descriptionMatch); assert.ok(pathRes.labelMatch); - assert.equal(pathRes.labelMatch.length, 1); - assert.equal(pathRes.labelMatch[0].start, 8); - assert.equal(pathRes.labelMatch[0].end, 11); - assert.equal(pathRes.descriptionMatch.length, 1); - assert.equal(pathRes.descriptionMatch[0].start, 1); - assert.equal(pathRes.descriptionMatch[0].end, 4); + assert.equal(pathRes.labelMatch!.length, 1); + assert.equal(pathRes.labelMatch![0].start, 8); + assert.equal(pathRes.labelMatch![0].end, 11); + assert.equal(pathRes.descriptionMatch!.length, 1); + assert.equal(pathRes.descriptionMatch![0].start, 1); + assert.equal(pathRes.descriptionMatch![0].end, 4); // No Match const noRes = scoreItem(resource, '987', true, ResourceAccessor, cache); @@ -182,7 +182,7 @@ suite('Quick Open Scorer', () => { test('scoreItem - invalid input', function () { - let res = scoreItem(null, null, true, ResourceAccessor, cache); + let res = scoreItem(null, null!, true, ResourceAccessor, cache); assert.equal(res.score, 0); res = scoreItem(null, 'null', true, ResourceAccessor, cache); @@ -199,12 +199,12 @@ suite('Quick Open Scorer', () => { assert.ok(pathRes.score); assert.ok(pathRes.descriptionMatch); assert.ok(pathRes.labelMatch); - assert.equal(pathRes.labelMatch.length, 1); - assert.equal(pathRes.labelMatch[0].start, 0); - assert.equal(pathRes.labelMatch[0].end, 7); - assert.equal(pathRes.descriptionMatch.length, 1); - assert.equal(pathRes.descriptionMatch[0].start, 23); - assert.equal(pathRes.descriptionMatch[0].end, 26); + assert.equal(pathRes.labelMatch!.length, 1); + assert.equal(pathRes.labelMatch![0].start, 0); + assert.equal(pathRes.labelMatch![0].end, 7); + assert.equal(pathRes.descriptionMatch!.length, 1); + assert.equal(pathRes.descriptionMatch![0].start, 23); + assert.equal(pathRes.descriptionMatch![0].end, 26); }); test('scoreItem - avoid match scattering (bug #36119)', function () { @@ -214,9 +214,9 @@ suite('Quick Open Scorer', () => { assert.ok(pathRes.score); assert.ok(pathRes.descriptionMatch); assert.ok(pathRes.labelMatch); - assert.equal(pathRes.labelMatch.length, 1); - assert.equal(pathRes.labelMatch[0].start, 0); - assert.equal(pathRes.labelMatch[0].end, 9); + assert.equal(pathRes.labelMatch!.length, 1); + assert.equal(pathRes.labelMatch![0].start, 0); + assert.equal(pathRes.labelMatch![0].end, 9); }); test('scoreItem - prefers more compact matches', function () { @@ -227,12 +227,12 @@ suite('Quick Open Scorer', () => { const res = scoreItem(resource, 'ad', true, ResourceAccessor, cache); assert.ok(res.score); assert.ok(res.descriptionMatch); - assert.ok(!res.labelMatch.length); - assert.equal(res.descriptionMatch.length, 2); - assert.equal(res.descriptionMatch[0].start, 11); - assert.equal(res.descriptionMatch[0].end, 12); - assert.equal(res.descriptionMatch[1].start, 13); - assert.equal(res.descriptionMatch[1].end, 14); + assert.ok(!res.labelMatch!.length); + assert.equal(res.descriptionMatch!.length, 2); + assert.equal(res.descriptionMatch![0].start, 11); + assert.equal(res.descriptionMatch![0].end, 12); + assert.equal(res.descriptionMatch![1].start, 13); + assert.equal(res.descriptionMatch![1].end, 14); }); test('scoreItem - proper target offset', function () { @@ -247,9 +247,9 @@ suite('Quick Open Scorer', () => { const res = scoreItem(resource, 'de', true, ResourceAccessor, cache); - assert.equal(res.labelMatch.length, 1); - assert.equal(res.labelMatch[0].start, 1); - assert.equal(res.labelMatch[0].end, 3); + assert.equal(res.labelMatch!.length, 1); + assert.equal(res.labelMatch![0].start, 1); + assert.equal(res.labelMatch![0].end, 3); }); test('scoreItem - proper target offset #3', function () { @@ -257,19 +257,19 @@ suite('Quick Open Scorer', () => { const res = scoreItem(resource, 'debug', true, ResourceAccessor, cache); - assert.equal(res.descriptionMatch.length, 3); - assert.equal(res.descriptionMatch[0].start, 9); - assert.equal(res.descriptionMatch[0].end, 10); - assert.equal(res.descriptionMatch[1].start, 36); - assert.equal(res.descriptionMatch[1].end, 37); - assert.equal(res.descriptionMatch[2].start, 40); - assert.equal(res.descriptionMatch[2].end, 41); + assert.equal(res.descriptionMatch!.length, 3); + assert.equal(res.descriptionMatch![0].start, 9); + assert.equal(res.descriptionMatch![0].end, 10); + assert.equal(res.descriptionMatch![1].start, 36); + assert.equal(res.descriptionMatch![1].end, 37); + assert.equal(res.descriptionMatch![2].start, 40); + assert.equal(res.descriptionMatch![2].end, 41); - assert.equal(res.labelMatch.length, 2); - assert.equal(res.labelMatch[0].start, 9); - assert.equal(res.labelMatch[0].end, 10); - assert.equal(res.labelMatch[1].start, 20); - assert.equal(res.labelMatch[1].end, 21); + assert.equal(res.labelMatch!.length, 2); + assert.equal(res.labelMatch![0].start, 9); + assert.equal(res.labelMatch![0].end, 10); + assert.equal(res.labelMatch![1].start, 20); + assert.equal(res.labelMatch![1].end, 21); }); test('scoreItem - no match unless query contained in sequence', function () { diff --git a/src/vs/base/parts/tree/browser/treeView.ts b/src/vs/base/parts/tree/browser/treeView.ts index 1ccb257ae35..1c96a2627e4 100644 --- a/src/vs/base/parts/tree/browser/treeView.ts +++ b/src/vs/base/parts/tree/browser/treeView.ts @@ -454,13 +454,13 @@ export class TreeView extends HeightMap { private highlightedItemWasDraggable: boolean; private onHiddenScrollTop: number; - private readonly _onDOMFocus: Emitter = new Emitter(); + private readonly _onDOMFocus = new Emitter(); get onDOMFocus(): Event { return this._onDOMFocus.event; } - private readonly _onDOMBlur: Emitter = new Emitter(); + private readonly _onDOMBlur = new Emitter(); get onDOMBlur(): Event { return this._onDOMBlur.event; } - private readonly _onDidScroll: Emitter = new Emitter(); + private readonly _onDidScroll = new Emitter(); get onDidScroll(): Event { return this._onDidScroll.event; } constructor(context: _.ITreeContext, container: HTMLElement) { diff --git a/src/vs/base/test/browser/ui/grid/gridview.test.ts b/src/vs/base/test/browser/ui/grid/gridview.test.ts index 3485fa63b31..3d000d85a67 100644 --- a/src/vs/base/test/browser/ui/grid/gridview.test.ts +++ b/src/vs/base/test/browser/ui/grid/gridview.test.ts @@ -21,10 +21,6 @@ suite('Gridview', function () { container.appendChild(gridview.element); }); - teardown(function () { - gridview = null; - }); - test('empty gridview is empty', function () { assert.deepEqual(nodesToArrays(gridview.getViews()), []); gridview.dispose(); diff --git a/src/vs/base/test/browser/ui/splitview/splitview.test.ts b/src/vs/base/test/browser/ui/splitview/splitview.test.ts index 185c9f25aa4..8ca22616e41 100644 --- a/src/vs/base/test/browser/ui/splitview/splitview.test.ts +++ b/src/vs/base/test/browser/ui/splitview/splitview.test.ts @@ -72,13 +72,9 @@ suite('Splitview', () => { container.style.height = `${200}px`; }); - teardown(() => { - container = null; - }); - test('empty splitview has empty DOM', () => { const splitview = new SplitView(container); - assert.equal(container.firstElementChild.firstElementChild.childElementCount, 0, 'split view should be empty'); + assert.equal(container.firstElementChild!.firstElementChild!.childElementCount, 0, 'split view should be empty'); splitview.dispose(); }); diff --git a/src/vs/base/test/browser/ui/tree/asyncDataTree.test.ts b/src/vs/base/test/browser/ui/tree/asyncDataTree.test.ts index 670254527a5..7e0fc7b847f 100644 --- a/src/vs/base/test/browser/ui/tree/asyncDataTree.test.ts +++ b/src/vs/base/test/browser/ui/tree/asyncDataTree.test.ts @@ -53,7 +53,7 @@ suite('AsyncDataTree', function () { const dataSource = new class implements IAsyncDataSource { hasChildren(element: Element): boolean { - return element.children && element.children.length > 0; + return !!element.children && element.children.length > 0; } getChildren(element: Element): Promise { return Promise.resolve(element.children || []); @@ -91,14 +91,14 @@ suite('AsyncDataTree', function () { { id: 'ac' } ]; - await tree.refresh(null); + await tree.refresh(root); assert.equal(container.querySelectorAll('.monaco-list-row').length, 1); await tree.expand(_('a')); assert.equal(container.querySelectorAll('.monaco-list-row').length, 4); _('a').children = []; - await tree.refresh(null); + await tree.refresh(root); assert.equal(container.querySelectorAll('.monaco-list-row').length, 1); }); }); \ No newline at end of file diff --git a/src/vs/base/test/common/arrays.test.ts b/src/vs/base/test/common/arrays.test.ts index dbc2162bc1d..38a235ea871 100644 --- a/src/vs/base/test/common/arrays.test.ts +++ b/src/vs/base/test/common/arrays.test.ts @@ -262,7 +262,7 @@ suite('Arrays', () => { } test('coalesce', () => { - let a = arrays.coalesce([null, 1, null, 2, 3]); + let a: Array = arrays.coalesce([null, 1, null, 2, 3]); assert.equal(a.length, 3); assert.equal(a[0], 1); assert.equal(a[1], 2); @@ -298,7 +298,7 @@ suite('Arrays', () => { }); test('coalesce - inplace', function () { - let a = [null, 1, null, 2, 3]; + let a: Array = [null, 1, null, 2, 3]; arrays.coalesceInPlace(a); assert.equal(a.length, 3); assert.equal(a[0], 1); diff --git a/src/vs/base/test/common/async.test.ts b/src/vs/base/test/common/async.test.ts index 76257d43640..f354e5edc22 100644 --- a/src/vs/base/test/common/async.test.ts +++ b/src/vs/base/test/common/async.test.ts @@ -522,7 +522,7 @@ suite('Async', () => { assert.ok(r2Queue); assert.equal(r1Queue, queue.queueFor(URI.file('/some/path'))); // same queue returned - let syncPromiseFactory = () => Promise.resolve(null); + let syncPromiseFactory = () => Promise.resolve(undefined); r1Queue.queue(syncPromiseFactory); diff --git a/src/vs/base/test/common/diff/diff.test.ts b/src/vs/base/test/common/diff/diff.test.ts index 70fe3acb597..3628f1e53e3 100644 --- a/src/vs/base/test/common/diff/diff.test.ts +++ b/src/vs/base/test/common/diff/diff.test.ts @@ -21,7 +21,7 @@ class StringDiffSequence implements ISequence { } function createArray(length: number, value: T): T[] { - var r = []; + const r: T[] = []; for (var i = 0; i < length; i++) { r[i] = value; } diff --git a/src/vs/base/test/common/event.test.ts b/src/vs/base/test/common/event.test.ts index 35bae3206a9..b042b60fe83 100644 --- a/src/vs/base/test/common/event.test.ts +++ b/src/vs/base/test/common/event.test.ts @@ -71,7 +71,7 @@ suite('Event', function () { // unhook listener while (bucket.length) { - bucket.pop().dispose(); + bucket.pop()!.dispose(); } // noop diff --git a/src/vs/base/test/common/history.test.ts b/src/vs/base/test/common/history.test.ts index b5d274656fb..9c93a0adeb5 100644 --- a/src/vs/base/test/common/history.test.ts +++ b/src/vs/base/test/common/history.test.ts @@ -113,8 +113,8 @@ suite('History Navigator', () => { assert.equal(testObject.current(), undefined); }); - function toArray(historyNavigator: HistoryNavigator): string[] { - let result: string[] = []; + function toArray(historyNavigator: HistoryNavigator): Array { + let result: Array = []; historyNavigator.first(); if (historyNavigator.current()) { do { diff --git a/src/vs/base/test/common/labels.test.ts b/src/vs/base/test/common/labels.test.ts index fa619b4780b..a3e54c16232 100644 --- a/src/vs/base/test/common/labels.test.ts +++ b/src/vs/base/test/common/labels.test.ts @@ -164,4 +164,19 @@ suite('Labels', () => { assert.equal(labels.getBaseLabel('c:\\some\\folder\\file.txt'), 'file.txt'); assert.equal(labels.getBaseLabel('c:\\some\\folder'), 'folder'); }); + + test('mnemonicButtonLabel', () => { + assert.equal(labels.mnemonicButtonLabel('Hello World'), 'Hello World'); + assert.equal(labels.mnemonicButtonLabel(''), ''); + if (platform.isWindows) { + assert.equal(labels.mnemonicButtonLabel('Hello & World'), 'Hello && World'); + assert.equal(labels.mnemonicButtonLabel('Do &¬ Save & Continue'), 'Do ¬ Save && Continue'); + } else if (platform.isMacintosh) { + assert.equal(labels.mnemonicButtonLabel('Hello & World'), 'Hello & World'); + assert.equal(labels.mnemonicButtonLabel('Do &¬ Save & Continue'), 'Do not Save & Continue'); + } else { + assert.equal(labels.mnemonicButtonLabel('Hello & World'), 'Hello & World'); + assert.equal(labels.mnemonicButtonLabel('Do &¬ Save & Continue'), 'Do _not Save & Continue'); + } + }); }); \ No newline at end of file diff --git a/src/vs/base/test/common/map.test.ts b/src/vs/base/test/common/map.test.ts index 25c41d54daa..c179bdb34db 100644 --- a/src/vs/base/test/common/map.test.ts +++ b/src/vs/base/test/common/map.test.ts @@ -140,7 +140,7 @@ suite('Map', () => { assert.strictEqual(cache.size, 5); assert.deepStrictEqual(cache.keys(), [3, 4, 5, 6, 7]); let values: number[] = []; - [3, 4, 5, 6, 7].forEach(key => values.push(cache.get(key))); + [3, 4, 5, 6, 7].forEach(key => values.push(cache.get(key)!)); assert.deepStrictEqual(values, [3, 4, 5, 6, 7]); }); @@ -155,7 +155,7 @@ suite('Map', () => { cache.peek(4); assert.deepStrictEqual(cache.keys(), [1, 2, 4, 5, 3]); let values: number[] = []; - [1, 2, 3, 4, 5].forEach(key => values.push(cache.get(key))); + [1, 2, 3, 4, 5].forEach(key => values.push(cache.get(key)!)); assert.deepStrictEqual(values, [1, 2, 3, 4, 5]); }); @@ -177,7 +177,7 @@ suite('Map', () => { assert.deepEqual(cache.size, 15); let values: number[] = []; for (let i = 6; i <= 20; i++) { - values.push(cache.get(i)); + values.push(cache.get(i)!); assert.strictEqual(cache.get(i), i); } assert.deepStrictEqual(cache.values(), values); @@ -194,7 +194,7 @@ suite('Map', () => { assert.strictEqual(cache.size, 5); assert.deepStrictEqual(cache.keys(), [7, 8, 9, 10, 11]); let values: number[] = []; - cache.keys().forEach(key => values.push(cache.get(key))); + cache.keys().forEach(key => values.push(cache.get(key)!)); assert.deepStrictEqual(values, [7, 8, 9, 10, 11]); assert.deepStrictEqual(cache.values(), values); }); @@ -588,7 +588,7 @@ suite('Map', () => { test('mapToSerializable / serializableToMap', function () { const map = new Map(); map.set('1', 'foo'); - map.set('2', null); + map.set('2', null!); map.set('3', 'bar'); const map2 = serializableToMap(mapToSerializable(map)); diff --git a/src/vs/base/test/common/octicon.test.ts b/src/vs/base/test/common/octicon.test.ts index 9135c38e1e2..c91055525d2 100644 --- a/src/vs/base/test/common/octicon.test.ts +++ b/src/vs/base/test/common/octicon.test.ts @@ -8,7 +8,7 @@ import { matchesFuzzyOcticonAware, parseOcticons } from 'vs/base/common/octicon' export interface IOcticonFilter { // Returns null if word doesn't match. - (query: string, target: { text: string, octiconOffsets?: number[] }): IMatch[]; + (query: string, target: { text: string, octiconOffsets?: number[] }): IMatch[] | null; } function filterOk(filter: IOcticonFilter, word: string, target: { text: string, octiconOffsets?: number[] }, highlights?: { start: number; end: number; }[]) { diff --git a/src/vs/base/test/common/strings.test.ts b/src/vs/base/test/common/strings.test.ts index 2e8ee07797b..cee9c7a1b55 100644 --- a/src/vs/base/test/common/strings.test.ts +++ b/src/vs/base/test/common/strings.test.ts @@ -320,7 +320,7 @@ suite('Strings', () => { }); test('fuzzyContains', () => { - assert.ok(!strings.fuzzyContains(void 0, null)); + assert.ok(!strings.fuzzyContains((void 0)!, null!)); assert.ok(strings.fuzzyContains('hello world', 'h')); assert.ok(!strings.fuzzyContains('hello world', 'q')); assert.ok(strings.fuzzyContains('hello world', 'hw')); diff --git a/src/vs/base/test/common/uri.test.ts b/src/vs/base/test/common/uri.test.ts index 77351bee75e..44b26dfe7b1 100644 --- a/src/vs/base/test/common/uri.test.ts +++ b/src/vs/base/test/common/uri.test.ts @@ -91,9 +91,9 @@ suite('URI', () => { test('with, identity', () => { let uri = URI.parse('foo:bar/path'); - let uri2 = uri.with(null); + let uri2 = uri.with(null!); assert.ok(uri === uri2); - uri2 = uri.with(undefined); + uri2 = uri.with(undefined!); assert.ok(uri === uri2); uri2 = uri.with({}); assert.ok(uri === uri2); diff --git a/src/vs/base/test/node/config.test.ts b/src/vs/base/test/node/config.test.ts index ee4f4e38c74..59df2e0d36f 100644 --- a/src/vs/base/test/node/config.test.ts +++ b/src/vs/base/test/node/config.test.ts @@ -141,7 +141,7 @@ suite('Config', () => { testFile('config', 'config.json').then(res => { fs.writeFileSync(res.testFile, '// my comment\n{ "foo": "bar" }'); - let watcher = new ConfigWatcher<{ foo: string; }>(res.testFile, { changeBufferDelay: 100, onError: console.error, defaultConfig: void 0 }); + let watcher = new ConfigWatcher<{ foo: string; }>(res.testFile, { changeBufferDelay: 100, onError: console.error, defaultConfig: { foo: 'bar' } }); watcher.getConfig(); // ensure we are in sync fs.writeFileSync(res.testFile, '// my comment\n{ "foo": "changed" }'); diff --git a/src/vs/base/test/node/extfs/extfs.test.ts b/src/vs/base/test/node/extfs/extfs.test.ts index 4031d3080da..b540aab4aae 100644 --- a/src/vs/base/test/node/extfs/extfs.test.ts +++ b/src/vs/base/test/node/extfs/extfs.test.ts @@ -38,7 +38,7 @@ function toReadable(value: string, throwError?: boolean): Readable { this.emit('error', new Error(readError)); } - let res: string; + let res!: string; let canPush = true; while (canPush && (res = stringChunks[counter++])) { canPush = this.push(res); @@ -96,14 +96,14 @@ suite('Extfs', () => { return done(error); } - assert.ok(!statAndIsLink.isSymbolicLink); + assert.ok(!statAndIsLink!.isSymbolicLink); extfs.statLink(symbolicLink, (error, statAndIsLink) => { if (error) { return done(error); } - assert.ok(statAndIsLink.isSymbolicLink); + assert.ok(statAndIsLink!.isSymbolicLink); extfs.delSync(directory); done(); }); @@ -258,7 +258,7 @@ suite('Extfs', () => { assert.ok(fs.existsSync(newDir)); - extfs.writeFileAndFlush(testFile, 'Hello World', null, error => { + extfs.writeFileAndFlush(testFile, 'Hello World', null!, error => { if (error) { return done(error); } @@ -267,7 +267,7 @@ suite('Extfs', () => { const largeString = (new Array(100 * 1024)).join('Large String\n'); - extfs.writeFileAndFlush(testFile, largeString, null, error => { + extfs.writeFileAndFlush(testFile, largeString, null!, error => { if (error) { return done(error); } @@ -293,7 +293,7 @@ suite('Extfs', () => { assert.ok(fs.existsSync(newDir)); - extfs.writeFileAndFlush(testFile, toReadable('Hello World'), null, error => { + extfs.writeFileAndFlush(testFile, toReadable('Hello World'), null!, error => { if (error) { return done(error); } @@ -302,7 +302,7 @@ suite('Extfs', () => { const largeString = (new Array(100 * 1024)).join('Large String\n'); - extfs.writeFileAndFlush(testFile, toReadable(largeString), null, error => { + extfs.writeFileAndFlush(testFile, toReadable(largeString), null!, error => { if (error) { return done(error); } @@ -329,7 +329,7 @@ suite('Extfs', () => { assert.ok(fs.existsSync(newDir)); - extfs.writeFileAndFlush(testFile, fs.createReadStream(sourceFile), null, error => { + extfs.writeFileAndFlush(testFile, fs.createReadStream(sourceFile), null!, error => { if (error) { return done(error); } @@ -356,7 +356,7 @@ suite('Extfs', () => { fs.mkdirSync(testFile); // this will trigger an error because testFile is now a directory! - extfs.writeFileAndFlush(testFile, 'Hello World', null, error => { + extfs.writeFileAndFlush(testFile, 'Hello World', null!, error => { if (!error) { return done(new Error('Expected error for writing to readonly file')); } @@ -382,7 +382,7 @@ suite('Extfs', () => { fs.mkdirSync(testFile); // this will trigger an error because testFile is now a directory! const readable = toReadable('Hello World'); - extfs.writeFileAndFlush(testFile, readable, null, error => { + extfs.writeFileAndFlush(testFile, readable, null!, error => { if (!error || (error).code !== 'EISDIR') { return done(new Error('Expected EISDIR error for writing to folder but got: ' + (error ? (error).code : 'no error'))); } @@ -408,7 +408,7 @@ suite('Extfs', () => { assert.ok(fs.existsSync(newDir)); - extfs.writeFileAndFlush(testFile, toReadable('Hello World', true /* throw error */), null, error => { + extfs.writeFileAndFlush(testFile, toReadable('Hello World', true /* throw error */), null!, error => { if (!error || error.message !== readError) { return done(new Error('Expected error for writing to folder')); } @@ -438,7 +438,7 @@ suite('Extfs', () => { fs.writeFileSync(testFile, ''); fs.chmodSync(testFile, 33060); // make readonly - extfs.writeFileAndFlush(testFile, toReadable('Hello World'), null, error => { + extfs.writeFileAndFlush(testFile, toReadable('Hello World'), null!, error => { if (!error || !((error).code !== 'EACCES' || (error).code !== 'EPERM')) { return done(new Error('Expected EACCES/EPERM error for writing to folder but got: ' + (error ? (error).code : 'no error'))); } @@ -464,7 +464,7 @@ suite('Extfs', () => { fs.mkdirSync(testFile); // this will trigger an error because testFile is now a directory! - extfs.writeFileAndFlush(testFile, fs.createReadStream(sourceFile), null, error => { + extfs.writeFileAndFlush(testFile, fs.createReadStream(sourceFile), null!, error => { if (!error) { return done(new Error('Expected error for writing to folder')); } @@ -487,12 +487,12 @@ suite('Extfs', () => { assert.ok(fs.existsSync(newDir)); - extfs.writeFileAndFlushSync(testFile, 'Hello World', null); + extfs.writeFileAndFlushSync(testFile, 'Hello World', null!); assert.equal(fs.readFileSync(testFile), 'Hello World'); const largeString = (new Array(100 * 1024)).join('Large String\n'); - extfs.writeFileAndFlushSync(testFile, largeString, null); + extfs.writeFileAndFlushSync(testFile, largeString, null!); assert.equal(fs.readFileSync(testFile), largeString); extfs.del(parentDir, os.tmpdir(), done, ignore); @@ -550,7 +550,7 @@ suite('Extfs', () => { const newDir = path.join(parentDir, 'extfs', id); mkdirp(newDir, 493, error => { - let realpath: string; + let realpath!: string; try { realpath = extfs.realpathSync(newDir); } catch (error) { diff --git a/src/vs/base/test/node/glob.test.ts b/src/vs/base/test/node/glob.test.ts index 4f851a4ca4f..7b003cc002b 100644 --- a/src/vs/base/test/node/glob.test.ts +++ b/src/vs/base/test/node/glob.test.ts @@ -738,24 +738,24 @@ suite('Glob', () => { }); test('falsy expression/pattern', function () { - assert.strictEqual(glob.match(null, 'foo'), false); + assert.strictEqual(glob.match(null!, 'foo'), false); assert.strictEqual(glob.match('', 'foo'), false); - assert.strictEqual(glob.parse(null)('foo'), false); + assert.strictEqual(glob.parse(null!)('foo'), false); assert.strictEqual(glob.parse('')('foo'), false); }); test('falsy path', function () { - assert.strictEqual(glob.parse('foo')(null), false); + assert.strictEqual(glob.parse('foo')(null!), false); assert.strictEqual(glob.parse('foo')(''), false); - assert.strictEqual(glob.parse('**/*.j?')(null), false); + assert.strictEqual(glob.parse('**/*.j?')(null!), false); assert.strictEqual(glob.parse('**/*.j?')(''), false); - assert.strictEqual(glob.parse('**/*.foo')(null), false); + assert.strictEqual(glob.parse('**/*.foo')(null!), false); assert.strictEqual(glob.parse('**/*.foo')(''), false); - assert.strictEqual(glob.parse('**/foo')(null), false); + assert.strictEqual(glob.parse('**/foo')(null!), false); assert.strictEqual(glob.parse('**/foo')(''), false); - assert.strictEqual(glob.parse('{**/baz,**/foo}')(null), false); + assert.strictEqual(glob.parse('{**/baz,**/foo}')(null!), false); assert.strictEqual(glob.parse('{**/baz,**/foo}')(''), false); - assert.strictEqual(glob.parse('{**/*.baz,**/*.foo}')(null), false); + assert.strictEqual(glob.parse('{**/*.baz,**/*.foo}')(null!), false); assert.strictEqual(glob.parse('{**/*.baz,**/*.foo}')(''), false); }); @@ -808,7 +808,7 @@ suite('Glob', () => { }, ['foo', 'bar', 'baz'], [ ['bar/foo', '**/foo/**'], ['foo/bar', '{**/bar/**,**/baz/**}'], - ['bar/nope', null] + ['bar/nope', null!] ]); const siblings = ['baz', 'baz.zip', 'nope']; @@ -817,12 +817,12 @@ suite('Glob', () => { '**/foo/**': { when: '$(basename).zip' }, '**/bar/**': true }, ['bar'], [ - ['bar/foo', null], - ['bar/foo/baz', null], - ['bar/foo/nope', null], + ['bar/foo', null!], + ['bar/foo/baz', null!], + ['bar/foo/nope', null!], ['foo/bar', '**/bar/**'], ], [ - null, + null!, hasSibling, hasSibling ]); @@ -832,7 +832,7 @@ suite('Glob', () => { const parsed = glob.parse(pattern, { trimForExclusions: true }); assert.deepStrictEqual(glob.getBasenameTerms(parsed), basenameTerms); matches.forEach(([text, result], i) => { - assert.strictEqual(parsed(text, null, siblingsFns[i]), result); + assert.strictEqual(parsed(text, null!, siblingsFns[i]), result); }); } @@ -914,7 +914,7 @@ suite('Glob', () => { [nativeSep('bar/foo/bar'), '**/foo/bar/**'], // Not supported // [nativeSep('foo/bar/bar'), '{**/bar/bar/**,**/baz/bar/**}'], - [nativeSep('/foo/bar/nope'), null] + [nativeSep('/foo/bar/nope'), null!] ]); const siblings = ['baz', 'baz.zip', 'nope']; @@ -923,12 +923,12 @@ suite('Glob', () => { '**/foo/123/**': { when: '$(basename).zip' }, '**/bar/123/**': true }, ['*/bar/123'], [ - [nativeSep('bar/foo/123'), null], - [nativeSep('bar/foo/123/baz'), null], - [nativeSep('bar/foo/123/nope'), null], + [nativeSep('bar/foo/123'), null!], + [nativeSep('bar/foo/123/baz'), null!], + [nativeSep('bar/foo/123/nope'), null!], [nativeSep('foo/bar/123'), '**/bar/123/**'], ], [ - null, + null!, hasSibling, hasSibling ]); @@ -938,7 +938,7 @@ suite('Glob', () => { const parsed = glob.parse(pattern, { trimForExclusions: true }); assert.deepStrictEqual(glob.getPathTerms(parsed), pathTerms); matches.forEach(([text, result], i) => { - assert.strictEqual(parsed(text, null, siblingsFns[i]), result); + assert.strictEqual(parsed(text, null!, siblingsFns[i]), result); }); } diff --git a/src/vs/base/test/node/pfs.test.ts b/src/vs/base/test/node/pfs.test.ts index 6edeabd4d58..172ef642d5d 100644 --- a/src/vs/base/test/node/pfs.test.ts +++ b/src/vs/base/test/node/pfs.test.ts @@ -24,7 +24,7 @@ suite('PFS', () => { return pfs.mkdirp(newDir, 493).then(() => { assert.ok(fs.existsSync(newDir)); - return pfs.writeFile(testFile, 'Hello World', null).then(() => { + return pfs.writeFile(testFile, 'Hello World', null!).then(() => { assert.equal(fs.readFileSync(testFile), 'Hello World'); return pfs.del(parentDir, os.tmpdir()); @@ -46,11 +46,11 @@ suite('PFS', () => { assert.ok(fs.existsSync(newDir)); return Promise.all([ - pfs.writeFile(testFile1, 'Hello World 1', null), - pfs.writeFile(testFile2, 'Hello World 2', null), - pfs.writeFile(testFile3, 'Hello World 3', null), - pfs.writeFile(testFile4, 'Hello World 4', null), - pfs.writeFile(testFile5, 'Hello World 5', null) + pfs.writeFile(testFile1, 'Hello World 1', null!), + pfs.writeFile(testFile2, 'Hello World 2', null!), + pfs.writeFile(testFile3, 'Hello World 3', null!), + pfs.writeFile(testFile4, 'Hello World 4', null!), + pfs.writeFile(testFile5, 'Hello World 5', null!) ]).then(() => { assert.equal(fs.readFileSync(testFile1), 'Hello World 1'); assert.equal(fs.readFileSync(testFile2), 'Hello World 2'); diff --git a/src/vs/base/test/node/storage/storage.test.ts b/src/vs/base/test/node/storage/storage.test.ts index 0acbb97461c..134379de4ab 100644 --- a/src/vs/base/test/node/storage/storage.test.ts +++ b/src/vs/base/test/node/storage/storage.test.ts @@ -99,7 +99,7 @@ suite('Storage Library', () => { await mkdirp(storageDir); class TestSQLiteStorageDatabase extends SQLiteStorageDatabase { - private _onDidChangeItemsExternal: Emitter = new Emitter(); + private _onDidChangeItemsExternal = new Emitter(); get onDidChangeItemsExternal(): Event { return this._onDidChangeItemsExternal.event; } fireDidChangeItemsExternal(event: IStorageItemsChangeEvent): void { diff --git a/src/vs/code/electron-browser/sharedProcess/contrib/contributions.ts b/src/vs/code/electron-browser/sharedProcess/contrib/contributions.ts index 6784a68ebd9..03f038fc1fb 100644 --- a/src/vs/code/electron-browser/sharedProcess/contrib/contributions.ts +++ b/src/vs/code/electron-browser/sharedProcess/contrib/contributions.ts @@ -8,11 +8,13 @@ import { LanguagePackCachedDataCleaner } from 'vs/code/electron-browser/sharedPr import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IDisposable, combinedDisposable } from 'vs/base/common/lifecycle'; import { StorageDataCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/storageDataCleaner'; +import { LogsDataCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/logsDataCleaner'; export function createSharedProcessContributions(service: IInstantiationService): IDisposable { return combinedDisposable([ service.createInstance(NodeCachedDataCleaner), service.createInstance(LanguagePackCachedDataCleaner), - service.createInstance(StorageDataCleaner) + service.createInstance(StorageDataCleaner), + service.createInstance(LogsDataCleaner) ]); } diff --git a/src/vs/code/electron-browser/sharedProcess/contrib/logsDataCleaner.ts b/src/vs/code/electron-browser/sharedProcess/contrib/logsDataCleaner.ts new file mode 100644 index 00000000000..2cf74b7bfa0 --- /dev/null +++ b/src/vs/code/electron-browser/sharedProcess/contrib/logsDataCleaner.ts @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { join, dirname, basename } from 'path'; +import { readdir, rimraf } from 'vs/base/node/pfs'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; + +export class LogsDataCleaner extends Disposable { + + constructor( + @IEnvironmentService private environmentService: IEnvironmentService + ) { + super(); + + this.cleanUpOldLogsSoon(); + } + + private cleanUpOldLogsSoon(): void { + let handle: any = setTimeout(() => { + handle = void 0; + + const currentLog = basename(this.environmentService.logsPath); + const logsRoot = dirname(this.environmentService.logsPath); + + readdir(logsRoot).then(children => { + const allSessions = children.filter(name => /^\d{8}T\d{6}$/.test(name)); + const oldSessions = allSessions.sort().filter((d, i) => d !== currentLog); + const toDelete = oldSessions.slice(0, Math.max(0, oldSessions.length - 9)); + + return Promise.all(toDelete.map(name => rimraf(join(logsRoot, name)))); + }).then(null, onUnexpectedError); + }, 10 * 1000); + + this._register(toDisposable(() => clearTimeout(handle))); + } +} diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index b25ad242115..205c1ee3e67 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { app, ipcMain as ipc, systemPreferences, shell, Event, contentTracing, protocol } from 'electron'; -import * as platform from 'vs/base/common/platform'; +import { IProcessEnvironment, isWindows, isMacintosh } from 'vs/base/common/platform'; import { WindowsManager } from 'vs/code/electron-main/windows'; import { IWindowsService, OpenContext, ActiveWindowManager } from 'vs/platform/windows/common/windows'; import { WindowsChannel } from 'vs/platform/windows/node/windowsIpc'; @@ -63,7 +63,7 @@ import { MenubarChannel } from 'vs/platform/menubar/node/menubarIpc'; import { hasArgs } from 'vs/platform/environment/node/argv'; import { RunOnceScheduler } from 'vs/base/common/async'; import { registerContextMenuListener } from 'vs/base/parts/contextmenu/electron-main/contextmenu'; -import { THEME_STORAGE_KEY, THEME_BG_STORAGE_KEY } from 'vs/code/electron-main/theme'; +import { storeBackgroundColor } from 'vs/code/electron-main/theme'; import { nativeSep, join } from 'vs/base/common/paths'; import { homedir } from 'os'; import { localize } from 'vs/nls'; @@ -74,6 +74,12 @@ import { SnapUpdateService } from 'vs/platform/update/electron-main/updateServic import { IStorageMainService, StorageMainService } from 'vs/platform/storage/node/storageMainService'; import { GlobalStorageDatabaseChannel } from 'vs/platform/storage/node/storageIpc'; import { generateUuid } from 'vs/base/common/uuid'; +import { startsWith } from 'vs/base/common/strings'; +import { BackupMainService } from 'vs/platform/backup/electron-main/backupMainService'; +import { IBackupMainService } from 'vs/platform/backup/common/backup'; +import { HistoryMainService } from 'vs/platform/history/electron-main/historyMainService'; +import { URLService } from 'vs/platform/url/common/urlService'; +import { WorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService'; export class CodeApplication extends Disposable { @@ -88,14 +94,13 @@ export class CodeApplication extends Disposable { constructor( private mainIpcServer: Server, - private userEnv: platform.IProcessEnvironment, + private userEnv: IProcessEnvironment, @IInstantiationService private instantiationService: IInstantiationService, @ILogService private logService: ILogService, @IEnvironmentService private environmentService: IEnvironmentService, @ILifecycleService private lifecycleService: ILifecycleService, @IConfigurationService private configurationService: ConfigurationService, - @IStateService private stateService: IStateService, - @IHistoryMainService private historyMainService: IHistoryMainService, + @IStateService private stateService: IStateService ) { super(); @@ -138,12 +143,27 @@ export class CodeApplication extends Disposable { app.on('web-contents-created', (event: any, contents) => { contents.on('will-attach-webview', (event: Electron.Event, webPreferences, params) => { + const isValidWebviewSource = (source: string): boolean => { + if (!source) { + return false; + } + + if (source === 'data:text/html;charset=utf-8,%3C%21DOCTYPE%20html%3E%0D%0A%3Chtml%20lang%3D%22en%22%20style%3D%22width%3A%20100%25%3B%20height%3A%20100%25%22%3E%0D%0A%3Chead%3E%0D%0A%09%3Ctitle%3EVirtual%20Document%3C%2Ftitle%3E%0D%0A%3C%2Fhead%3E%0D%0A%3Cbody%20style%3D%22margin%3A%200%3B%20overflow%3A%20hidden%3B%20width%3A%20100%25%3B%20height%3A%20100%25%22%3E%0D%0A%3C%2Fbody%3E%0D%0A%3C%2Fhtml%3E') { + return true; + } + + const srcUri = URI.parse(source).fsPath.toLowerCase(); + const rootUri = URI.file(this.environmentService.appRoot).fsPath.toLowerCase(); + + return startsWith(srcUri, rootUri + nativeSep); + }; + // Ensure defaults delete webPreferences.preload; webPreferences.nodeIntegration = false; // Verify URLs being loaded - if (this.isValidWebviewSource(params.src) && this.isValidWebviewSource(webPreferences.preloadURL)) { + if (isValidWebviewSource(params.src) && isValidWebviewSource(webPreferences.preloadURL)) { return; } @@ -168,85 +188,6 @@ export class CodeApplication extends Disposable { }); }); - const connectionPool: Map = new Map(); - - class ActiveConnection { - private _authority: string; - private _client: Promise>; - private _disposeRunner: RunOnceScheduler; - - constructor(authority: string, host: string, port: number) { - this._authority = authority; - this._client = connectRemoteAgentManagement(authority, host, port, `main`); - this._disposeRunner = new RunOnceScheduler(() => this._dispose(), 5000); - } - - private _dispose(): void { - this._disposeRunner.dispose(); - connectionPool.delete(this._authority); - this._client.then((connection) => { - connection.dispose(); - }); - } - - public getClient(): Promise> { - this._disposeRunner.schedule(); - return this._client; - } - } - - const resolvedAuthorities = new Map(); - ipc.on('vscode:remoteAuthorityResolved', (event: any, data: ResolvedAuthority) => { - resolvedAuthorities.set(data.authority, data); - }); - const resolveAuthority = (authority: string): ResolvedAuthority | null => { - if (authority.indexOf('+') >= 0) { - if (resolvedAuthorities.has(authority)) { - return resolvedAuthorities.get(authority); - } - return null; - } else { - const [host, strPort] = authority.split(':'); - const port = parseInt(strPort, 10); - return { authority, host, port, syncExtensions: false }; - } - }; - - protocol.registerBufferProtocol(REMOTE_HOST_SCHEME, async (request, callback) => { - if (request.method !== 'GET') { - return callback(null); - } - const uri = URI.parse(request.url); - - let activeConnection: ActiveConnection = null; - if (connectionPool.has(uri.authority)) { - activeConnection = connectionPool.get(uri.authority); - } else { - let resolvedAuthority = resolveAuthority(uri.authority); - if (!resolvedAuthority) { - callback(null); - return; - } - activeConnection = new ActiveConnection(uri.authority, resolvedAuthority.host, resolvedAuthority.port); - connectionPool.set(uri.authority, activeConnection); - } - try { - const rawClient = await activeConnection.getClient(); - if (connectionPool.has(uri.authority)) { // not disposed in the meantime - const channel = rawClient.getChannel(REMOTE_FILE_SYSTEM_CHANNEL_NAME); - - // TODO@alex don't use call directly, wrap it around a `RemoteExtensionsFileSystemProvider` - const fileContents = await channel.call('readFile', [uri]); - callback(Buffer.from(fileContents)); - } else { - callback(null); - } - } catch (err) { - errors.onUnexpectedError(err); - callback(null); - } - }); - let macOpenFileURIs: URI[] = []; let runningTimeout: any = null; app.on('open-file', (event: Event, path: string) => { @@ -315,38 +256,10 @@ export class CodeApplication extends Disposable { } }); - ipc.on('vscode:toggleDevTools', (event: Event) => { - event.sender.toggleDevTools(); - }); + ipc.on('vscode:toggleDevTools', (event: Event) => event.sender.toggleDevTools()); + ipc.on('vscode:openDevTools', (event: Event) => event.sender.openDevTools()); - ipc.on('vscode:openDevTools', (event: Event) => { - event.sender.openDevTools(); - }); - - ipc.on('vscode:reloadWindow', (event: Event) => { - event.sender.reload(); - }); - - // Keyboard layout changes - KeyboardLayoutMonitor.INSTANCE.onDidChangeKeyboardLayout(() => { - if (this.windowsMainService) { - this.windowsMainService.sendToAll('vscode:keyboardLayoutChanged', false); - } - }); - } - - private isValidWebviewSource(source: string): boolean { - if (!source) { - return false; - } - - if (source === 'data:text/html;charset=utf-8,%3C%21DOCTYPE%20html%3E%0D%0A%3Chtml%20lang%3D%22en%22%20style%3D%22width%3A%20100%25%3B%20height%3A%20100%25%22%3E%0D%0A%3Chead%3E%0D%0A%09%3Ctitle%3EVirtual%20Document%3C%2Ftitle%3E%0D%0A%3C%2Fhead%3E%0D%0A%3Cbody%20style%3D%22margin%3A%200%3B%20overflow%3A%20hidden%3B%20width%3A%20100%25%3B%20height%3A%20100%25%22%3E%0D%0A%3C%2Fbody%3E%0D%0A%3C%2Fhtml%3E') { - return true; - } - - const srcUri: any = URI.parse(source).fsPath.toLowerCase(); - const rootUri = URI.file(this.environmentService.appRoot).fsPath.toLowerCase(); - return srcUri.startsWith(rootUri + nativeSep); + ipc.on('vscode:reloadWindow', (event: Event) => event.sender.reload()); } private onUnexpectedError(err: Error): void { @@ -374,10 +287,7 @@ export class CodeApplication extends Disposable { // Theme changes if (event === 'vscode:changeColorTheme' && typeof payload === 'string') { - let data = JSON.parse(payload); - - this.stateService.setItem(THEME_STORAGE_KEY, data.baseTheme); - this.stateService.setItem(THEME_BG_STORAGE_KEY, data.background); + storeBackgroundColor(this.stateService, JSON.parse(payload)); } } @@ -390,7 +300,7 @@ export class CodeApplication extends Disposable { // This will help Windows to associate the running program with // any shortcut that is pinned to the taskbar and prevent showing // two icons in the taskbar for the same app. - if (platform.isWindows && product.win32AppUserModelId) { + if (isWindows && product.win32AppUserModelId) { app.setAppUserModelId(product.win32AppUserModelId); } @@ -401,7 +311,7 @@ export class CodeApplication extends Disposable { // Explicitly opt out of the patch here before creating any windows. // See: https://github.com/Microsoft/vscode/issues/35361#issuecomment-399794085 try { - if (platform.isMacintosh && this.configurationService.getValue('window.nativeTabs') === true && !systemPreferences.getUserDefault('NSUseImprovedLayoutPass', 'boolean')) { + if (isMacintosh && this.configurationService.getValue('window.nativeTabs') === true && !systemPreferences.getUserDefault('NSUseImprovedLayoutPass', 'boolean')) { systemPreferences.setUserDefault('NSUseImprovedLayoutPass', 'boolean', true as any); } } catch (error) { @@ -511,7 +421,7 @@ export class CodeApplication extends Disposable { services.set(IUpdateService, new SyncDescriptor(Win32UpdateService)); } else if (process.platform === 'linux') { if (process.env.SNAP && process.env.SNAP_REVISION) { - services.set(IUpdateService, new SyncDescriptor(SnapUpdateService)); + services.set(IUpdateService, new SyncDescriptor(SnapUpdateService, [process.env.SNAP, process.env.SNAP_REVISION])); } else { services.set(IUpdateService, new SyncDescriptor(LinuxUpdateService)); } @@ -525,6 +435,10 @@ export class CodeApplication extends Disposable { services.set(IIssueService, new SyncDescriptor(IssueService, [machineId, this.userEnv])); services.set(IMenubarService, new SyncDescriptor(MenubarService)); services.set(IStorageMainService, new SyncDescriptor(StorageMainService)); + services.set(IBackupMainService, new SyncDescriptor(BackupMainService)); + services.set(IHistoryMainService, new SyncDescriptor(HistoryMainService)); + services.set(IURLService, new SyncDescriptor(URLService)); + services.set(IWorkspacesMainService, new SyncDescriptor(WorkspacesMainService)); // Telemetry if (!this.environmentService.isExtensionDevelopment && !this.environmentService.args['disable-telemetry'] && !!product.enableTelemetry) { @@ -630,8 +544,6 @@ export class CodeApplication extends Disposable { // Propagate to clients const windowsMainService = this.windowsMainService = accessor.get(IWindowsMainService); // TODO@Joao: unfold this - const args = this.environmentService.args; - // Create a URL handler which forwards to the last active window const activeWindowManager = new ActiveWindowManager(windowsService); const activeWindowRouter = new StaticRouter(ctx => activeWindowManager.getActiveClientId().then(id => ctx === id)); @@ -640,7 +552,7 @@ export class CodeApplication extends Disposable { // On Mac, Code can be running without any open windows, so we must create a window to handle urls, // if there is none - if (platform.isMacintosh) { + if (isMacintosh) { const environmentService = accessor.get(IEnvironmentService); urlService.registerHandler({ @@ -661,6 +573,7 @@ export class CodeApplication extends Disposable { urlService.registerHandler(multiplexURLHandler); // Watch Electron URLs and forward them to the UrlService + const args = this.environmentService.args; const urls = args['open-url'] ? args._urls : []; const urlListener = new ElectronURLListener(urls, urlService, this.windowsMainService); this._register(urlListener); @@ -687,9 +600,10 @@ export class CodeApplication extends Disposable { private afterWindowOpen(accessor: ServicesAccessor): void { const windowsMainService = accessor.get(IWindowsMainService); + const historyMainService = accessor.get(IHistoryMainService); let windowsMutex: Mutex | null = null; - if (platform.isWindows) { + if (isWindows) { // Setup Windows mutex try { @@ -711,7 +625,7 @@ export class CodeApplication extends Disposable { // Ensure Windows foreground love module try { // tslint:disable-next-line:no-unused-expression - require.__$__nodeRequire('windows-foreground-love'); + require.__$__nodeRequire('windows-foreground-love'); } catch (e) { if (!this.environmentService.isBuilt) { windowsMainService.showMessageBox({ @@ -725,12 +639,105 @@ export class CodeApplication extends Disposable { } } + // Remote Authorities + this.handleRemoteAuthorities(); + + // Keyboard layout changes + KeyboardLayoutMonitor.INSTANCE.onDidChangeKeyboardLayout(() => { + this.windowsMainService.sendToAll('vscode:keyboardLayoutChanged', false); + }); + // Jump List - this.historyMainService.updateWindowsJumpList(); - this.historyMainService.onRecentlyOpenedChange(() => this.historyMainService.updateWindowsJumpList()); + historyMainService.updateWindowsJumpList(); + historyMainService.onRecentlyOpenedChange(() => historyMainService.updateWindowsJumpList()); // Start shared process after a while const sharedProcessSpawn = this._register(new RunOnceScheduler(() => getShellEnvironment().then(userEnv => this.sharedProcess.spawn(userEnv)), 3000)); sharedProcessSpawn.schedule(); } + + private handleRemoteAuthorities(): void { + const connectionPool: Map = new Map(); + + const isBuilt = this.environmentService.isBuilt; + + class ActiveConnection { + private _authority: string; + private _client: Promise>; + private _disposeRunner: RunOnceScheduler; + + constructor(authority: string, host: string, port: number) { + this._authority = authority; + this._client = connectRemoteAgentManagement(authority, host, port, `main`, isBuilt); + this._disposeRunner = new RunOnceScheduler(() => this._dispose(), 5000); + } + + private _dispose(): void { + this._disposeRunner.dispose(); + connectionPool.delete(this._authority); + this._client.then((connection) => { + connection.dispose(); + }); + } + + public getClient(): Promise> { + this._disposeRunner.schedule(); + return this._client; + } + } + + const resolvedAuthorities = new Map(); + ipc.on('vscode:remoteAuthorityResolved', (event: any, data: ResolvedAuthority) => { + resolvedAuthorities.set(data.authority, data); + }); + + const resolveAuthority = (authority: string): ResolvedAuthority | null => { + if (authority.indexOf('+') >= 0) { + if (resolvedAuthorities.has(authority)) { + return resolvedAuthorities.get(authority); + } + return null; + } else { + const [host, strPort] = authority.split(':'); + const port = parseInt(strPort, 10); + return { authority, host, port, syncExtensions: false }; + } + }; + + protocol.registerBufferProtocol(REMOTE_HOST_SCHEME, async (request, callback) => { + if (request.method !== 'GET') { + return callback(null); + } + const uri = URI.parse(request.url); + + let activeConnection: ActiveConnection = null; + if (connectionPool.has(uri.authority)) { + activeConnection = connectionPool.get(uri.authority); + } else { + let resolvedAuthority = resolveAuthority(uri.authority); + if (!resolvedAuthority) { + callback(null); + return; + } + activeConnection = new ActiveConnection(uri.authority, resolvedAuthority.host, resolvedAuthority.port); + connectionPool.set(uri.authority, activeConnection); + } + try { + const rawClient = await activeConnection.getClient(); + if (connectionPool.has(uri.authority)) { // not disposed in the meantime + const channel = rawClient.getChannel(REMOTE_FILE_SYSTEM_CHANNEL_NAME); + + // TODO@alex don't use call directly, wrap it around a `RemoteExtensionsFileSystemProvider` + const fileContents = await channel.call('readFile', [uri]); + callback(Buffer.from(fileContents)); + } else { + callback(null); + } + } catch (err) { + errors.onUnexpectedError(err); + callback(null); + } + }); + } } + diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index b3f48067b9a..2165dcc944f 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -8,9 +8,8 @@ import { app, dialog } from 'electron'; import { assign } from 'vs/base/common/objects'; import * as platform from 'vs/base/common/platform'; import product from 'vs/platform/node/product'; -import * as path from 'path'; import { parseMainProcessArgv } from 'vs/platform/environment/node/argv'; -import { mkdirp, readdir, rimraf } from 'vs/base/node/pfs'; +import { mkdirp } from 'vs/base/node/pfs'; import { validatePaths } from 'vs/code/node/paths'; import { LifecycleService, ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain'; import { Server, serve, connect } from 'vs/base/parts/ipc/node/ipc.net'; @@ -22,22 +21,14 @@ import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { ILogService, ConsoleLogMainService, MultiplexLogService, getLogLevel } from 'vs/platform/log/common/log'; import { StateService } from 'vs/platform/state/node/stateService'; import { IStateService } from 'vs/platform/state/common/state'; -import { IBackupMainService } from 'vs/platform/backup/common/backup'; -import { BackupMainService } from 'vs/platform/backup/electron-main/backupMainService'; import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment'; import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ConfigurationService } from 'vs/platform/configuration/node/configurationService'; import { IRequestService } from 'vs/platform/request/node/request'; import { RequestService } from 'vs/platform/request/electron-main/requestService'; -import { IURLService } from 'vs/platform/url/common/url'; -import { URLService } from 'vs/platform/url/common/urlService'; import * as fs from 'fs'; import { CodeApplication } from 'vs/code/electron-main/app'; -import { HistoryMainService } from 'vs/platform/history/electron-main/historyMainService'; -import { IHistoryMainService } from 'vs/platform/history/common/history'; -import { WorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService'; -import { IWorkspacesMainService } from 'vs/platform/workspaces/common/workspaces'; import { localize } from 'vs/nls'; import { mnemonicButtonLabel } from 'vs/base/common/labels'; import { createSpdLogService } from 'vs/platform/log/node/spdlogService'; @@ -45,67 +36,16 @@ import { IDiagnosticsService, DiagnosticsService } from 'vs/platform/diagnostics import { BufferLogService } from 'vs/platform/log/common/bufferLog'; import { uploadLogs } from 'vs/code/electron-main/logUploader'; import { setUnexpectedErrorHandler } from 'vs/base/common/errors'; -import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { CommandLineDialogService } from 'vs/platform/dialogs/node/dialogService'; import { createWaitMarkerFile } from 'vs/code/node/wait'; -function createServices(args: ParsedArgs, bufferLogService: BufferLogService): IInstantiationService { - const services = new ServiceCollection(); - - const environmentService = new EnvironmentService(args, process.execPath); - - const logService = new MultiplexLogService([new ConsoleLogMainService(getLogLevel(environmentService)), bufferLogService]); - process.once('exit', () => logService.dispose()); - setTimeout(() => cleanupOlderLogs(environmentService).then(null, err => console.error(err)), 10000); - - services.set(IEnvironmentService, environmentService); - services.set(ILogService, logService); - services.set(IWorkspacesMainService, new SyncDescriptor(WorkspacesMainService)); - services.set(IHistoryMainService, new SyncDescriptor(HistoryMainService)); - services.set(ILifecycleService, new SyncDescriptor(LifecycleService)); - services.set(IStateService, new SyncDescriptor(StateService)); - services.set(IConfigurationService, new SyncDescriptor(ConfigurationService)); - services.set(IRequestService, new SyncDescriptor(RequestService)); - services.set(IURLService, new SyncDescriptor(URLService)); - services.set(IBackupMainService, new SyncDescriptor(BackupMainService)); - services.set(IDialogService, new SyncDescriptor(CommandLineDialogService)); - services.set(IDiagnosticsService, new SyncDescriptor(DiagnosticsService)); - - return new InstantiationService(services, true); -} - -/** - * Cleans up older logs, while keeping the 10 most recent ones. -*/ -async function cleanupOlderLogs(environmentService: EnvironmentService): Promise { - const currentLog = path.basename(environmentService.logsPath); - const logsRoot = path.dirname(environmentService.logsPath); - const children = await readdir(logsRoot); - const allSessions = children.filter(name => /^\d{8}T\d{6}$/.test(name)); - const oldSessions = allSessions.sort().filter((d, i) => d !== currentLog); - const toDelete = oldSessions.slice(0, Math.max(0, oldSessions.length - 9)); - - await Promise.all(toDelete.map(name => rimraf(path.join(logsRoot, name)))); -} - -function createPaths(environmentService: IEnvironmentService): Promise { - const paths = [ - environmentService.extensionsPath, - environmentService.nodeCachedDataDir, - environmentService.logsPath, - environmentService.globalStorageHome, - environmentService.workspaceStorageHome - ]; - - return Promise.all(paths.map(path => path && mkdirp(path))); -} - class ExpectedError extends Error { - public readonly isExpected = true; + readonly isExpected = true; } function setupIPC(accessor: ServicesAccessor): Promise { const logService = accessor.get(ILogService); + const environmentService = accessor.get(IEnvironmentService); + const instantiationService = accessor.get(IInstantiationService); function allowSetForegroundWindow(service: LaunchChannelClient): Promise { let promise: Promise = Promise.resolve(); @@ -127,8 +67,6 @@ function setupIPC(accessor: ServicesAccessor): Promise { } function setup(retry: boolean): Promise { - const environmentService = accessor.get(IEnvironmentService); - return serve(environmentService.mainIPCHandle).then(server => { // Print --status usage info @@ -154,7 +92,15 @@ function setupIPC(accessor: ServicesAccessor): Promise { return server; }, err => { + + // Handle unexpected errors (the only expected error is EADDRINUSE that + // indicates a second instance of Code is running) if (err.code !== 'EADDRINUSE') { + + // Show a dialog for errors that can be resolved by the user + handleStartupDataDirError(environmentService, err); + + // Any other runtime error is just printed to the console return Promise.reject(err); } @@ -195,14 +141,18 @@ function setupIPC(accessor: ServicesAccessor): Promise { // Process Info if (environmentService.args.status) { return service.getMainProcessInfo().then(info => { - return accessor.get(IDiagnosticsService).printDiagnostics(info).then(() => Promise.reject(new ExpectedError())); + return instantiationService.invokeFunction(accessor => { + return accessor.get(IDiagnosticsService).printDiagnostics(info).then(() => Promise.reject(new ExpectedError())); + }); }); } // Log uploader if (typeof environmentService.args['upload-logs'] !== 'undefined') { - return uploadLogs(service, accessor.get(IRequestService), environmentService) - .then(() => Promise.reject(new ExpectedError())); + return instantiationService.invokeFunction(accessor => { + return uploadLogs(service, accessor.get(IRequestService), environmentService) + .then(() => Promise.reject(new ExpectedError())); + }); } logService.trace('Sending env to running instance...'); @@ -262,6 +212,15 @@ function showStartupWarningDialog(message: string, detail: string): void { }); } +function handleStartupDataDirError(environmentService: IEnvironmentService, error): void { + if (error.code === 'EACCES' || error.code === 'EPERM') { + showStartupWarningDialog( + localize('startupDataDirError', "Unable to write program user data."), + localize('startupDataDirErrorDetail', "Please make sure the directory {0} is writeable.", environmentService.userDataPath) + ); + } +} + function quit(accessor: ServicesAccessor, reason?: ExpectedError | Error): void { const logService = accessor.get(ILogService); const lifecycleService = accessor.get(ILifecycleService); @@ -313,14 +272,20 @@ function startup(args: ParsedArgs): void { const instantiationService = createServices(args, bufferLogService); instantiationService.invokeFunction(accessor => { const environmentService = accessor.get(IEnvironmentService); + const stateService = accessor.get(IStateService); // Patch `process.env` with the instance's environment const instanceEnvironment = patchEnvironment(environmentService); // Startup - return instantiationService - .invokeFunction(a => createPaths(a.get(IEnvironmentService))) - .then(() => instantiationService.invokeFunction(setupIPC)) + return initServices(environmentService, stateService as StateService) + .then(() => instantiationService.invokeFunction(setupIPC), error => { + + // Show a dialog for errors that can be resolved by the user + handleStartupDataDirError(environmentService, error); + + return Promise.reject(error); + }) .then(mainIpcServer => { bufferLogService.logger = createSpdLogService('main', bufferLogService.getLevel(), environmentService.logsPath); @@ -329,6 +294,43 @@ function startup(args: ParsedArgs): void { }).then(null, err => instantiationService.invokeFunction(quit, err)); } +function createServices(args: ParsedArgs, bufferLogService: BufferLogService): IInstantiationService { + const services = new ServiceCollection(); + + const environmentService = new EnvironmentService(args, process.execPath); + + const logService = new MultiplexLogService([new ConsoleLogMainService(getLogLevel(environmentService)), bufferLogService]); + process.once('exit', () => logService.dispose()); + + services.set(IEnvironmentService, environmentService); + services.set(ILogService, logService); + services.set(ILifecycleService, new SyncDescriptor(LifecycleService)); + services.set(IStateService, new SyncDescriptor(StateService)); + services.set(IConfigurationService, new SyncDescriptor(ConfigurationService)); + services.set(IRequestService, new SyncDescriptor(RequestService)); + services.set(IDiagnosticsService, new SyncDescriptor(DiagnosticsService)); + + return new InstantiationService(services, true); +} + +function initServices(environmentService: IEnvironmentService, stateService: StateService): Promise { + + // Ensure paths for environment service exist + const environmentServiceInitialization = Promise.all([ + environmentService.extensionsPath, + environmentService.nodeCachedDataDir, + environmentService.logsPath, + environmentService.globalStorageHome, + environmentService.workspaceStorageHome, + environmentService.backupHome + ].map(path => path && mkdirp(path))); + + // State service + const stateServiceInitialization = stateService.init(); + + return Promise.all([environmentServiceInitialization, stateServiceInitialization]); +} + function main(): void { // Set the error handler early enough so that we are not getting the diff --git a/src/vs/code/electron-main/theme.ts b/src/vs/code/electron-main/theme.ts index cd0f4bf02f4..5518c38404a 100644 --- a/src/vs/code/electron-main/theme.ts +++ b/src/vs/code/electron-main/theme.ts @@ -7,12 +7,17 @@ import { isWindows, isMacintosh } from 'vs/base/common/platform'; import { systemPreferences } from 'electron'; import { IStateService } from 'vs/platform/state/common/state'; -export const DEFAULT_BG_LIGHT = '#FFFFFF'; -export const DEFAULT_BG_DARK = '#1E1E1E'; -export const DEFAULT_BG_HC_BLACK = '#000000'; +const DEFAULT_BG_LIGHT = '#FFFFFF'; +const DEFAULT_BG_DARK = '#1E1E1E'; +const DEFAULT_BG_HC_BLACK = '#000000'; -export const THEME_STORAGE_KEY = 'theme'; -export const THEME_BG_STORAGE_KEY = 'themeBackground'; +const THEME_STORAGE_KEY = 'theme'; +const THEME_BG_STORAGE_KEY = 'themeBackground'; + +export function storeBackgroundColor(stateService: IStateService, data: { baseTheme: string, background: string }): void { + stateService.setItem(THEME_STORAGE_KEY, data.baseTheme); + stateService.setItem(THEME_BG_STORAGE_KEY, data.background); +} export function getBackgroundColor(stateService: IStateService): string { if (isWindows && systemPreferences.isInvertedColorScheme()) { diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index e0e46e1b08f..d0184727757 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -273,7 +273,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { // inform all waiting promises that we are ready now while (this.whenReadyCallbacks.length) { - this.whenReadyCallbacks.pop()(this); + this.whenReadyCallbacks.pop()!(this); } } @@ -543,12 +543,9 @@ export class CodeWindow extends Disposable implements ICodeWindow { } } - reload(configuration?: IWindowConfiguration, cli?: ParsedArgs): void { - + reload(configurationIn?: IWindowConfiguration, cli?: ParsedArgs): void { // If config is not provided, copy our current one - if (!configuration) { - configuration = objects.mixin({}, this.currentConfig); - } + const configuration = configurationIn ? configurationIn : objects.mixin({}, this.currentConfig); // Delete some properties we do not want during reload delete configuration.filesToOpen; @@ -713,7 +710,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { return state; } - private validateWindowState(state: IWindowState): IWindowState { + private validateWindowState(state: IWindowState): IWindowState | null { if (!state) { return null; } @@ -856,6 +853,10 @@ export class CodeWindow extends Disposable implements ICodeWindow { return true; // default } + if (windowConfig.nativeTabs) { + return true; // https://github.com/electron/electron/issues/16142 + } + return windowConfig.nativeFullScreen !== false; } @@ -1028,7 +1029,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { private createTouchBarGroupSegments(items: ISerializableCommandAction[] = []): ITouchBarSegment[] { const segments: ITouchBarSegment[] = items.map(item => { - let icon: Electron.NativeImage; + let icon: Electron.NativeImage | undefined; if (item.iconLocation && item.iconLocation.dark.scheme === 'file') { icon = nativeImage.createFromPath(URI.revive(item.iconLocation.dark).fsPath); if (icon.isEmpty()) { diff --git a/src/vs/code/node/windowsFinder.ts b/src/vs/code/node/windowsFinder.ts index 8541b69dbe5..4c1765657de 100644 --- a/src/vs/code/node/windowsFinder.ts +++ b/src/vs/code/node/windowsFinder.ts @@ -26,7 +26,7 @@ export interface IBestWindowOrFolderOptions { fileUri?: URI; userHome?: string; codeSettingsFolder?: string; - workspaceResolver: (workspace: IWorkspaceIdentifier) => IResolvedWorkspace; + workspaceResolver: (workspace: IWorkspaceIdentifier) => IResolvedWorkspace | null; } export function findBestWindowOrFolderForFile({ windows, newWindow, reuseWindow, context, fileUri, workspaceResolver }: IBestWindowOrFolderOptions): W | null { @@ -39,7 +39,7 @@ export function findBestWindowOrFolderForFile({ windows return !newWindow ? getLastActiveWindow(windows) : null; } -function findWindowOnFilePath(windows: W[], fileUri: URI, workspaceResolver: (workspace: IWorkspaceIdentifier) => IResolvedWorkspace): W | null { +function findWindowOnFilePath(windows: W[], fileUri: URI, workspaceResolver: (workspace: IWorkspaceIdentifier) => IResolvedWorkspace | null): W | null { // First check for windows with workspaces that have a parent folder of the provided path opened const workspaceWindows = windows.filter(window => !!window.openedWorkspace); diff --git a/src/vs/code/test/node/windowsFinder.test.ts b/src/vs/code/test/node/windowsFinder.test.ts index 0c04c5b2cf5..bd9e7ee187a 100644 --- a/src/vs/code/test/node/windowsFinder.test.ts +++ b/src/vs/code/test/node/windowsFinder.test.ts @@ -31,7 +31,7 @@ function options(custom?: Partial>): I } const vscodeFolderWindow: ISimpleWindow = { lastFocusTime: 1, openedFolderUri: URI.file(path.join(fixturesFolder, 'vscode_folder')) }; -const lastActiveWindow: ISimpleWindow = { lastFocusTime: 3, openedFolderUri: null }; +const lastActiveWindow: ISimpleWindow = { lastFocusTime: 3, openedFolderUri: undefined }; const noVscodeFolderWindow: ISimpleWindow = { lastFocusTime: 2, openedFolderUri: URI.file(path.join(fixturesFolder, 'no_vscode_folder')) }; const windows: ISimpleWindow[] = [ vscodeFolderWindow, diff --git a/src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts b/src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts index df80f0b2087..8ead82c249a 100644 --- a/src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts +++ b/src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts @@ -48,6 +48,7 @@ export class EditorScrollbar extends ViewPart { handleMouseWheel: configScrollbarOpts.handleMouseWheel, arrowSize: configScrollbarOpts.arrowSize, mouseWheelScrollSensitivity: configScrollbarOpts.mouseWheelScrollSensitivity, + fastScrollSensitivity: configScrollbarOpts.fastScrollSensitivity, }; this.scrollbar = this._register(new SmoothScrollableElement(linesContent.domNode, scrollbarOptions, this._context.viewLayout.scrollable)); @@ -127,7 +128,8 @@ export class EditorScrollbar extends ViewPart { const editor = this._context.configuration.editor; let newOpts: ScrollableElementChangeOptions = { handleMouseWheel: editor.viewInfo.scrollbar.handleMouseWheel, - mouseWheelScrollSensitivity: editor.viewInfo.scrollbar.mouseWheelScrollSensitivity + mouseWheelScrollSensitivity: editor.viewInfo.scrollbar.mouseWheelScrollSensitivity, + fastScrollSensitivity: editor.viewInfo.scrollbar.fastScrollSensitivity }; this.scrollbar.updateOptions(newOpts); } diff --git a/src/vs/editor/common/config/commonEditorConfig.ts b/src/vs/editor/common/config/commonEditorConfig.ts index 8fa09be3b70..7974ee1a03f 100644 --- a/src/vs/editor/common/config/commonEditorConfig.ts +++ b/src/vs/editor/common/config/commonEditorConfig.ts @@ -33,7 +33,7 @@ export interface ITabFocus { export const TabFocus: ITabFocus = new class implements ITabFocus { private _tabFocus: boolean = false; - private readonly _onDidChangeTabFocus: Emitter = new Emitter(); + private readonly _onDidChangeTabFocus = new Emitter(); public readonly onDidChangeTabFocus: Event = this._onDidChangeTabFocus.event; public getTabFocusMode(): boolean { @@ -429,6 +429,11 @@ const editorConfiguration: IConfigurationNode = { 'default': EDITOR_DEFAULTS.viewInfo.scrollbar.mouseWheelScrollSensitivity, 'markdownDescription': nls.localize('mouseWheelScrollSensitivity', "A multiplier to be used on the `deltaX` and `deltaY` of mouse wheel scroll events.") }, + 'editor.fastScrollSensitivity': { + 'type': 'number', + 'default': EDITOR_DEFAULTS.viewInfo.scrollbar.fastScrollSensitivity, + 'markdownDescription': nls.localize('fastScrollSensitivity', "Scrolling speed mulitiplier when pressing `Alt`") + }, 'editor.multiCursorModifier': { 'type': 'string', 'enum': ['ctrlCmd', 'alt'], diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index ecdb1f39f45..bef7f91cd4e 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -458,6 +458,11 @@ export interface IEditorOptions { * Defaults to 1. */ mouseWheelScrollSensitivity?: number; + /** + * FastScrolling mulitplier speed when pressing `Alt` + * Defaults to 5. + */ + fastScrollSensitivity?: number; /** * The modifier to be used to add multiple cursors with the mouse. * Defaults to 'alt' @@ -875,6 +880,7 @@ export interface InternalEditorScrollbarOptions { readonly verticalScrollbarSize: number; readonly verticalSliderSize: number; readonly mouseWheelScrollSensitivity: number; + readonly fastScrollSensitivity: number; } export interface InternalEditorMinimapOptions { @@ -1292,6 +1298,7 @@ export class InternalEditorOptions { && a.verticalScrollbarSize === b.verticalScrollbarSize && a.verticalSliderSize === b.verticalSliderSize && a.mouseWheelScrollSensitivity === b.mouseWheelScrollSensitivity + && a.fastScrollSensitivity === b.fastScrollSensitivity ); } @@ -1795,7 +1802,7 @@ export class EditorOptionsValidator { }; } - private static _sanitizeScrollbarOpts(opts: IEditorScrollbarOptions | undefined, defaults: InternalEditorScrollbarOptions, mouseWheelScrollSensitivity: number): InternalEditorScrollbarOptions { + private static _sanitizeScrollbarOpts(opts: IEditorScrollbarOptions | undefined, defaults: InternalEditorScrollbarOptions, mouseWheelScrollSensitivity: number, fastScrollSensitivity: number): InternalEditorScrollbarOptions { if (typeof opts !== 'object') { return defaults; } @@ -1818,7 +1825,8 @@ export class EditorOptionsValidator { verticalSliderSize: _clampedInt(opts.verticalSliderSize, verticalScrollbarSize, 0, 1000), handleMouseWheel: _boolean(opts.handleMouseWheel, defaults.handleMouseWheel), - mouseWheelScrollSensitivity: mouseWheelScrollSensitivity + mouseWheelScrollSensitivity: mouseWheelScrollSensitivity, + fastScrollSensitivity: fastScrollSensitivity, }; } @@ -1965,7 +1973,12 @@ export class EditorOptionsValidator { // Disallow 0, as it would prevent/block scrolling mouseWheelScrollSensitivity = 1; } - const scrollbar = this._sanitizeScrollbarOpts(opts.scrollbar, defaults.scrollbar, mouseWheelScrollSensitivity); + + let fastScrollSensitivity = _float(opts.fastScrollSensitivity, defaults.scrollbar.fastScrollSensitivity); + if (fastScrollSensitivity <= 0) { + fastScrollSensitivity = defaults.scrollbar.fastScrollSensitivity; + } + const scrollbar = this._sanitizeScrollbarOpts(opts.scrollbar, defaults.scrollbar, mouseWheelScrollSensitivity, fastScrollSensitivity); const minimap = this._sanitizeMinimapOpts(opts.minimap, defaults.minimap); return { @@ -2592,6 +2605,7 @@ export const EDITOR_DEFAULTS: IValidatedEditorOptions = { verticalSliderSize: 14, handleMouseWheel: true, mouseWheelScrollSensitivity: 1, + fastScrollSensitivity: 5, }, minimap: { enabled: true, diff --git a/src/vs/editor/common/config/editorZoom.ts b/src/vs/editor/common/config/editorZoom.ts index 1be9203a573..522cfa6e095 100644 --- a/src/vs/editor/common/config/editorZoom.ts +++ b/src/vs/editor/common/config/editorZoom.ts @@ -15,7 +15,7 @@ export const EditorZoom: IEditorZoom = new class implements IEditorZoom { private _zoomLevel: number = 0; - private readonly _onDidChangeZoomLevel: Emitter = new Emitter(); + private readonly _onDidChangeZoomLevel = new Emitter(); public readonly onDidChangeZoomLevel: Event = this._onDidChangeZoomLevel.event; public getZoomLevel(): number { diff --git a/src/vs/editor/common/modes/languageConfigurationRegistry.ts b/src/vs/editor/common/modes/languageConfigurationRegistry.ts index 50c6663b7fe..0efcb9559f4 100644 --- a/src/vs/editor/common/modes/languageConfigurationRegistry.ts +++ b/src/vs/editor/common/modes/languageConfigurationRegistry.ts @@ -177,7 +177,7 @@ export class LanguageConfigurationRegistryImpl { private _entries: RichEditSupport[]; - private readonly _onDidChange: Emitter = new Emitter(); + private readonly _onDidChange = new Emitter(); public readonly onDidChange: Event = this._onDidChange.event; constructor() { diff --git a/src/vs/editor/common/modes/languageFeatureRegistry.ts b/src/vs/editor/common/modes/languageFeatureRegistry.ts index 616a4a40d98..1426b5e581e 100644 --- a/src/vs/editor/common/modes/languageFeatureRegistry.ts +++ b/src/vs/editor/common/modes/languageFeatureRegistry.ts @@ -30,7 +30,7 @@ export class LanguageFeatureRegistry { private _clock: number = 0; private _entries: Entry[] = []; - private readonly _onDidChange: Emitter = new Emitter(); + private readonly _onDidChange = new Emitter(); constructor() { } diff --git a/src/vs/editor/common/modes/modesRegistry.ts b/src/vs/editor/common/modes/modesRegistry.ts index a91dfa7887a..21b81b383ec 100644 --- a/src/vs/editor/common/modes/modesRegistry.ts +++ b/src/vs/editor/common/modes/modesRegistry.ts @@ -20,7 +20,7 @@ export class EditorModesRegistry { private _languages: ILanguageExtensionPoint[]; private _dynamicLanguages: ILanguageExtensionPoint[]; - private readonly _onDidChangeLanguages: Emitter = new Emitter(); + private readonly _onDidChangeLanguages = new Emitter(); public readonly onDidChangeLanguages: Event = this._onDidChangeLanguages.event; constructor() { diff --git a/src/vs/editor/common/modes/tokenizationRegistry.ts b/src/vs/editor/common/modes/tokenizationRegistry.ts index f685c7c02cc..158608f91e4 100644 --- a/src/vs/editor/common/modes/tokenizationRegistry.ts +++ b/src/vs/editor/common/modes/tokenizationRegistry.ts @@ -13,7 +13,7 @@ export class TokenizationRegistryImpl implements ITokenizationRegistry { private _map: { [language: string]: ITokenizationSupport }; private _promises: { [language: string]: Thenable }; - private readonly _onDidChange: Emitter = new Emitter(); + private readonly _onDidChange = new Emitter(); public readonly onDidChange: Event = this._onDidChange.event; private _colorMap: Color[] | null; diff --git a/src/vs/editor/common/services/modeServiceImpl.ts b/src/vs/editor/common/services/modeServiceImpl.ts index 21c4b0ce7a9..db3cd258a1f 100644 --- a/src/vs/editor/common/services/modeServiceImpl.ts +++ b/src/vs/editor/common/services/modeServiceImpl.ts @@ -45,10 +45,10 @@ export class ModeServiceImpl implements IModeService { private readonly _instantiatedModes: { [modeId: string]: IMode; }; private readonly _registry: LanguagesRegistry; - private readonly _onDidCreateMode: Emitter = new Emitter(); + private readonly _onDidCreateMode = new Emitter(); public readonly onDidCreateMode: Event = this._onDidCreateMode.event; - protected readonly _onLanguagesMaybeChanged: Emitter = new Emitter(); + protected readonly _onLanguagesMaybeChanged = new Emitter(); private readonly onLanguagesMaybeChanged: Event = this._onLanguagesMaybeChanged.event; constructor(warnOnOverwrite = false) { diff --git a/src/vs/editor/common/services/modelServiceImpl.ts b/src/vs/editor/common/services/modelServiceImpl.ts index 9f37823db53..8cf29738393 100644 --- a/src/vs/editor/common/services/modelServiceImpl.ts +++ b/src/vs/editor/common/services/modelServiceImpl.ts @@ -268,7 +268,7 @@ export class ModelServiceImpl extends Disposable implements IModelService { private readonly _onModelRemoved: Emitter = this._register(new Emitter()); public readonly onModelRemoved: Event = this._onModelRemoved.event; - private readonly _onModelModeChanged: Emitter<{ model: ITextModel; oldModeId: string; }> = this._register(new Emitter<{ model: ITextModel; oldModeId: string; }>({ leakWarningThreshold: 500 })); + private readonly _onModelModeChanged: Emitter<{ model: ITextModel; oldModeId: string; }> = this._register(new Emitter<{ model: ITextModel; oldModeId: string; }>()); public readonly onModelModeChanged: Event<{ model: ITextModel; oldModeId: string; }> = this._onModelModeChanged.event; private _modelCreationOptionsByLanguageAndResource: { @@ -294,6 +294,7 @@ export class ModelServiceImpl extends Disposable implements IModelService { if (this._markerService) { this._markerServiceSubscription = this._markerService.onMarkerChanged(this._handleMarkerChange, this); + this._handleMarkerChange(this._markerService.read().map(m => m.resource)); } this._configurationServiceSubscription = this._configurationService.onDidChangeConfiguration(e => this._updateModelOptions()); diff --git a/src/vs/editor/common/services/resolverService.ts b/src/vs/editor/common/services/resolverService.ts index 09ac4bbcf61..28aca391a32 100644 --- a/src/vs/editor/common/services/resolverService.ts +++ b/src/vs/editor/common/services/resolverService.ts @@ -24,6 +24,11 @@ export interface ITextModelService { * Registers a specific `scheme` content provider. */ registerTextModelContentProvider(scheme: string, provider: ITextModelContentProvider): IDisposable; + + /** + * Check if a provider for the given `scheme` exists + */ + hasTextModelContentProvider(scheme: string): boolean; } export interface ITextModelContentProvider { diff --git a/src/vs/editor/contrib/comment/test/lineCommentCommand.test.ts b/src/vs/editor/contrib/comment/test/lineCommentCommand.test.ts index a32458d6abd..e9c0c51499a 100644 --- a/src/vs/editor/contrib/comment/test/lineCommentCommand.test.ts +++ b/src/vs/editor/contrib/comment/test/lineCommentCommand.test.ts @@ -950,7 +950,9 @@ suite('Editor Contrib - Line Comment in mixed modes', () => { this._register(modes.TokenizationRegistry.register(this.getLanguageIdentifier().language, { getInitialState: (): modes.IState => NULL_STATE, - tokenize: undefined, + tokenize: () => { + throw new Error('not implemented'); + }, tokenize2: (line: string, state: modes.IState): TokenizationResult2 => { let languageId = (/^ /.test(line) ? INNER_LANGUAGE_ID : OUTER_LANGUAGE_ID); diff --git a/src/vs/editor/contrib/contextmenu/contextmenu.ts b/src/vs/editor/contrib/contextmenu/contextmenu.ts index 7e4c3823b3f..d38634e75cf 100644 --- a/src/vs/editor/contrib/contextmenu/contextmenu.ts +++ b/src/vs/editor/contrib/contextmenu/contextmenu.ts @@ -46,7 +46,7 @@ export class ContextMenuController implements IEditorContribution { this._toDispose.push(this._editor.onContextMenu((e: IEditorMouseEvent) => this._onContextMenu(e))); this._toDispose.push(this._editor.onDidScrollChange((e: IScrollEvent) => { - if (this._contextMenuIsBeingShownCount > 0) { + if (this._contextMenuIsBeingShownCount > 0 && e.scrollTopChanged) { this._contextViewService.hideContextView(); } })); diff --git a/src/vs/editor/contrib/format/format.ts b/src/vs/editor/contrib/format/format.ts index 0f510fde91e..401a6fc3fc0 100644 --- a/src/vs/editor/contrib/format/format.ts +++ b/src/vs/editor/contrib/format/format.ts @@ -17,11 +17,15 @@ import { CancellationToken } from 'vs/base/common/cancellation'; export class NoProviderError extends Error { - static readonly Name = 'NOPRO'; + static is(thing: any): thing is NoProviderError { + return thing instanceof Error && thing.name === NoProviderError._name; + } + + private static readonly _name = 'NOPRO'; constructor(message?: string) { super(); - this.name = NoProviderError.Name; + this.name = NoProviderError._name; if (message) { this.message = message; } diff --git a/src/vs/editor/contrib/format/formatActions.ts b/src/vs/editor/contrib/format/formatActions.ts index 24c0b50385a..fef43d6243c 100644 --- a/src/vs/editor/contrib/format/formatActions.ts +++ b/src/vs/editor/contrib/format/formatActions.ts @@ -9,9 +9,9 @@ import { KeyCode, KeyMod, KeyChord } from 'vs/base/common/keyCodes'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import * as editorCommon from 'vs/editor/common/editorCommon'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { registerEditorAction, ServicesAccessor, EditorAction, registerEditorContribution, IActionOptions } from 'vs/editor/browser/editorExtensions'; -import { OnTypeFormattingEditProviderRegistry, DocumentRangeFormattingEditProviderRegistry } from 'vs/editor/common/modes'; -import { getOnTypeFormattingEdits, getDocumentFormattingEdits, getDocumentRangeFormattingEdits, NoProviderError } from 'vs/editor/contrib/format/format'; +import { registerEditorAction, ServicesAccessor, EditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; +import { OnTypeFormattingEditProviderRegistry, DocumentRangeFormattingEditProviderRegistry, DocumentFormattingEditProviderRegistry, FormattingOptions } from 'vs/editor/common/modes'; +import { getOnTypeFormattingEdits, NoProviderError } from 'vs/editor/contrib/format/format'; import { FormattingEdit } from 'vs/editor/contrib/format/formattingEdit'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; @@ -21,12 +21,12 @@ import { Range } from 'vs/editor/common/core/range'; import { alert } from 'vs/base/browser/ui/aria/aria'; import { EditorState, CodeEditorStateFlag } from 'vs/editor/browser/core/editorState'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { ICodeEditor, IActiveCodeEditor } from 'vs/editor/browser/editorBrowser'; import { ISingleEditOperation } from 'vs/editor/common/model'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { CancellationToken } from 'vs/base/common/cancellation'; - +import { sequence } from 'vs/base/common/async'; function alertFormattingEdits(edits: ISingleEditOperation[]): void { @@ -55,6 +55,98 @@ function alertFormattingEdits(edits: ISingleEditOperation[]): void { } } +export const enum FormatRangeType { + Full, + Selection, +} + +export function formatDocumentRange(workerService: IEditorWorkerService, editor: IActiveCodeEditor, rangeOrRangeType: Range | FormatRangeType, options: FormattingOptions, token: CancellationToken): Promise { + + const provider = DocumentRangeFormattingEditProviderRegistry.ordered(editor.getModel()).slice(0, 1); + if (provider.length === 0) { + return Promise.reject(new NoProviderError()); + } + + let allEdits: ISingleEditOperation[] = []; + + editor.pushUndoStop(); + return sequence(provider.map(provider => { + // create a formatting task per provider. they run sequentially, + // potentially undoing the working of a previous formatter + return () => { + const state = new EditorState(editor, CodeEditorStateFlag.Value | CodeEditorStateFlag.Position); + const model = editor.getModel(); + + let range: Range; + if (rangeOrRangeType === FormatRangeType.Full) { + // full + range = model.getFullModelRange(); + + } else if (rangeOrRangeType === FormatRangeType.Selection) { + // selection or line (when empty) + range = editor.getSelection(); + if (range.isEmpty()) { + range = new Range(range.startLineNumber, 1, range.endLineNumber, model.getLineMaxColumn(range.endLineNumber)); + } + } else { + // as is + range = rangeOrRangeType; + } + return Promise.resolve(provider.provideDocumentRangeFormattingEdits(model, range, options, token)).then(edits => { + // break edits into smaller edits + return workerService.computeMoreMinimalEdits(editor.getModel().uri, edits); + }).then(edits => { + // make edit only when the editor didn't change while + // computing and only when there are edits + if (state.validate(editor) && isNonEmptyArray(edits)) { + FormattingEdit.execute(editor, edits); + allEdits = allEdits.concat(edits); + } + }); + }; + })).then(() => { + alertFormattingEdits(allEdits); + editor.pushUndoStop(); + editor.focus(); + editor.revealPositionInCenterIfOutsideViewport(editor.getPosition(), editorCommon.ScrollType.Immediate); + }); +} + +export function formatDocument(workerService: IEditorWorkerService, editor: IActiveCodeEditor, options: FormattingOptions, token: CancellationToken): Promise { + const provider = DocumentFormattingEditProviderRegistry.ordered(editor.getModel()).slice(0); + if (provider.length === 0) { + return formatDocumentRange(workerService, editor, FormatRangeType.Full, options, token); + } + + let allEdits: ISingleEditOperation[] = []; + + editor.pushUndoStop(); + return sequence(provider.map(provider => { + // create a formatting task per provider. they run sequentially, + // potentially undoing the working of a previous formatter + return () => { + const state = new EditorState(editor, CodeEditorStateFlag.Value | CodeEditorStateFlag.Position); + const model = editor.getModel(); + return Promise.resolve(provider.provideDocumentFormattingEdits(model, options, token)).then(edits => { + // break edits into smaller edits + return workerService.computeMoreMinimalEdits(editor.getModel().uri, edits); + }).then(edits => { + // make edit only when the editor didn't change while + // computing and only when there are edits + if (state.validate(editor) && isNonEmptyArray(edits)) { + FormattingEdit.execute(editor, edits); + allEdits = allEdits.concat(edits); + } + }); + }; + })).then(() => { + alertFormattingEdits(allEdits); + editor.pushUndoStop(); + editor.focus(); + editor.revealPositionInCenterIfOutsideViewport(editor.getPosition(), editorCommon.ScrollType.Immediate); + }); +} + class FormatOnType implements editorCommon.IEditorContribution { private static readonly ID = 'editor.contrib.autoFormat'; @@ -224,8 +316,7 @@ class FormatOnPaste implements editorCommon.IEditorContribution { let model = this.editor.getModel(); // no support - let [support] = DocumentRangeFormattingEditProviderRegistry.ordered(model); - if (!support || !support.provideDocumentRangeFormattingEdits) { + if (!DocumentRangeFormattingEditProviderRegistry.has(model)) { return; } @@ -245,19 +336,7 @@ class FormatOnPaste implements editorCommon.IEditorContribution { const model = this.editor.getModel(); const { tabSize, insertSpaces } = model.getOptions(); - const state = new EditorState(this.editor, CodeEditorStateFlag.Value | CodeEditorStateFlag.Position); - - getDocumentRangeFormattingEdits(model, range, { tabSize, insertSpaces }, CancellationToken.None).then(edits => { - return this.workerService.computeMoreMinimalEdits(model.uri, edits); - }).then(edits => { - if (!state.validate(this.editor)) { - return; - } - if (isNonEmptyArray(edits)) { - FormattingEdit.execute(this.editor, edits); - alertFormattingEdits(edits); - } - }); + formatDocumentRange(this.workerService, this.editor, range, { tabSize, insertSpaces }, CancellationToken.None); } public getId(): string { @@ -270,53 +349,7 @@ class FormatOnPaste implements editorCommon.IEditorContribution { } } -export abstract class AbstractFormatAction extends EditorAction { - - public run(accessor: ServicesAccessor, editor: ICodeEditor): Promise { - if (!editor.hasModel()) { - return Promise.resolve(void 0); - } - - const workerService = accessor.get(IEditorWorkerService); - const notificationService = accessor.get(INotificationService); - - const formattingPromise = this._getFormattingEdits(editor, CancellationToken.None); - if (!formattingPromise) { - return Promise.resolve(void 0); - } - - // Capture the state of the editor - const state = new EditorState(editor, CodeEditorStateFlag.Value | CodeEditorStateFlag.Position); - - // Receive formatted value from worker - return formattingPromise.then(edits => workerService.computeMoreMinimalEdits(editor.getModel().uri, edits)).then(edits => { - if (!state.validate(editor)) { - return; - } - - if (isNonEmptyArray(edits)) { - FormattingEdit.execute(editor, edits); - alertFormattingEdits(edits); - editor.focus(); - editor.revealPositionInCenterIfOutsideViewport(editor.getPosition(), editorCommon.ScrollType.Immediate); - } - }, err => { - if (err instanceof Error && err.name === NoProviderError.Name) { - this._notifyNoProviderError(notificationService, editor.getModel().getLanguageIdentifier().language); - } else { - throw err; - } - }); - } - - protected abstract _getFormattingEdits(editor: ICodeEditor, token: CancellationToken): Promise | undefined; - - protected _notifyNoProviderError(notificationService: INotificationService, language: string): void { - notificationService.info(nls.localize('no.provider', "There is no formatter for '{0}'-files installed.", language)); - } -} - -export class FormatDocumentAction extends AbstractFormatAction { +export class FormatDocumentAction extends EditorAction { constructor() { super({ @@ -339,21 +372,22 @@ export class FormatDocumentAction extends AbstractFormatAction { }); } - protected _getFormattingEdits(editor: ICodeEditor, token: CancellationToken): Promise | undefined { + run(accessor: ServicesAccessor, editor: ICodeEditor): Promise | void { if (!editor.hasModel()) { - return undefined; + return; } - const model = editor.getModel(); - const { tabSize, insertSpaces } = model.getOptions(); - return getDocumentFormattingEdits(model, { tabSize, insertSpaces }, token); - } - - protected _notifyNoProviderError(notificationService: INotificationService, language: string): void { - notificationService.info(nls.localize('no.documentprovider', "There is no document formatter for '{0}'-files installed.", language)); + const notificationService = accessor.get(INotificationService); + const workerService = accessor.get(IEditorWorkerService); + const { tabSize, insertSpaces } = editor.getModel().getOptions(); + return formatDocument(workerService, editor, { tabSize, insertSpaces }, CancellationToken.None).catch(err => { + if (NoProviderError.is(err)) { + notificationService.info(nls.localize('no.documentprovider', "There is no document formatter for '{0}'-files installed.", editor.getModel().getLanguageIdentifier().language)); + } + }); } } -export class FormatSelectionAction extends AbstractFormatAction { +export class FormatSelectionAction extends EditorAction { constructor() { super({ @@ -374,24 +408,18 @@ export class FormatSelectionAction extends AbstractFormatAction { }); } - protected _getFormattingEdits(editor: ICodeEditor, token: CancellationToken): Promise | undefined { + run(accessor: ServicesAccessor, editor: ICodeEditor): Promise | void { if (!editor.hasModel()) { - return undefined; + return; } - - const model = editor.getModel(); - let selection = editor.getSelection(); - if (selection.isEmpty()) { - const maxColumn = model.getLineMaxColumn(selection.startLineNumber); - selection = selection.setStartPosition(selection.startLineNumber, 1); - selection = selection.setEndPosition(selection.endLineNumber, maxColumn); - } - const { tabSize, insertSpaces } = model.getOptions(); - return getDocumentRangeFormattingEdits(model, selection, { tabSize, insertSpaces }, token); - } - - protected _notifyNoProviderError(notificationService: INotificationService, language: string): void { - notificationService.info(nls.localize('no.selectionprovider', "There is no selection formatter for '{0}'-files installed.", language)); + const notificationService = accessor.get(INotificationService); + const workerService = accessor.get(IEditorWorkerService); + const { tabSize, insertSpaces } = editor.getModel().getOptions(); + return formatDocumentRange(workerService, editor, FormatRangeType.Selection, { tabSize, insertSpaces }, CancellationToken.None).catch(err => { + if (NoProviderError.is(err)) { + notificationService.info(nls.localize('no.selectionprovider', "There is no selection formatter for '{0}'-files installed.", editor.getModel().getLanguageIdentifier().language)); + } + }); } } @@ -404,25 +432,15 @@ registerEditorAction(FormatSelectionAction); // and we keep it here such that existing keybinding configurations etc will still work CommandsRegistry.registerCommand('editor.action.format', accessor => { const editor = accessor.get(ICodeEditorService).getFocusedCodeEditor(); - if (editor) { - return new class extends AbstractFormatAction { - constructor() { - super({} as IActionOptions); - } - _getFormattingEdits(editor: ICodeEditor, token: CancellationToken): Promise | undefined { - if (!editor.hasModel()) { - return undefined; - } - - const model = editor.getModel(); - const editorSelection = editor.getSelection(); - const { tabSize, insertSpaces } = model.getOptions(); - - return editorSelection.isEmpty() - ? getDocumentFormattingEdits(model, { tabSize, insertSpaces }, token) - : getDocumentRangeFormattingEdits(model, editorSelection, { tabSize, insertSpaces }, token); - } - }().run(accessor, editor); + if (!editor || !editor.hasModel()) { + return undefined; + } + const { tabSize, insertSpaces } = editor.getModel().getOptions(); + const workerService = accessor.get(IEditorWorkerService); + + if (editor.getSelection().isEmpty()) { + return formatDocument(workerService, editor, { tabSize, insertSpaces }, CancellationToken.None); + } else { + return formatDocumentRange(workerService, editor, FormatRangeType.Selection, { tabSize, insertSpaces }, CancellationToken.None); } - return undefined; }); diff --git a/src/vs/editor/contrib/parameterHints/parameterHints.ts b/src/vs/editor/contrib/parameterHints/parameterHints.ts index c9104b79c65..2a6ac351cd9 100644 --- a/src/vs/editor/contrib/parameterHints/parameterHints.ts +++ b/src/vs/editor/contrib/parameterHints/parameterHints.ts @@ -12,10 +12,11 @@ import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { registerEditorAction, registerEditorContribution, ServicesAccessor, EditorAction, EditorCommand, registerEditorCommand } from 'vs/editor/browser/editorExtensions'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { ParameterHintsWidget, TriggerContext } from './parameterHintsWidget'; +import { ParameterHintsWidget } from './parameterHintsWidget'; import { Context } from 'vs/editor/contrib/parameterHints/provideSignatureHelp'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import * as modes from 'vs/editor/common/modes'; +import { TriggerContext } from 'vs/editor/contrib/parameterHints/parameterHintsModel'; class ParameterHintsController implements IEditorContribution { diff --git a/src/vs/editor/contrib/parameterHints/parameterHintsModel.ts b/src/vs/editor/contrib/parameterHints/parameterHintsModel.ts new file mode 100644 index 00000000000..a071dabec1a --- /dev/null +++ b/src/vs/editor/contrib/parameterHints/parameterHintsModel.ts @@ -0,0 +1,261 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CancelablePromise, createCancelablePromise, Delayer } from 'vs/base/common/async'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { Emitter } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; +import { CharacterSet } from 'vs/editor/common/core/characterClassifier'; +import * as modes from 'vs/editor/common/modes'; +import { provideSignatureHelp } from 'vs/editor/contrib/parameterHints/provideSignatureHelp'; + +export interface TriggerContext { + readonly triggerKind: modes.SignatureHelpTriggerKind; + readonly triggerCharacter?: string; +} + +const DefaultState = new class { readonly state = 'default'; }; +const PendingState = new class { readonly state = 'pending'; }; + +class ActiveState { + readonly state = 'active'; + constructor( + readonly hints: modes.SignatureHelp + ) { } +} + +type ParameterHintState = typeof DefaultState | typeof PendingState | ActiveState; + +export class ParameterHintsModel extends Disposable { + + private static readonly DEFAULT_DELAY = 120; // ms + + private readonly _onChangedHints = this._register(new Emitter()); + public readonly onChangedHints = this._onChangedHints.event; + + private editor: ICodeEditor; + private enabled: boolean; + private state: ParameterHintState = DefaultState; + private triggerChars = new CharacterSet(); + private retriggerChars = new CharacterSet(); + + private throttledDelayer: Delayer; + private provideSignatureHelpRequest?: CancelablePromise; + private triggerId = 0; + + constructor( + editor: ICodeEditor, + delay: number = ParameterHintsModel.DEFAULT_DELAY + ) { + super(); + + this.editor = editor; + this.enabled = false; + + this.throttledDelayer = new Delayer(delay); + + this._register(this.editor.onDidChangeConfiguration(() => this.onEditorConfigurationChange())); + this._register(this.editor.onDidChangeModel(e => this.onModelChanged())); + this._register(this.editor.onDidChangeModelLanguage(_ => this.onModelChanged())); + this._register(this.editor.onDidChangeCursorSelection(e => this.onCursorChange(e))); + this._register(this.editor.onDidChangeModelContent(e => this.onModelContentChange())); + this._register(modes.SignatureHelpProviderRegistry.onDidChange(this.onModelChanged, this)); + this._register(this.editor.onDidType(text => this.onDidType(text))); + + this.onEditorConfigurationChange(); + this.onModelChanged(); + } + + cancel(silent: boolean = false): void { + this.state = DefaultState; + + this.throttledDelayer.cancel(); + + if (!silent) { + this._onChangedHints.fire(undefined); + } + + if (this.provideSignatureHelpRequest) { + this.provideSignatureHelpRequest.cancel(); + this.provideSignatureHelpRequest = undefined; + } + } + + trigger(context: TriggerContext, delay?: number): void { + const model = this.editor.getModel(); + if (model === null || !modes.SignatureHelpProviderRegistry.has(model)) { + return; + } + + const triggerId = ++this.triggerId; + this.throttledDelayer.trigger( + () => this.doTrigger({ + triggerKind: context.triggerKind, + triggerCharacter: context.triggerCharacter, + isRetrigger: this.state.state === 'active' || this.state.state === 'pending', + }, triggerId), delay).then(undefined, onUnexpectedError); + } + + public next(): void { + if (this.state.state !== 'active') { + return; + } + + const length = this.state.hints.signatures.length; + const activeSignature = this.state.hints.activeSignature; + const last = (activeSignature % length) === (length - 1); + const cycle = this.editor.getConfiguration().contribInfo.parameterHints.cycle; + + // If there is only one signature, or we're on last signature of list + if ((length < 2 || last) && !cycle) { + this.cancel(); + return; + } + + this.updateActiveSignature(last && cycle ? 0 : activeSignature + 1); + } + + public previous(): void { + if (this.state.state !== 'active') { + return; + } + + const length = this.state.hints.signatures.length; + const activeSignature = this.state.hints.activeSignature; + const first = activeSignature === 0; + const cycle = this.editor.getConfiguration().contribInfo.parameterHints.cycle; + + // If there is only one signature, or we're on first signature of list + if ((length < 2 || first) && !cycle) { + this.cancel(); + return; + } + + this.updateActiveSignature(first && cycle ? length - 1 : activeSignature - 1); + } + + private updateActiveSignature(activeSignature: number) { + if (this.state.state !== 'active') { + return; + } + + this.state = { + state: 'active', + hints: { ...this.state.hints, activeSignature } + }; + this._onChangedHints.fire(this.state.hints); + } + + private doTrigger(triggerContext: modes.SignatureHelpContext, triggerId: number): Promise { + this.cancel(true); + + if (!this.editor.hasModel()) { + return Promise.resolve(false); + } + + const model = this.editor.getModel(); + const position = this.editor.getPosition(); + + this.state = PendingState; + + this.provideSignatureHelpRequest = createCancelablePromise(token => + provideSignatureHelp(model, position, triggerContext, token)); + + return this.provideSignatureHelpRequest.then(result => { + // Check that we are still resolving the correct signature help + if (triggerId !== this.triggerId) { + return false; + } + + if (!result || !result.signatures || result.signatures.length === 0) { + this.cancel(); + return false; + } else { + this.state = new ActiveState(result); + this._onChangedHints.fire(this.state.hints); + return true; + } + }).catch(error => { + this.state = DefaultState; + onUnexpectedError(error); + return false; + }); + } + + private get isTriggered(): boolean { + return this.state.state === 'active' || this.state.state === 'pending' || this.throttledDelayer.isTriggered(); + } + + private onModelChanged(): void { + this.cancel(); + + // Update trigger characters + this.triggerChars = new CharacterSet(); + this.retriggerChars = new CharacterSet(); + + const model = this.editor.getModel(); + if (!model) { + return; + } + + for (const support of modes.SignatureHelpProviderRegistry.ordered(model)) { + for (const ch of support.signatureHelpTriggerCharacters || []) { + this.triggerChars.add(ch.charCodeAt(0)); + + // All trigger characters are also considered retrigger characters + this.retriggerChars.add(ch.charCodeAt(0)); + } + + for (const ch of support.signatureHelpRetriggerCharacters || []) { + this.retriggerChars.add(ch.charCodeAt(0)); + } + } + } + + private onDidType(text: string) { + if (!this.enabled) { + return; + } + + const lastCharIndex = text.length - 1; + const triggerCharCode = text.charCodeAt(lastCharIndex); + + if (this.triggerChars.has(triggerCharCode) || this.isTriggered && this.retriggerChars.has(triggerCharCode)) { + this.trigger({ + triggerKind: modes.SignatureHelpTriggerKind.TriggerCharacter, + triggerCharacter: text.charAt(lastCharIndex), + }); + } + } + + private onCursorChange(e: ICursorSelectionChangedEvent): void { + if (e.source === 'mouse') { + this.cancel(); + } else if (this.isTriggered) { + this.trigger({ triggerKind: modes.SignatureHelpTriggerKind.ContentChange }); + } + } + + private onModelContentChange(): void { + if (this.isTriggered) { + this.trigger({ triggerKind: modes.SignatureHelpTriggerKind.ContentChange }); + } + } + + private onEditorConfigurationChange(): void { + this.enabled = this.editor.getConfiguration().contribInfo.parameterHints.enabled; + + if (!this.enabled) { + this.cancel(); + } + } + + dispose(): void { + this.cancel(true); + super.dispose(); + } +} \ No newline at end of file diff --git a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts index c4696af5f52..7d347210c2b 100644 --- a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts +++ b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts @@ -3,232 +3,28 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!./parameterHints'; -import * as nls from 'vs/nls'; -import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; import * as dom from 'vs/base/browser/dom'; -import * as aria from 'vs/base/browser/ui/aria/aria'; -import * as modes from 'vs/editor/common/modes'; -import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser'; -import { createCancelablePromise, CancelablePromise, Delayer } from 'vs/base/common/async'; -import { onUnexpectedError } from 'vs/base/common/errors'; -import { Event, Emitter } from 'vs/base/common/event'; import { domEvent, stop } from 'vs/base/browser/event'; -import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { Context, provideSignatureHelp } from 'vs/editor/contrib/parameterHints/provideSignatureHelp'; +import * as aria from 'vs/base/browser/ui/aria/aria'; import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; -import { CharacterSet } from 'vs/editor/common/core/characterClassifier'; +import { Event } from 'vs/base/common/event'; +import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import 'vs/css!./parameterHints'; +import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser'; import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions'; -import { ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; -import { registerThemingParticipant, HIGH_CONTRAST } from 'vs/platform/theme/common/themeService'; -import { editorHoverBackground, editorHoverBorder, textLinkForeground, textCodeBlockBackground } from 'vs/platform/theme/common/colorRegistry'; -import { IOpenerService } from 'vs/platform/opener/common/opener'; +import * as modes from 'vs/editor/common/modes'; import { IModeService } from 'vs/editor/common/services/modeService'; import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer'; +import { Context } from 'vs/editor/contrib/parameterHints/provideSignatureHelp'; +import * as nls from 'vs/nls'; +import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { editorHoverBackground, editorHoverBorder, textCodeBlockBackground, textLinkForeground } from 'vs/platform/theme/common/colorRegistry'; +import { HIGH_CONTRAST, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { ParameterHintsModel, TriggerContext } from 'vs/editor/contrib/parameterHints/parameterHintsModel'; const $ = dom.$; -export interface TriggerContext { - readonly triggerKind: modes.SignatureHelpTriggerKind; - readonly triggerCharacter?: string; -} - -export interface IHintEvent { - hints: modes.SignatureHelp; -} - -export class ParameterHintsModel extends Disposable { - - private static readonly DEFAULT_DELAY = 120; // ms - - private readonly _onHint = this._register(new Emitter()); - public readonly onHint: Event = this._onHint.event; - - private readonly _onCancel = this._register(new Emitter()); - public readonly onCancel: Event = this._onCancel.event; - - private editor: ICodeEditor; - private enabled: boolean; - private triggerCharactersListeners: IDisposable[]; - private active: boolean = false; - private pending: boolean = false; - private triggerChars = new CharacterSet(); - private retriggerChars = new CharacterSet(); - - private throttledDelayer: Delayer; - private provideSignatureHelpRequest?: CancelablePromise; - - constructor( - editor: ICodeEditor, - delay: number = ParameterHintsModel.DEFAULT_DELAY - ) { - super(); - - this.editor = editor; - this.enabled = false; - this.triggerCharactersListeners = []; - - this.throttledDelayer = new Delayer(delay); - - this._register(this.editor.onDidChangeConfiguration(() => this.onEditorConfigurationChange())); - this._register(this.editor.onDidChangeModel(e => this.onModelChanged())); - this._register(this.editor.onDidChangeModelLanguage(_ => this.onModelChanged())); - this._register(this.editor.onDidChangeCursorSelection(e => this.onCursorChange(e))); - this._register(this.editor.onDidChangeModelContent(e => this.onModelContentChange())); - this._register(modes.SignatureHelpProviderRegistry.onDidChange(this.onModelChanged, this)); - this._register(this.editor.onDidType(text => this.onDidType(text))); - - this.onEditorConfigurationChange(); - this.onModelChanged(); - } - - cancel(silent: boolean = false): void { - this.active = false; - this.pending = false; - - this.throttledDelayer.cancel(); - - if (!silent) { - this._onCancel.fire(void 0); - } - - if (this.provideSignatureHelpRequest) { - this.provideSignatureHelpRequest.cancel(); - this.provideSignatureHelpRequest = undefined; - } - } - - trigger(context: TriggerContext, delay?: number): void { - - const model = this.editor.getModel(); - if (model === null || !modes.SignatureHelpProviderRegistry.has(model)) { - return; - } - - this.throttledDelayer.trigger( - () => this.doTrigger({ - triggerKind: context.triggerKind, - triggerCharacter: context.triggerCharacter, - isRetrigger: this.isTriggered, - }), delay).then(undefined, onUnexpectedError); - } - - private doTrigger(triggerContext: modes.SignatureHelpContext): Promise { - this.cancel(true); - - if (!this.editor.hasModel()) { - return Promise.resolve(false); - } - - const model = this.editor.getModel(); - const position = this.editor.getPosition(); - - this.pending = true; - - this.provideSignatureHelpRequest = createCancelablePromise(token => - provideSignatureHelp(model!, position!, triggerContext, token)); - - return this.provideSignatureHelpRequest.then(result => { - this.pending = false; - - if (!result || !result.signatures || result.signatures.length === 0) { - this.cancel(); - this._onCancel.fire(void 0); - return false; - } - - this.active = true; - const event: IHintEvent = { hints: result }; - this._onHint.fire(event); - return true; - - }).catch(error => { - this.pending = false; - onUnexpectedError(error); - return false; - }); - } - - private get isTriggered(): boolean { - return this.active || this.pending || this.throttledDelayer.isTriggered(); - } - - private onModelChanged(): void { - this.cancel(); - - // Update trigger characters - this.triggerChars = new CharacterSet(); - this.retriggerChars = new CharacterSet(); - - const model = this.editor.getModel(); - if (!model) { - return; - } - - for (const support of modes.SignatureHelpProviderRegistry.ordered(model)) { - if (Array.isArray(support.signatureHelpTriggerCharacters)) { - for (const ch of support.signatureHelpTriggerCharacters) { - this.triggerChars.add(ch.charCodeAt(0)); - - // All trigger characters are also considered retrigger characters - this.retriggerChars.add(ch.charCodeAt(0)); - - } - } - if (Array.isArray(support.signatureHelpRetriggerCharacters)) { - for (const ch of support.signatureHelpRetriggerCharacters) { - this.retriggerChars.add(ch.charCodeAt(0)); - } - } - } - } - - private onDidType(text: string) { - if (!this.enabled) { - return; - } - - const lastCharIndex = text.length - 1; - const triggerCharCode = text.charCodeAt(lastCharIndex); - - if (this.triggerChars.has(triggerCharCode) || this.isTriggered && this.retriggerChars.has(triggerCharCode)) { - this.trigger({ - triggerKind: modes.SignatureHelpTriggerKind.TriggerCharacter, - triggerCharacter: text.charAt(lastCharIndex), - }); - } - } - - private onCursorChange(e: ICursorSelectionChangedEvent): void { - if (e.source === 'mouse') { - this.cancel(); - } else if (this.isTriggered) { - this.trigger({ triggerKind: modes.SignatureHelpTriggerKind.ContentChange }); - } - } - - private onModelContentChange(): void { - if (this.isTriggered) { - this.trigger({ triggerKind: modes.SignatureHelpTriggerKind.ContentChange }); - } - } - - private onEditorConfigurationChange(): void { - this.enabled = this.editor.getConfiguration().contribInfo.parameterHints.enabled; - - if (!this.enabled) { - this.cancel(); - } - } - - dispose(): void { - this.cancel(true); - this.triggerCharactersListeners = dispose(this.triggerCharactersListeners); - - super.dispose(); - } -} - export class ParameterHintsWidget implements IContentWidget, IDisposable { private static readonly ID = 'editor.widget.parameterHintsWidget'; @@ -242,9 +38,7 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable { private signature: HTMLElement; private docs: HTMLElement; private overloads: HTMLElement; - private currentSignature: number; private visible: boolean; - private hints: modes.SignatureHelp | null; private announcedLabel: string | null; private scrollbar: DomScrollableElement; private disposables: IDisposable[]; @@ -265,15 +59,13 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable { this.visible = false; this.disposables = []; - this.disposables.push(this.model.onHint(e => { - this.show(); - this.hints = e.hints; - this.currentSignature = e.hints.activeSignature; - this.render(); - })); - - this.disposables.push(this.model.onCancel(() => { - this.hide(); + this.disposables.push(this.model.onChangedHints(newParameterHints => { + if (newParameterHints) { + this.show(); + this.render(newParameterHints); + } else { + this.hide(); + } })); } @@ -302,8 +94,6 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable { this.docs = dom.append(body, $('.docs')); - this.currentSignature = 0; - this.editor.addContentWidget(this); this.hide(); @@ -355,7 +145,6 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable { this.keyVisible.reset(); this.visible = false; - this.hints = null; this.announcedLabel = null; dom.removeClass(this.element, 'visible'); this.editor.layoutContentWidget(this); @@ -371,19 +160,15 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable { return null; } - private render(): void { - if (!this.hints) { - return; - } - - const multiple = this.hints.signatures.length > 1; + private render(hints: modes.SignatureHelp): void { + const multiple = hints.signatures.length > 1; dom.toggleClass(this.element, 'multiple', multiple); this.keyMultipleSignatures.set(multiple); this.signature.innerHTML = ''; this.docs.innerHTML = ''; - const signature = this.hints.signatures[this.currentSignature]; + const signature = hints.signatures[hints.activeSignature]; if (!signature) { return; @@ -401,13 +186,13 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable { label.textContent = signature.label; } else { - this.renderParameters(code, signature, this.hints.activeParameter); + this.renderParameters(code, signature, hints.activeParameter); } dispose(this.renderDisposeables); this.renderDisposeables = []; - const activeParameter = signature.parameters[this.hints.activeParameter]; + const activeParameter = signature.parameters[hints.activeParameter]; if (activeParameter && activeParameter.documentation) { const documentation = $('span.documentation'); @@ -434,16 +219,16 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable { dom.append(this.docs, renderedContents.element); } - let currentOverload = String(this.currentSignature + 1); + let currentOverload = String(hints.activeSignature + 1); - if (this.hints.signatures.length < 10) { - currentOverload += `/${this.hints.signatures.length}`; + if (hints.signatures.length < 10) { + currentOverload += `/${hints.signatures.length}`; } this.overloads.textContent = currentOverload; if (activeParameter) { - const labelToAnnounce = this.getParameterLabel(signature, this.hints.activeParameter); + const labelToAnnounce = this.getParameterLabel(signature, hints.activeParameter); // Select method gets called on every user type while parameter hints are visible. // We do not want to spam the user with same announcements, so we only announce if the current parameter changed. @@ -497,85 +282,16 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable { } } - // private select(position: number): void { - // const signature = this.signatureViews[position]; - - // if (!signature) { - // return; - // } - - // this.signatures.style.height = `${ signature.height }px`; - // this.signatures.scrollTop = signature.top; - - // let overloads = '' + (position + 1); - - // if (this.signatureViews.length < 10) { - // overloads += '/' + this.signatureViews.length; - // } - - // this.overloads.textContent = overloads; - - // if (this.hints && this.hints.signatures[position].parameters[this.hints.activeParameter]) { - // const labelToAnnounce = this.hints.signatures[position].parameters[this.hints.activeParameter].label; - // // Select method gets called on every user type while parameter hints are visible. - // // We do not want to spam the user with same announcements, so we only announce if the current parameter changed. - // if (this.announcedLabel !== labelToAnnounce) { - // aria.alert(nls.localize('hint', "{0}, hint", labelToAnnounce)); - // this.announcedLabel = labelToAnnounce; - // } - // } - - // this.editor.layoutContentWidget(this); - // } - - next(): boolean { - if (!this.hints) { - return false; + next(): void { + if (this.model) { + this.model.next(); } - - const length = this.hints.signatures.length; - const last = (this.currentSignature % length) === (length - 1); - const cycle = this.editor.getConfiguration().contribInfo.parameterHints.cycle; - - // If there is only one signature, or we're on last signature of list - if ((length < 2 || last) && !cycle) { - this.cancel(); - return false; - } - - if (last && cycle) { - this.currentSignature = 0; - } else { - this.currentSignature++; - } - - this.render(); - return true; } - previous(): boolean { - if (!this.hints) { - return false; + previous(): void { + if (this.model) { + this.model.previous(); } - - const length = this.hints.signatures.length; - const first = this.currentSignature === 0; - const cycle = this.editor.getConfiguration().contribInfo.parameterHints.cycle; - - // If there is only one signature, or we're on first signature of list - if ((length < 2 || first) && !cycle) { - this.cancel(); - return false; - } - - if (first && cycle) { - this.currentSignature = length - 1; - } else { - this.currentSignature--; - } - - this.render(); - return true; } cancel(): void { diff --git a/src/vs/editor/contrib/parameterHints/test/parameterHintsModel.test.ts b/src/vs/editor/contrib/parameterHints/test/parameterHintsModel.test.ts index 8d80428ad0f..ff1931ae1e5 100644 --- a/src/vs/editor/contrib/parameterHints/test/parameterHintsModel.test.ts +++ b/src/vs/editor/contrib/parameterHints/test/parameterHintsModel.test.ts @@ -17,7 +17,7 @@ import { ServiceCollection } from 'vs/platform/instantiation/common/serviceColle import { IStorageService, InMemoryStorageService } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; -import { ParameterHintsModel } from '../parameterHintsWidget'; +import { ParameterHintsModel } from 'vs/editor/contrib/parameterHints/parameterHintsModel'; const mockFile = URI.parse('test:somefile.ttt'); const mockFileSelector = { scheme: 'test' }; @@ -89,12 +89,13 @@ suite('ParameterHintsModel', () => { if (invokeCount === 1) { assert.strictEqual(context.triggerKind, modes.SignatureHelpTriggerKind.TriggerCharacter); assert.strictEqual(context.triggerCharacter, triggerChar); + assert.strictEqual(context.isRetrigger, false); // Retrigger editor.trigger('keyboard', Handler.Type, { text: triggerChar }); } else { assert.strictEqual(invokeCount, 2); assert.strictEqual(context.triggerKind, modes.SignatureHelpTriggerKind.TriggerCharacter); - assert.ok(context.isRetrigger); + assert.strictEqual(context.isRetrigger, true); assert.strictEqual(context.triggerCharacter, triggerChar); done(); } @@ -122,6 +123,7 @@ suite('ParameterHintsModel', () => { if (invokeCount === 1) { assert.strictEqual(context.triggerKind, modes.SignatureHelpTriggerKind.TriggerCharacter); assert.strictEqual(context.triggerCharacter, triggerChar); + assert.strictEqual(context.isRetrigger, false); // Cancel and retrigger hintModel.cancel(); @@ -130,6 +132,8 @@ suite('ParameterHintsModel', () => { assert.strictEqual(invokeCount, 2); assert.strictEqual(context.triggerKind, modes.SignatureHelpTriggerKind.TriggerCharacter); assert.strictEqual(context.triggerCharacter, triggerChar); + assert.strictEqual(context.isRetrigger, false); + done(); } return emptySigHelpResult; @@ -244,10 +248,10 @@ suite('ParameterHintsModel', () => { assert.strictEqual(-1, didRequestCancellationOf); return new Promise((resolve, reject) => - hintsModel.onHint(e => { + hintsModel.onChangedHints(newParamterHints => { try { assert.strictEqual(0, didRequestCancellationOf); - assert.strictEqual('1', e.hints.signatures[0].label); + assert.strictEqual('1', newParamterHints!.signatures[0].label); resolve(); } catch (e) { reject(e); diff --git a/src/vs/editor/contrib/referenceSearch/referencesTree.ts b/src/vs/editor/contrib/referenceSearch/referencesTree.ts index 73508ceaaa4..e9282d70092 100644 --- a/src/vs/editor/contrib/referenceSearch/referencesTree.ts +++ b/src/vs/editor/contrib/referenceSearch/referencesTree.ts @@ -20,7 +20,10 @@ import { escape } from 'vs/base/common/strings'; import { Disposable } from 'vs/base/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; -import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; +import { IListVirtualDelegate, IKeyboardNavigationLabelProvider } from 'vs/base/browser/ui/list/list'; +import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { basename } from 'vs/base/common/paths'; //#region data source @@ -75,6 +78,21 @@ export class Delegate implements IListVirtualDelegate { } } +export class StringRepresentationProvider implements IKeyboardNavigationLabelProvider { + + constructor(@IKeybindingService private readonly _keybindingService: IKeybindingService) { } + + getKeyboardNavigationLabel(element: TreeElement): { toString(): string; } { + // todo@joao `OneReference` elements are lazy and their "real" label + // isn't known yet + return basename(element.uri.path); + } + + mightProducePrintableCharacter(event: IKeyboardEvent): boolean { + return this._keybindingService.mightProducePrintableCharacter(event); + } +} + //#region render: File class FileReferencesTemplate extends Disposable { @@ -100,7 +118,7 @@ class FileReferencesTemplate extends Disposable { set(element: FileReferences) { let parent = dirname(element.uri); - this.file.setValue(getBaseLabel(element.uri), parent ? this._uriLabel.getUriLabel(parent, { relative: true }) : undefined, { title: this._uriLabel.getUriLabel(element.uri) }); + this.file.setLabel(getBaseLabel(element.uri), parent ? this._uriLabel.getUriLabel(parent, { relative: true }) : undefined, { title: this._uriLabel.getUriLabel(element.uri) }); const len = element.children.length; this.badge.setCount(len); if (element.failure) { diff --git a/src/vs/editor/contrib/referenceSearch/referencesWidget.ts b/src/vs/editor/contrib/referenceSearch/referencesWidget.ts index 403567a3094..affb6a2eb52 100644 --- a/src/vs/editor/contrib/referenceSearch/referencesWidget.ts +++ b/src/vs/editor/contrib/referenceSearch/referencesWidget.ts @@ -21,7 +21,7 @@ import { IModelDeltaDecoration, TrackedRangeStickiness } from 'vs/editor/common/ import { ModelDecorationOptions, TextModel } from 'vs/editor/common/model/textModel'; import { Location } from 'vs/editor/common/modes'; import { ITextEditorModel, ITextModelService } from 'vs/editor/common/services/resolverService'; -import { AriaProvider, DataSource, Delegate, FileReferencesRenderer, OneReferenceRenderer, TreeElement } from 'vs/editor/contrib/referenceSearch/referencesTree'; +import { AriaProvider, DataSource, Delegate, FileReferencesRenderer, OneReferenceRenderer, TreeElement, StringRepresentationProvider } from 'vs/editor/contrib/referenceSearch/referencesTree'; import * as nls from 'vs/nls'; import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -353,7 +353,8 @@ export class ReferenceWidget extends PeekViewWidget { const treeOptions = { ariaLabel: nls.localize('treeAriaLabel', "References"), keyboardSupport: this._defaultTreeKeyboardSupport, - accessibilityProvider: new AriaProvider() + accessibilityProvider: new AriaProvider(), + keyboardNavigationLabelProvider: this._instantiationService.createInstance(StringRepresentationProvider) }; const treeDataSource = this._instantiationService.createInstance(DataSource); diff --git a/src/vs/editor/contrib/snippet/snippetController2.ts b/src/vs/editor/contrib/snippet/snippetController2.ts index df08a20b3a9..8db5ba1e17d 100644 --- a/src/vs/editor/contrib/snippet/snippetController2.ts +++ b/src/vs/editor/contrib/snippet/snippetController2.ts @@ -34,10 +34,10 @@ export class SnippetController2 implements IEditorContribution { private readonly _hasNextTabstop: IContextKey; private readonly _hasPrevTabstop: IContextKey; - private _session: SnippetSession; + private _session?: SnippetSession; private _snippetListener: IDisposable[] = []; private _modelVersionId: number; - private _currentChoice: Choice; + private _currentChoice?: Choice; constructor( private readonly _editor: ICodeEditor, @@ -87,6 +87,9 @@ export class SnippetController2 implements IEditorContribution { undoStopBefore: boolean = true, undoStopAfter: boolean = true, adjustWhitespace: boolean = true, ): void { + if (!this._editor.hasModel()) { + return; + } // don't listen while inserting the snippet // as that is the inflight state causing cancelation @@ -118,7 +121,7 @@ export class SnippetController2 implements IEditorContribution { } private _updateState(): void { - if (!this._session) { + if (!this._session || !this._editor.hasModel()) { // canceled in the meanwhile return; } @@ -147,6 +150,11 @@ export class SnippetController2 implements IEditorContribution { } private _handleChoice(): void { + if (!this._session) { + this._currentChoice = undefined; + return; + } + const { choice } = this._session; if (!choice) { this._currentChoice = undefined; @@ -196,12 +204,16 @@ export class SnippetController2 implements IEditorContribution { } prev(): void { - this._session.prev(); + if (this._session) { + this._session.prev(); + } this._updateState(); } next(): void { - this._session.next(); + if (this._session) { + this._session.next(); + } this._updateState(); } @@ -209,7 +221,7 @@ export class SnippetController2 implements IEditorContribution { return this._inSnippet.get(); } - getSessionEnclosingRange(): Range { + getSessionEnclosingRange(): Range | undefined { if (this._session) { return this._session.getEnclosingRange(); } diff --git a/src/vs/editor/contrib/snippet/snippetSession.ts b/src/vs/editor/contrib/snippet/snippetSession.ts index 17bf8b96b3a..98c7d98d131 100644 --- a/src/vs/editor/contrib/snippet/snippetSession.ts +++ b/src/vs/editor/contrib/snippet/snippetSession.ts @@ -95,6 +95,9 @@ export class OneSnippet { } move(fwd: boolean | undefined): Selection[] { + if (!this._editor.hasModel()) { + return []; + } this._initDecorations(); @@ -190,7 +193,7 @@ export class OneSnippet { computePossibleSelections() { const result = new Map(); for (const placeholdersWithEqualIndex of this._placeholderGroups) { - let ranges: Range[]; + let ranges: Range[] | undefined; for (const placeholder of placeholdersWithEqualIndex) { if (placeholder.isFinalTabstop) { @@ -278,7 +281,7 @@ export class OneSnippet { } public getEnclosingRange(): Range { - let result: Range; + let result: Range | undefined; const model = this._editor.getModel(); this._placeholderDecorations.forEach((decorationId) => { const placeholderRange = model.getDecorationRange(decorationId); @@ -341,11 +344,14 @@ export class SnippetSession { } static createEditsAndSnippets(editor: ICodeEditor, template: string, overwriteBefore: number, overwriteAfter: number, enforceFinalTabstop: boolean, adjustWhitespace: boolean): { edits: IIdentifiedSingleEditOperation[], snippets: OneSnippet[] } { - - const model = editor.getModel(); const edits: IIdentifiedSingleEditOperation[] = []; const snippets: OneSnippet[] = []; + if (!editor.hasModel()) { + return { edits, snippets }; + } + const model = editor.getModel(); + const modelBasedVariableResolver = new ModelBasedVariableResolver(model); const clipboardService = editor.invokeWithinContext(accessor => accessor.get(IClipboardService, optional)); @@ -444,6 +450,9 @@ export class SnippetSession { } insert(): void { + if (!this._editor.hasModel()) { + return; + } const model = this._editor.getModel(); @@ -463,6 +472,9 @@ export class SnippetSession { } merge(template: string, overwriteBefore: number = 0, overwriteAfter: number = 0, adjustWhitespace: boolean = true): void { + if (!this._editor.hasModel()) { + return; + } this._templateMerges.push([this._snippets[0]._nestingLevel, this._snippets[0]._placeholderGroupsIdx, template]); const { edits, snippets } = SnippetSession.createEditsAndSnippets(this._editor, template, overwriteBefore, overwriteAfter, true, adjustWhitespace); @@ -596,7 +608,7 @@ export class SnippetSession { } public getEnclosingRange(): Range { - let result: Range; + let result: Range | undefined; for (const snippet of this._snippets) { const snippetRange = snippet.getEnclosingRange(); if (!result) { diff --git a/src/vs/editor/contrib/suggest/suggestModel.ts b/src/vs/editor/contrib/suggest/suggestModel.ts index 763ca02bbf9..900f7b2d66b 100644 --- a/src/vs/editor/contrib/suggest/suggestModel.ts +++ b/src/vs/editor/contrib/suggest/suggestModel.ts @@ -104,9 +104,9 @@ export class SuggestModel implements IDisposable { private _currentSelection: Selection; private _completionModel?: CompletionModel; - private readonly _onDidCancel: Emitter = new Emitter(); - private readonly _onDidTrigger: Emitter = new Emitter(); - private readonly _onDidSuggest: Emitter = new Emitter(); + private readonly _onDidCancel = new Emitter(); + private readonly _onDidTrigger = new Emitter(); + private readonly _onDidSuggest = new Emitter(); readonly onDidCancel: Event = this._onDidCancel.event; readonly onDidTrigger: Event = this._onDidTrigger.event; diff --git a/src/vs/editor/contrib/suggest/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts index de7330e305e..ae57d64b7db 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -185,7 +185,7 @@ class Renderer implements IListRenderer ]; } - data.iconLabel.setValue(suggestion.label, undefined, labelOptions); + data.iconLabel.setLabel(suggestion.label, undefined, labelOptions); data.typeLabel.textContent = (suggestion.detail || '').replace(/\n.*$/m, ''); if (canExpandCompletionItem(element)) { diff --git a/src/vs/editor/standalone/browser/simpleServices.ts b/src/vs/editor/standalone/browser/simpleServices.ts index 2a2b5dc59ad..84cc477a801 100644 --- a/src/vs/editor/standalone/browser/simpleServices.ts +++ b/src/vs/editor/standalone/browser/simpleServices.ts @@ -116,6 +116,10 @@ export class SimpleEditorModelResolverService implements ITextModelService { }; } + public hasTextModelContentProvider(scheme: string): boolean { + return false; + } + private findModel(editor: ICodeEditor, resource: URI): ITextModel | null { let model = editor.getModel(); if (model && model.uri.toString() !== resource.toString()) { @@ -218,7 +222,7 @@ export class StandaloneCommandService implements ICommandService { private readonly _instantiationService: IInstantiationService; private _dynamicCommands: { [id: string]: ICommand; }; - private readonly _onWillExecuteCommand: Emitter = new Emitter(); + private readonly _onWillExecuteCommand = new Emitter(); public readonly onWillExecuteCommand: Event = this._onWillExecuteCommand.event; constructor(instantiationService: IInstantiationService) { @@ -502,13 +506,13 @@ export class SimpleWorkspaceContextService implements IWorkspaceContextService { private static SCHEME = 'inmemory'; - private readonly _onDidChangeWorkspaceName: Emitter = new Emitter(); + private readonly _onDidChangeWorkspaceName = new Emitter(); public readonly onDidChangeWorkspaceName: Event = this._onDidChangeWorkspaceName.event; - private readonly _onDidChangeWorkspaceFolders: Emitter = new Emitter(); + private readonly _onDidChangeWorkspaceFolders = new Emitter(); public readonly onDidChangeWorkspaceFolders: Event = this._onDidChangeWorkspaceFolders.event; - private readonly _onDidChangeWorkbenchState: Emitter = new Emitter(); + private readonly _onDidChangeWorkbenchState = new Emitter(); public readonly onDidChangeWorkbenchState: Event = this._onDidChangeWorkbenchState.event; private readonly workspace: IWorkspace; @@ -608,7 +612,7 @@ export class SimpleBulkEditService implements IBulkEditService { export class SimpleUriLabelService implements ILabelService { _serviceBrand: any; - private readonly _onDidRegisterFormatter: Emitter = new Emitter(); + private readonly _onDidRegisterFormatter = new Emitter(); public readonly onDidRegisterFormatter: Event = this._onDidRegisterFormatter.event; public getUriLabel(resource: URI, options?: { relative?: boolean, forceNoTildify?: boolean }): string { diff --git a/src/vs/editor/test/browser/editorTestServices.ts b/src/vs/editor/test/browser/editorTestServices.ts index 31a6abfe92c..a4fd499a1f8 100644 --- a/src/vs/editor/test/browser/editorTestServices.ts +++ b/src/vs/editor/test/browser/editorTestServices.ts @@ -29,7 +29,7 @@ export class TestCommandService implements ICommandService { private readonly _instantiationService: IInstantiationService; - private readonly _onWillExecuteCommand: Emitter = new Emitter(); + private readonly _onWillExecuteCommand = new Emitter(); public readonly onWillExecuteCommand: Event = this._onWillExecuteCommand.event; constructor(instantiationService: IInstantiationService) { diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index e4b771b30d1..8224e78e7f8 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -2787,6 +2787,11 @@ declare namespace monaco.editor { * Defaults to 1. */ mouseWheelScrollSensitivity?: number; + /** + * FastScrolling mulitplier speed when pressing `Alt` + * Defaults to 5. + */ + fastScrollSensitivity?: number; /** * The modifier to be used to add multiple cursors with the mouse. * Defaults to 'alt' @@ -3149,6 +3154,7 @@ declare namespace monaco.editor { readonly verticalScrollbarSize: number; readonly verticalSliderSize: number; readonly mouseWheelScrollSensitivity: number; + readonly fastScrollSensitivity: number; } export interface InternalEditorMinimapOptions { diff --git a/src/vs/platform/backup/electron-main/backupMainService.ts b/src/vs/platform/backup/electron-main/backupMainService.ts index e34e8728d3e..38ba6e62460 100644 --- a/src/vs/platform/backup/electron-main/backupMainService.ts +++ b/src/vs/platform/backup/electron-main/backupMainService.ts @@ -7,7 +7,7 @@ import * as fs from 'fs'; import * as path from 'path'; import * as crypto from 'crypto'; import * as platform from 'vs/base/common/platform'; -import * as extfs from 'vs/base/node/extfs'; +import { writeFileAndFlushSync, readdirSync, delSync } from 'vs/base/node/extfs'; import * as arrays from 'vs/base/common/arrays'; import { IBackupMainService, IBackupWorkspacesFormat, IEmptyWindowBackupInfo } from 'vs/platform/backup/common/backup'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; @@ -23,14 +23,14 @@ import { Schemas } from 'vs/base/common/network'; export class BackupMainService implements IBackupMainService { - public _serviceBrand: any; + _serviceBrand: any; protected backupHome: string; protected workspacesJsonPath: string; - protected rootWorkspaces: IWorkspaceIdentifier[]; - protected folderWorkspaces: URI[]; - protected emptyWorkspaces: IEmptyWindowBackupInfo[]; + private rootWorkspaces: IWorkspaceIdentifier[]; + private folderWorkspaces: URI[]; + private emptyWorkspaces: IEmptyWindowBackupInfo[]; constructor( @IEnvironmentService environmentService: IEnvironmentService, @@ -43,7 +43,7 @@ export class BackupMainService implements IBackupMainService { this.loadSync(); } - public getWorkspaceBackups(): IWorkspaceIdentifier[] { + getWorkspaceBackups(): IWorkspaceIdentifier[] { if (this.isHotExitOnExitAndWindowClose()) { // Only non-folder windows are restored on main process launch when // hot exit is configured as onExitAndWindowClose. @@ -53,16 +53,17 @@ export class BackupMainService implements IBackupMainService { return this.rootWorkspaces.slice(0); // return a copy } - public getFolderBackupPaths(): URI[] { + getFolderBackupPaths(): URI[] { if (this.isHotExitOnExitAndWindowClose()) { // Only non-folder windows are restored on main process launch when // hot exit is configured as onExitAndWindowClose. return []; } + return this.folderWorkspaces.slice(0); // return a copy } - public isHotExitEnabled(): boolean { + isHotExitEnabled(): boolean { return this.getHotExitConfig() !== HotExitConfiguration.OFF; } @@ -76,11 +77,11 @@ export class BackupMainService implements IBackupMainService { return (config && config.files && config.files.hotExit) || HotExitConfiguration.ON_EXIT; } - public getEmptyWindowBackupPaths(): IEmptyWindowBackupInfo[] { + getEmptyWindowBackupPaths(): IEmptyWindowBackupInfo[] { return this.emptyWorkspaces.slice(0); // return a copy } - public registerWorkspaceBackupSync(workspace: IWorkspaceIdentifier, migrateFrom?: string): string { + registerWorkspaceBackupSync(workspace: IWorkspaceIdentifier, migrateFrom?: string): string { if (!this.rootWorkspaces.some(w => w.id === workspace.id)) { this.rootWorkspaces.push(workspace); this.saveSync(); @@ -112,7 +113,7 @@ export class BackupMainService implements IBackupMainService { } } - public unregisterWorkspaceBackupSync(workspace: IWorkspaceIdentifier): void { + unregisterWorkspaceBackupSync(workspace: IWorkspaceIdentifier): void { let index = arrays.firstIndex(this.rootWorkspaces, w => w.id === workspace.id); if (index !== -1) { this.rootWorkspaces.splice(index, 1); @@ -120,15 +121,16 @@ export class BackupMainService implements IBackupMainService { } } - public registerFolderBackupSync(folderUri: URI): string { + registerFolderBackupSync(folderUri: URI): string { if (!this.folderWorkspaces.some(uri => areResourcesEquals(folderUri, uri))) { this.folderWorkspaces.push(folderUri); this.saveSync(); } + return this.getBackupPath(this.getFolderHash(folderUri)); } - public unregisterFolderBackupSync(folderUri: URI): void { + unregisterFolderBackupSync(folderUri: URI): void { let index = arrays.firstIndex(this.folderWorkspaces, uri => areResourcesEquals(folderUri, uri)); if (index !== -1) { this.folderWorkspaces.splice(index, 1); @@ -136,22 +138,24 @@ export class BackupMainService implements IBackupMainService { } } - public registerEmptyWindowBackupSync(backupInfo: IEmptyWindowBackupInfo): string { - + registerEmptyWindowBackupSync(backupInfo: IEmptyWindowBackupInfo): string { let backupFolder = backupInfo.backupFolder; let remoteAuthority = backupInfo.remoteAuthority; + // Generate a new folder if this is a new empty workspace if (!backupFolder) { backupFolder = this.getRandomEmptyWindowId(); } + if (!this.emptyWorkspaces.some(w => isEqual(w.backupFolder, backupFolder, !platform.isLinux))) { this.emptyWorkspaces.push({ backupFolder, remoteAuthority }); this.saveSync(); } + return this.getBackupPath(backupFolder); } - public unregisterEmptyWindowBackupSync(backupFolder: string): void { + unregisterEmptyWindowBackupSync(backupFolder: string): void { let index = arrays.firstIndex(this.emptyWorkspaces, w => isEqual(w.backupFolder, backupFolder, !platform.isLinux)); if (index !== -1) { this.emptyWorkspaces.splice(index, 1); @@ -160,7 +164,6 @@ export class BackupMainService implements IBackupMainService { } protected loadSync(): void { - let backups: IBackupWorkspacesFormat; try { backups = JSON.parse(fs.readFileSync(this.workspacesJsonPath, 'utf8').toString()); // invalid JSON or permission issue can happen here @@ -202,11 +205,11 @@ export class BackupMainService implements IBackupMainService { } catch (e) { // ignore URI parsing exceptions } + this.folderWorkspaces = this.validateFolders(workspaceFolders); // save again in case some workspaces or folders have been removed this.saveSync(); - } private getBackupPath(oldFolderHash: string): string { @@ -246,6 +249,7 @@ export class BackupMainService implements IBackupMainService { } } } + return result; } @@ -256,7 +260,6 @@ export class BackupMainService implements IBackupMainService { const result: URI[] = []; const seen: { [id: string]: boolean } = Object.create(null); - for (let folderURI of folderWorkspaces) { const key = getComparisonKey(folderURI); if (!seen[key]) { @@ -281,6 +284,7 @@ export class BackupMainService implements IBackupMainService { return result; } + private validateEmptyWorkspaces(emptyWorkspaces: IEmptyWindowBackupInfo[]): IEmptyWindowBackupInfo[] { if (!Array.isArray(emptyWorkspaces)) { return []; @@ -314,7 +318,7 @@ export class BackupMainService implements IBackupMainService { private deleteStaleBackup(backupPath: string) { try { if (fs.existsSync(backupPath)) { - extfs.delSync(backupPath); + delSync(backupPath); } } catch (ex) { this.logService.error(`Backup: Could not delete stale backup: ${ex.toString()}`); @@ -344,14 +348,14 @@ export class BackupMainService implements IBackupMainService { private hasBackupsSync(backupPath: string): boolean { try { - const backupSchemas = extfs.readdirSync(backupPath); + const backupSchemas = readdirSync(backupPath); if (backupSchemas.length === 0) { return false; // empty backups } return backupSchemas.some(backupSchema => { try { - return extfs.readdirSync(path.join(backupPath, backupSchema)).length > 0; + return readdirSync(path.join(backupPath, backupSchema)).length > 0; } catch (error) { return false; // invalid folder } @@ -363,17 +367,14 @@ export class BackupMainService implements IBackupMainService { private saveSync(): void { try { - // The user data directory must exist so only the Backup directory needs to be checked. - if (!fs.existsSync(this.backupHome)) { - fs.mkdirSync(this.backupHome); - } const backups: IBackupWorkspacesFormat = { rootWorkspaces: this.rootWorkspaces, folderURIWorkspaces: this.folderWorkspaces.map(f => f.toString()), emptyWorkspaceInfos: this.emptyWorkspaces, emptyWorkspaces: this.emptyWorkspaces.map(info => info.backupFolder) }; - extfs.writeFileAndFlushSync(this.workspacesJsonPath, JSON.stringify(backups)); + + writeFileAndFlushSync(this.workspacesJsonPath, JSON.stringify(backups)); } catch (ex) { this.logService.error(`Backup: Could not save workspaces.json: ${ex.toString()}`); } @@ -384,14 +385,15 @@ export class BackupMainService implements IBackupMainService { } protected getFolderHash(folderUri: URI): string { - let key; + let key: string; + if (folderUri.scheme === Schemas.file) { // for backward compatibility, use the fspath as key key = platform.isLinux ? folderUri.fsPath : folderUri.fsPath.toLowerCase(); - } else { key = hasToIgnoreCase(folderUri) ? folderUri.toString().toLowerCase() : folderUri.toString(); } + return crypto.createHash('md5').update(key).digest('hex'); } diff --git a/src/vs/platform/backup/test/electron-main/backupMainService.test.ts b/src/vs/platform/backup/test/electron-main/backupMainService.test.ts index ce8d19d2831..7921caa38c0 100644 --- a/src/vs/platform/backup/test/electron-main/backupMainService.test.ts +++ b/src/vs/platform/backup/test/electron-main/backupMainService.test.ts @@ -109,12 +109,13 @@ suite('BackupMainService', () => { let configService: TestConfigurationService; setup(() => { - configService = new TestConfigurationService(); - service = new TestBackupMainService(backupHome, backupWorkspacesPath, configService); // Delete any existing backups completely and then re-create it. return pfs.del(backupHome, os.tmpdir()).then(() => { return pfs.mkdirp(backupHome); + }).then(() => { + configService = new TestConfigurationService(); + service = new TestBackupMainService(backupHome, backupWorkspacesPath, configService); }); }); diff --git a/src/vs/platform/configuration/common/configurationRegistry.ts b/src/vs/platform/configuration/common/configurationRegistry.ts index a22359e8cb4..4cea7236d0d 100644 --- a/src/vs/platform/configuration/common/configurationRegistry.ts +++ b/src/vs/platform/configuration/common/configurationRegistry.ts @@ -10,6 +10,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import * as types from 'vs/base/common/types'; import * as strings from 'vs/base/common/strings'; import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; +import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; export const Extensions = { Configuration: 'base.contributions.configuration' @@ -93,7 +94,7 @@ export interface IConfigurationNode { } export interface IDefaultConfigurationExtension { - id: string; + id: CanonicalExtensionIdentifier; name: string; defaults: { [key: string]: {} }; } @@ -115,7 +116,7 @@ class ConfigurationRegistry implements IConfigurationRegistry { private overrideIdentifiers: string[] = []; private overridePropertyPattern: string; - private readonly _onDidSchemaChange: Emitter = new Emitter(); + private readonly _onDidSchemaChange = new Emitter(); readonly onDidSchemaChange: Event = this._onDidSchemaChange.event; private readonly _onDidRegisterConfiguration: Emitter = new Emitter(); diff --git a/src/vs/platform/dialogs/node/dialogService.ts b/src/vs/platform/dialogs/node/dialogService.ts deleted file mode 100644 index cf04c7608e7..00000000000 --- a/src/vs/platform/dialogs/node/dialogService.ts +++ /dev/null @@ -1,67 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as readline from 'readline'; -import { IDialogService, IConfirmation, IConfirmationResult } from 'vs/platform/dialogs/common/dialogs'; -import Severity from 'vs/base/common/severity'; -import { localize } from 'vs/nls'; -import { canceled } from 'vs/base/common/errors'; - -export class CommandLineDialogService implements IDialogService { - - _serviceBrand: any; - - show(severity: Severity, message: string, options: string[]): Promise { - const promise = new Promise((c, e) => { - const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout, - terminal: true - }); - rl.prompt(); - rl.write(this.toQuestion(message, options)); - - rl.prompt(); - - rl.once('line', (answer) => { - rl.close(); - c(this.toOption(answer, options)); - }); - rl.once('SIGINT', () => { - rl.close(); - e(canceled()); - }); - }); - return promise; - } - - private toQuestion(message: string, options: string[]): string { - return options.reduce((previousValue: string, currentValue: string, currentIndex: number) => { - return previousValue + currentValue + '(' + currentIndex + ')' + (currentIndex < options.length - 1 ? ' | ' : '\n'); - }, message + ' '); - } - - private toOption(answer: string, options: string[]): number { - const value = parseInt(answer); - if (!isNaN(value)) { - return value; - } - answer = answer.toLocaleLowerCase(); - for (let i = 0; i < options.length; i++) { - if (options[i].toLocaleLowerCase() === answer) { - return i; - } - } - return -1; - } - - confirm(confirmation: IConfirmation): Promise { - return this.show(Severity.Info, confirmation.message, [confirmation.primaryButton || localize('ok', "Ok"), confirmation.secondaryButton || localize('cancel', "Cancel")]).then(index => { - return { - confirmed: index === 0 - } as IConfirmationResult; - }); - } -} diff --git a/src/vs/platform/extensionManagement/test/electron-browser/extensionEnablementService.test.ts b/src/vs/platform/extensionManagement/test/electron-browser/extensionEnablementService.test.ts index 5fa1d7cbb21..b898a8f1350 100644 --- a/src/vs/platform/extensionManagement/test/electron-browser/extensionEnablementService.test.ts +++ b/src/vs/platform/extensionManagement/test/electron-browser/extensionEnablementService.test.ts @@ -45,8 +45,8 @@ suite('ExtensionEnablementService Test', () => { let instantiationService: TestInstantiationService; let testObject: IExtensionEnablementService; - const didUninstallEvent: Emitter = new Emitter(); - const didInstallEvent: Emitter = new Emitter(); + const didUninstallEvent = new Emitter(); + const didInstallEvent = new Emitter(); setup(() => { instantiationService = new TestInstantiationService(); diff --git a/src/vs/platform/extensions/common/extensions.ts b/src/vs/platform/extensions/common/extensions.ts index 25998a367b2..101110e9403 100644 --- a/src/vs/platform/extensions/common/extensions.ts +++ b/src/vs/platform/extensions/common/extensions.ts @@ -6,6 +6,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IExtensionManifest } from 'vs/platform/extensionManagement/common/extensionManagement'; import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import * as strings from 'vs/base/common/strings'; export const MANIFEST_CACHE_FOLDER = 'CachedExtensions'; export const USER_MANIFEST_CACHE_FILE = 'user'; @@ -31,3 +32,58 @@ export function isUIExtension(manifest: IExtensionManifest, configurationService default: return uiExtensions.has(extensionId) || !manifest.main; } } + +/** + * **!Do not construct directly!** + * + * **!Only static methods because it gets serialized!** + * + * This represents the "canonical" version for an extension identifier. Extension ids + * have to be case-insensitive (due to the marketplace), but we must ensure case + * preservation because the extension API is already public at this time. + * + * For example, given an extension with the publisher `"Hello"` and the name `"World"`, + * its canonical extension identifier is `"Hello.World"`. This extension could be + * referenced in some other extension's dependencies using the string `"hello.world"`. + * + * To make matters more complicated, an extension can optionally have an UUID. When two + * extensions have the same UUID, they are considered equal even if their identifier is different. + */ +export class CanonicalExtensionIdentifier { + public readonly value: string; + private readonly _lower: string; + + constructor(value: string) { + this.value = value; + this._lower = value.toLowerCase(); + } + + public static equals(a: CanonicalExtensionIdentifier | string | null | undefined, b: CanonicalExtensionIdentifier | string | null | undefined) { + if (typeof a === 'undefined' || a === null) { + return (typeof b === 'undefined' || b === null); + } + if (typeof b === 'undefined' || b === null) { + return false; + } + if (typeof a === 'string' || typeof b === 'string') { + // At least one of the arguments is an extension id in string form, + // so we have to use the string comparison which ignores case. + let aValue = (typeof a === 'string' ? a : a.value); + let bValue = (typeof b === 'string' ? b : b.value); + return strings.equalsIgnoreCase(aValue, bValue); + } + + // Now we know both arguments are CanonicalExtensionIdentifier + return (a._lower === b._lower); + } + + /** + * Gives the value by which to index (for equality). + */ + public static toKey(id: CanonicalExtensionIdentifier | string): string { + if (typeof id === 'string') { + return id.toLowerCase(); + } + return id._lower; + } +} diff --git a/src/vs/platform/history/electron-main/historyMainService.ts b/src/vs/platform/history/electron-main/historyMainService.ts index 88e7c2e5b2a..ac90272ec41 100644 --- a/src/vs/platform/history/electron-main/historyMainService.ts +++ b/src/vs/platform/history/electron-main/historyMainService.ts @@ -180,7 +180,6 @@ export class HistoryMainService implements IHistoryMainService { // out of sync quickly over time. the attempted fix is to always set the list fresh // from our MRU history data. So we clear the documents first and then set the documents // again. - app.clearRecentDocuments(); const mru = this.getRecentlyOpened(); @@ -257,6 +256,7 @@ export class HistoryMainService implements IHistoryMainService { if (workspaceOrFile instanceof URI) { return getComparisonKey(workspaceOrFile); } + return workspaceOrFile.id; } @@ -286,6 +286,7 @@ export class HistoryMainService implements IHistoryMainService { } } } + if (Array.isArray(storedRecents.files2)) { for (const file of storedRecents.files2) { if (typeof file === 'string') { @@ -300,11 +301,13 @@ export class HistoryMainService implements IHistoryMainService { } } } + return result; } private saveRecentlyOpened(recent: IRecentlyOpened): void { const serialized: ISerializedRecentlyOpened = { workspaces2: [], files2: [] }; + for (const workspace of recent.workspaces) { if (isSingleFolderWorkspaceIdentifier(workspace)) { serialized.workspaces2.push(workspace.toString()); @@ -312,9 +315,11 @@ export class HistoryMainService implements IHistoryMainService { serialized.workspaces2.push(workspace); } } + for (const file of recent.files) { serialized.files2.push(file.toString()); } + this.stateService.setItem(HistoryMainService.recentlyOpenedStorageKey, serialized); } diff --git a/src/vs/platform/jsonschemas/common/jsonContributionRegistry.ts b/src/vs/platform/jsonschemas/common/jsonContributionRegistry.ts index bc631b93ef7..2f173cd9838 100644 --- a/src/vs/platform/jsonschemas/common/jsonContributionRegistry.ts +++ b/src/vs/platform/jsonschemas/common/jsonContributionRegistry.ts @@ -52,7 +52,7 @@ class JSONContributionRegistry implements IJSONContributionRegistry { private schemasById: { [id: string]: IJSONSchema }; - private readonly _onDidChangeSchema: Emitter = new Emitter(); + private readonly _onDidChangeSchema = new Emitter(); readonly onDidChangeSchema: Event = this._onDidChangeSchema.event; constructor() { diff --git a/src/vs/platform/keybinding/common/abstractKeybindingService.ts b/src/vs/platform/keybinding/common/abstractKeybindingService.ts index 7e6f07aff23..da4d3af573c 100644 --- a/src/vs/platform/keybinding/common/abstractKeybindingService.ts +++ b/src/vs/platform/keybinding/common/abstractKeybindingService.ts @@ -156,10 +156,20 @@ export abstract class AbstractKeybindingService extends Disposable implements IK this._currentChord = null; } + public dispatchByUserSettingsLabel(userSettingsLabel: string, target: IContextKeyServiceTarget): void { + const keybindings = this.resolveUserBinding(userSettingsLabel); + if (keybindings.length >= 1) { + this._doDispatch(keybindings[0], target); + } + } + protected _dispatch(e: IKeyboardEvent, target: IContextKeyServiceTarget): boolean { + return this._doDispatch(this.resolveKeyboardEvent(e), target); + } + + private _doDispatch(keybinding: ResolvedKeybinding, target: IContextKeyServiceTarget): boolean { let shouldPreventDefault = false; - const keybinding = this.resolveKeyboardEvent(e); if (keybinding.isChord()) { console.warn('Unexpected keyboard event mapped to a chord'); return false; diff --git a/src/vs/platform/keybinding/common/keybinding.ts b/src/vs/platform/keybinding/common/keybinding.ts index 762362bd8cd..0cef819a75c 100644 --- a/src/vs/platform/keybinding/common/keybinding.ts +++ b/src/vs/platform/keybinding/common/keybinding.ts @@ -62,6 +62,8 @@ export interface IKeybindingService { */ softDispatch(keyboardEvent: IKeyboardEvent, target: IContextKeyServiceTarget): IResolveResult | null; + dispatchByUserSettingsLabel(userSettingsLabel: string, target: IContextKeyServiceTarget): void; + /** * Look up keybindings for a command. * Use `lookupKeybinding` if you are interested in the preferred keybinding. diff --git a/src/vs/platform/keybinding/test/common/mockKeybindingService.ts b/src/vs/platform/keybinding/test/common/mockKeybindingService.ts index 42213cc40a7..5cf1babe59b 100644 --- a/src/vs/platform/keybinding/test/common/mockKeybindingService.ts +++ b/src/vs/platform/keybinding/test/common/mockKeybindingService.ts @@ -120,6 +120,10 @@ export class MockKeybindingService implements IKeybindingService { return null; } + public dispatchByUserSettingsLabel(userSettingsLabel: string, target: IContextKeyServiceTarget): void { + + } + public dispatchEvent(e: IKeyboardEvent, target: IContextKeyServiceTarget): boolean { return false; } diff --git a/src/vs/platform/list/browser/listService.ts b/src/vs/platform/list/browser/listService.ts index 9932cf6a0a3..b9d7146387e 100644 --- a/src/vs/platform/list/browser/listService.ts +++ b/src/vs/platform/list/browser/listService.ts @@ -505,7 +505,7 @@ export interface IResourceResultsNavigationOptions { export class TreeResourceNavigator extends Disposable { - private readonly _openResource: Emitter = new Emitter(); + private readonly _openResource = new Emitter(); readonly openResource: Event = this._openResource.event; constructor(private tree: WorkbenchTree, private options?: IResourceResultsNavigationOptions) { diff --git a/src/vs/platform/menubar/common/menubar.ts b/src/vs/platform/menubar/common/menubar.ts index c88dab47842..84ceaf14ec2 100644 --- a/src/vs/platform/menubar/common/menubar.ts +++ b/src/vs/platform/menubar/common/menubar.ts @@ -25,6 +25,7 @@ export interface IMenubarMenu { export interface IMenubarKeybinding { label: string; + userSettingsLabel: string; isNative?: boolean; // Assumed true if missing } diff --git a/src/vs/platform/menubar/electron-main/menubar.ts b/src/vs/platform/menubar/electron-main/menubar.ts index 7481c400fc7..43c65fcdb05 100644 --- a/src/vs/platform/menubar/electron-main/menubar.ts +++ b/src/vs/platform/menubar/electron-main/menubar.ts @@ -7,7 +7,7 @@ import * as nls from 'vs/nls'; import { isMacintosh, language } from 'vs/base/common/platform'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { app, shell, Menu, MenuItem, BrowserWindow } from 'electron'; -import { OpenContext, IRunActionInWindowRequest, getTitleBarStyle } from 'vs/platform/windows/common/windows'; +import { OpenContext, IRunActionInWindowRequest, getTitleBarStyle, IRunKeybindingInWindowRequest } from 'vs/platform/windows/common/windows'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IUpdateService, StateType } from 'vs/platform/update/common/update'; @@ -29,6 +29,15 @@ interface IMenuItemClickHandler { inNoWindow: () => void; } +type IMenuItemInvocation = ( + { type: 'commandId'; commandId: string; } + | { type: 'keybinding'; userSettingsLabel: string; } +); + +interface IMenuItemWithKeybinding { + userSettingsLabel?: string; +} + export class Menubar { private static readonly lastKnownMenubarStorageKey = 'lastKnownMenubarData'; @@ -84,10 +93,6 @@ export class Menubar { } private restoreCachedMenubarData() { - // TODO@sbatten remove this at some point down the road - const outdatedKeys = ['lastKnownAdditionalKeybindings', 'lastKnownKeybindings', 'lastKnownMenubar']; - outdatedKeys.forEach(key => this.stateService.removeItem(key)); - const menubarData = this.stateService.getItem(Menubar.lastKnownMenubarStorageKey); if (menubarData) { if (menubarData.menus) { @@ -573,17 +578,49 @@ export class Menubar { } } + private static _menuItemIsTriggeredViaKeybinding(event: Electron.Event, userSettingsLabel: string): boolean { + // The event coming in from Electron will inform us only about the modifier keys pressed. + // The strategy here is to check if the modifier keys match those of the keybinding, + // since it is highly unlikely to use modifier keys when clicking with the mouse + if (!userSettingsLabel) { + // There is no keybinding + return false; + } + + let ctrlRequired = /ctrl/.test(userSettingsLabel); + let shiftRequired = /shift/.test(userSettingsLabel); + let altRequired = /alt/.test(userSettingsLabel); + let metaRequired = /cmd/.test(userSettingsLabel) || /super/.test(userSettingsLabel); + + if (!ctrlRequired && !shiftRequired && !altRequired && !metaRequired) { + // This keybinding does not use any modifier keys, so we cannot use this heuristic + return false; + } + + return ( + ctrlRequired === event.ctrlKey + && shiftRequired === event.shiftKey + && altRequired === event.altKey + && metaRequired === event.metaKey + ); + } + private createMenuItem(label: string, commandId: string | string[], enabled?: boolean, checked?: boolean): Electron.MenuItem; private createMenuItem(label: string, click: () => void, enabled?: boolean, checked?: boolean): Electron.MenuItem; private createMenuItem(arg1: string, arg2: any, arg3?: boolean, arg4?: boolean): Electron.MenuItem { const label = this.mnemonicLabel(arg1); - const click: () => void = (typeof arg2 === 'function') ? arg2 : (menuItem: Electron.MenuItem, win: Electron.BrowserWindow, event: Electron.Event) => { + const click: () => void = (typeof arg2 === 'function') ? arg2 : (menuItem: Electron.MenuItem & IMenuItemWithKeybinding, win: Electron.BrowserWindow, event: Electron.Event) => { + const userSettingsLabel = menuItem.userSettingsLabel; let commandId = arg2; if (Array.isArray(arg2)) { commandId = this.isOptionClick(event) ? arg2[1] : arg2[0]; // support alternative action if we got multiple action Ids and the option key was pressed while invoking } - this.runActionInRenderer(commandId); + if (userSettingsLabel && Menubar._menuItemIsTriggeredViaKeybinding(event, userSettingsLabel)) { + this.runActionInRenderer({ type: 'keybinding', userSettingsLabel }); + } else { + this.runActionInRenderer({ type: 'commandId', commandId }); + } }; const enabled = typeof arg3 === 'boolean' ? arg3 : this.windowsMainService.getWindowCount() > 0; const checked = typeof arg4 === 'boolean' ? arg4 : false; @@ -656,7 +693,7 @@ export class Menubar { }; } - private runActionInRenderer(id: string): void { + private runActionInRenderer(invocation: IMenuItemInvocation): void { // We make sure to not run actions when the window has no focus, this helps // for https://github.com/Microsoft/vscode/issues/25907 and specifically for // https://github.com/Microsoft/vscode/issues/11928 @@ -671,18 +708,21 @@ export class Menubar { } if (activeWindow) { - if (!activeWindow.isReady && isMacintosh && id === 'workbench.action.toggleDevTools' && !this.environmentService.isBuilt) { - // prevent this action from running twice on macOS (https://github.com/Microsoft/vscode/issues/62719) - // we already register a keybinding in bootstrap-window.js for opening developer tools in case something - // goes wrong and that keybinding is only removed when the application has loaded (= window ready). - return; + if (invocation.type === 'commandId') { + if (!activeWindow.isReady && isMacintosh && invocation.commandId === 'workbench.action.toggleDevTools' && !this.environmentService.isBuilt) { + // prevent this action from running twice on macOS (https://github.com/Microsoft/vscode/issues/62719) + // we already register a keybinding in bootstrap-window.js for opening developer tools in case something + // goes wrong and that keybinding is only removed when the application has loaded (= window ready). + return; + } + this.windowsMainService.sendToFocused('vscode:runAction', { id: invocation.commandId, from: 'menu' } as IRunActionInWindowRequest); + } else { + this.windowsMainService.sendToFocused('vscode:runKeybinding', { userSettingsLabel: invocation.userSettingsLabel } as IRunKeybindingInWindowRequest); } - - this.windowsMainService.sendToFocused('vscode:runAction', { id, from: 'menu' } as IRunActionInWindowRequest); } } - private withKeybinding(commandId: string | undefined, options: Electron.MenuItemConstructorOptions): Electron.MenuItemConstructorOptions { + private withKeybinding(commandId: string | undefined, options: Electron.MenuItemConstructorOptions & IMenuItemWithKeybinding): Electron.MenuItemConstructorOptions { const binding = typeof commandId === 'string' ? this.keybindings[commandId] : undefined; // Apply binding if there is one @@ -691,6 +731,7 @@ export class Menubar { // if the binding is native, we can just apply it if (binding.isNative !== false) { options.accelerator = binding.label; + options.userSettingsLabel = binding.userSettingsLabel; } // the keybinding is not native so we cannot show it as part of the accelerator of diff --git a/src/vs/platform/remote/common/remoteAuthorityResolver.ts b/src/vs/platform/remote/common/remoteAuthorityResolver.ts index 51628f97ee5..d028e37cce8 100644 --- a/src/vs/platform/remote/common/remoteAuthorityResolver.ts +++ b/src/vs/platform/remote/common/remoteAuthorityResolver.ts @@ -12,6 +12,8 @@ export interface ResolvedAuthority { readonly host: string; readonly port: number; readonly syncExtensions: boolean; + readonly debugListenPort?: number; + readonly debugConnectPort?: number; } export interface IRemoteAuthorityResolverService { diff --git a/src/vs/platform/remote/node/remoteAgentConnection.ts b/src/vs/platform/remote/node/remoteAgentConnection.ts index ddcb7177b4d..2ab02017c16 100644 --- a/src/vs/platform/remote/node/remoteAgentConnection.ts +++ b/src/vs/platform/remote/node/remoteAgentConnection.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Client, Protocol } from 'vs/base/parts/ipc/node/ipc.net'; +import { Client, BufferedProtocol } from 'vs/base/parts/ipc/node/ipc.net'; import { IExtensionHostDebugParams } from 'vs/platform/environment/common/environment'; export interface RemoteAgentConnectionContext { @@ -11,15 +11,19 @@ export interface RemoteAgentConnectionContext { clientId: string; } -export function connectRemoteAgentManagement(remoteAuthority: string, host: string, port: number, clientId: string): Promise> { +export function connectRemoteAgentManagement(remoteAuthority: string, host: string, port: number, clientId: string, isBuilt: boolean): Promise> { throw new Error(`Not implemented`); } export interface IExtensionHostConnectionResult { - protocol: Protocol; + protocol: BufferedProtocol; debugPort?: number; } -export function connectRemoteAgentExtensionHost(host: string, port: number, debugArguments: IExtensionHostDebugParams): Promise { +export interface IRemoteExtensionHostDebugParams extends IExtensionHostDebugParams { + updatePort?: boolean; +} + +export function connectRemoteAgentExtensionHost(host: string, port: number, debugArguments: IRemoteExtensionHostDebugParams, isBuilt: boolean): Promise { throw new Error(`Not implemented`); } diff --git a/src/vs/platform/state/node/stateService.ts b/src/vs/platform/state/node/stateService.ts index bc513bbdbfe..c4b5bc9bad8 100644 --- a/src/vs/platform/state/node/stateService.ts +++ b/src/vs/platform/state/node/stateService.ts @@ -10,18 +10,52 @@ import { writeFileAndFlushSync } from 'vs/base/node/extfs'; import { isUndefined, isUndefinedOrNull } from 'vs/base/common/types'; import { IStateService } from 'vs/platform/state/common/state'; import { ILogService } from 'vs/platform/log/common/log'; +import { readFile } from 'vs/base/node/pfs'; export class FileStorage { - private _lazyDatabase: object | null = null; + private _database: object | null = null; + private lastFlushedSerializedDatabase: string | null = null; - constructor(private dbPath: string, private onError: (error) => void) { } + constructor(private dbPath: string, private onError: (error: Error) => void) { } private get database(): object { - if (!this._lazyDatabase) { - this._lazyDatabase = this.loadSync(); + if (!this._database) { + this._database = this.loadSync(); + } + + return this._database; + } + + init(): Promise { + return readFile(this.dbPath).then(contents => { + try { + this.lastFlushedSerializedDatabase = contents.toString(); + this._database = JSON.parse(this.lastFlushedSerializedDatabase); + } catch (error) { + this._database = {}; + } + }, error => { + if (error.code !== 'ENOENT') { + this.onError(error); + } + + this._database = {}; + }); + } + + private loadSync(): object { + try { + this.lastFlushedSerializedDatabase = fs.readFileSync(this.dbPath).toString(); + + return JSON.parse(this.lastFlushedSerializedDatabase); + } catch (error) { + if (error.code !== 'ENOENT') { + this.onError(error); + } + + return {}; } - return this._lazyDatabase; } getItem(key: string, defaultValue: T): T; @@ -36,6 +70,7 @@ export class FileStorage { } setItem(key: string, data: any): void { + // Remove an item when it is undefined or null if (isUndefinedOrNull(data)) { return this.removeItem(key); @@ -53,6 +88,7 @@ export class FileStorage { } removeItem(key: string): void { + // Only update if the key is actually present (not undefined) if (!isUndefined(this.database[key])) { this.database[key] = void 0; @@ -60,21 +96,15 @@ export class FileStorage { } } - private loadSync(): object { - try { - return JSON.parse(fs.readFileSync(this.dbPath).toString()); // invalid JSON or permission issue can happen here - } catch (error) { - if (error && error.code !== 'ENOENT') { - this.onError(error); - } - - return {}; - } - } - private saveSync(): void { + const serializedDatabase = JSON.stringify(this.database, null, 4); + if (serializedDatabase === this.lastFlushedSerializedDatabase) { + return; // return early if the database has not changed + } + try { - writeFileAndFlushSync(this.dbPath, JSON.stringify(this.database, null, 4)); // permission issue can happen here + writeFileAndFlushSync(this.dbPath, serializedDatabase); // permission issue can happen here + this.lastFlushedSerializedDatabase = serializedDatabase; } catch (error) { this.onError(error); } @@ -85,10 +115,19 @@ export class StateService implements IStateService { _serviceBrand: any; + private static STATE_FILE = 'storage.json'; + private fileStorage: FileStorage; - constructor(@IEnvironmentService environmentService: IEnvironmentService, @ILogService logService: ILogService) { - this.fileStorage = new FileStorage(path.join(environmentService.userDataPath, 'storage.json'), error => logService.error(error)); + constructor( + @IEnvironmentService environmentService: IEnvironmentService, + @ILogService logService: ILogService + ) { + this.fileStorage = new FileStorage(path.join(environmentService.userDataPath, StateService.STATE_FILE), error => logService.error(error)); + } + + init(): Promise { + return this.fileStorage.init(); } getItem(key: string, defaultValue: T): T; diff --git a/src/vs/platform/statusbar/common/statusbar.ts b/src/vs/platform/statusbar/common/statusbar.ts index a14dc163806..f2d4097c2b2 100644 --- a/src/vs/platform/statusbar/common/statusbar.ts +++ b/src/vs/platform/statusbar/common/statusbar.ts @@ -6,6 +6,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IDisposable } from 'vs/base/common/lifecycle'; import { ThemeColor } from 'vs/platform/theme/common/themeService'; +import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; export const IStatusbarService = createDecorator('statusbarService'); @@ -48,7 +49,7 @@ export interface IStatusbarEntry { /** * An optional extension ID if this entry is provided from an extension. */ - readonly extensionId?: string; + readonly extensionId?: CanonicalExtensionIdentifier; /** * Wether to show a beak above the status bar entry. diff --git a/src/vs/platform/storage/node/storageService.ts b/src/vs/platform/storage/node/storageService.ts index ebd65fc8513..e9fc3f9586f 100644 --- a/src/vs/platform/storage/node/storageService.ts +++ b/src/vs/platform/storage/node/storageService.ts @@ -107,6 +107,11 @@ export class StorageService extends Disposable implements IStorageService { return Promise.reject(error); }); + }).then(void 0, error => { + onUnexpectedError(error); + + // Upon error, fallback to in-memory storage + return this.createWorkspaceStorage(SQLiteStorageDatabase.IN_MEMORY_PATH).init(); }); } diff --git a/src/vs/platform/theme/common/colorRegistry.ts b/src/vs/platform/theme/common/colorRegistry.ts index 78a8bd74f86..86239c3480f 100644 --- a/src/vs/platform/theme/common/colorRegistry.ts +++ b/src/vs/platform/theme/common/colorRegistry.ts @@ -195,7 +195,7 @@ export const selectBorder = registerColor('dropdown.border', { dark: selectBackg export const listFocusBackground = registerColor('list.focusBackground', { dark: '#062F4A', light: '#D6EBFF', hc: null }, nls.localize('listFocusBackground', "List/Tree background color for the focused item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); export const listFocusForeground = registerColor('list.focusForeground', { dark: null, light: null, hc: null }, nls.localize('listFocusForeground', "List/Tree foreground color for the focused item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); -export const listActiveSelectionBackground = registerColor('list.activeSelectionBackground', { dark: '#094771', light: '#2477CE', hc: null }, nls.localize('listActiveSelectionBackground', "List/Tree background color for the selected item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); +export const listActiveSelectionBackground = registerColor('list.activeSelectionBackground', { dark: '#094771', light: '#0074E8', hc: null }, nls.localize('listActiveSelectionBackground', "List/Tree background color for the selected item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); export const listActiveSelectionForeground = registerColor('list.activeSelectionForeground', { dark: Color.white, light: Color.white, hc: null }, nls.localize('listActiveSelectionForeground', "List/Tree foreground color for the selected item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); export const listInactiveSelectionBackground = registerColor('list.inactiveSelectionBackground', { dark: '#37373D', light: '#E4E6F1', hc: null }, nls.localize('listInactiveSelectionBackground', "List/Tree background color for the selected item when the list/tree is inactive. An active list/tree has keyboard focus, an inactive does not.")); export const listInactiveSelectionForeground = registerColor('list.inactiveSelectionForeground', { dark: null, light: null, hc: null }, nls.localize('listInactiveSelectionForeground', "List/Tree foreground color for the selected item when the list/tree is inactive. An active list/tree has keyboard focus, an inactive does not.")); diff --git a/src/vs/platform/update/electron-main/updateService.snap.ts b/src/vs/platform/update/electron-main/updateService.snap.ts index ed0cb25b057..c11676fed4b 100644 --- a/src/vs/platform/update/electron-main/updateService.snap.ts +++ b/src/vs/platform/update/electron-main/updateService.snap.ts @@ -14,6 +14,7 @@ import * as path from 'path'; import { realpath, watch } from 'fs'; import { spawn } from 'child_process'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { stat } from 'vs/base/node/pfs'; abstract class AbstractUpdateService2 implements IUpdateService { @@ -136,7 +137,11 @@ export class SnapUpdateService extends AbstractUpdateService2 { _serviceBrand: any; + private snapUpdatePath: string; + constructor( + private snap: string, + private snapRevision: string, @ILifecycleService lifecycleService: ILifecycleService, @IEnvironmentService environmentService: IEnvironmentService, @ILogService logService: ILogService, @@ -144,10 +149,9 @@ export class SnapUpdateService extends AbstractUpdateService2 { ) { super(lifecycleService, environmentService, logService); - if (typeof process.env.SNAP === 'undefined') { - throw new Error(`'SNAP' environment variable not set`); - } - const watcher = watch(path.dirname(process.env.SNAP)); + this.snapUpdatePath = path.join(this.snap, `usr/share/${product.applicationName}/snapUpdate.sh`); + + const watcher = watch(path.dirname(this.snap)); const onChange = Event.fromNodeEventEmitter(watcher, 'change', (_, fileName: string) => fileName); const onCurrentChange = Event.filter(onChange, n => n === 'current'); const onDebouncedCurrentChange = Event.debounce(onCurrentChange, (_, e) => e, 2000); @@ -164,7 +168,7 @@ export class SnapUpdateService extends AbstractUpdateService2 { this.isUpdateAvailable().then(result => { if (result) { - this.setState(State.Ready({ version: 'something', productVersion: 'someting' })); + this.setState(State.Ready({ version: 'something', productVersion: 'something' })); } else { /* __GDPR__ "update:notAvailable" : { @@ -191,26 +195,23 @@ export class SnapUpdateService extends AbstractUpdateService2 { protected doQuitAndInstall(): void { this.logService.trace('update#quitAndInstall(): running raw#quitAndInstall()'); - if (typeof process.env.SNAP === 'undefined') { - return; - } - // Allow 3 seconds for VS Code to close - spawn('bash', ['-c', path.join(process.env.SNAP, `usr/share/${product.applicationName}/snapUpdate.sh`)], { + spawn('bash', ['-c', this.snapUpdatePath], { detached: true, stdio: ['ignore', 'ignore', 'ignore'] }); } - private isUpdateAvailable(): Promise { - return new Promise((c, e) => { - realpath(`/snap/${product.applicationName}/current`, (err, resolvedCurrentSnapPath) => { - if (err) { return e(err); } + private async isUpdateAvailable(): Promise { + try { + await stat(this.snapUpdatePath); + } catch (err) { + return false; + } - const currentRevision = path.basename(resolvedCurrentSnapPath); - c(process.env.SNAP_REVISION !== currentRevision); - }); - }); + const resolvedCurrentSnapPath = await new Promise((c, e) => realpath(`${path.dirname(this.snap)}/current`, (err, r) => err ? e(err) : c(r))); + const currentRevision = path.basename(resolvedCurrentSnapPath); + return this.snapRevision !== currentRevision; } isLatestVersion(): Promise { diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index e6d5f0048b5..b68575d2b2c 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -409,6 +409,10 @@ export interface IRunActionInWindowRequest { args?: any[]; } +export interface IRunKeybindingInWindowRequest { + userSettingsLabel: string; +} + export class ActiveWindowManager implements IDisposable { private disposables: IDisposable[] = []; diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 430c42aca4d..945417405a4 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -1854,6 +1854,11 @@ declare module 'vscode' { * * `{}` to group conditions (e.g. `**​/*.{ts,js}` matches all TypeScript and JavaScript files) * * `[]` to declare a range of characters to match in a path segment (e.g., `example.[0-9]` to match on `example.0`, `example.1`, …) * * `[!...]` to negate a range of characters to match in a path segment (e.g., `example.[!0-9]` to match on `example.a`, `example.b`, but not `example.0`) + * + * Note: a backslash (`\`) is not valid within a glob pattern. If you have an existing file + * path to match against, consider to use the [relative pattern](#RelativePattern) support + * that takes care of converting any backslash into slash. Otherwise, make sure to convert + * any backslash to slash when creating the glob pattern. */ export type GlobPattern = string | RelativePattern; diff --git a/src/vs/workbench/api/browser/viewsContainersExtensionPoint.ts b/src/vs/workbench/api/browser/viewsContainersExtensionPoint.ts index 299da54da22..7de2b9f8ff9 100644 --- a/src/vs/workbench/api/browser/viewsContainersExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsContainersExtensionPoint.ts @@ -30,6 +30,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; import { URI } from 'vs/base/common/uri'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; export interface IUserFriendlyViewsContainerDescriptor { id: string; @@ -147,12 +148,12 @@ class ViewsContainersExtensionHandler implements IWorkbenchContribution { containers.forEach(descriptor => { const cssClass = `extensionViewlet-${descriptor.id}`; const icon = resources.joinPath(extension.extensionLocation, descriptor.icon); - this.registerCustomViewlet({ id: `workbench.view.extension.${descriptor.id}`, title: descriptor.title, icon }, order++, cssClass, extension.id); + this.registerCustomViewlet({ id: `workbench.view.extension.${descriptor.id}`, title: descriptor.title, icon }, order++, cssClass, extension.identifier); }); return order; } - private registerCustomViewlet(descriptor: IUserFriendlyViewsContainerDescriptor2, order: number, cssClass: string, extensionId: string): void { + private registerCustomViewlet(descriptor: IUserFriendlyViewsContainerDescriptor2, order: number, cssClass: string, extensionId: CanonicalExtensionIdentifier): void { const viewContainersRegistry = Registry.as(ViewContainerExtensions.ViewContainersRegistry); const viewletRegistry = Registry.as(ViewletExtensions.Viewlets); const id = descriptor.id; diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts index a8738c55bc6..a45ff4401bf 100644 --- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts @@ -19,6 +19,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { VIEWLET_ID as EXPLORER } from 'vs/workbench/parts/files/common/files'; import { VIEWLET_ID as SCM } from 'vs/workbench/parts/scm/common/scm'; import { VIEWLET_ID as DEBUG } from 'vs/workbench/parts/debug/common/debug'; +import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; interface IUserFriendlyViewDescriptor { id: string; @@ -136,7 +137,7 @@ class ViewsContainersExtensionHandler implements IWorkbenchContribution { canToggleVisibility: true, collapsed: this.showCollapsed(container), treeView: this.instantiationService.createInstance(CustomTreeView, item.id, container), - order: extension.description.id === container.extensionId ? index + 1 : void 0 + order: CanonicalExtensionIdentifier.equals(extension.description.identifier, container.extensionId) ? index + 1 : void 0 }; viewIds.push(viewDescriptor.id); diff --git a/src/vs/workbench/api/electron-browser/mainThreadComments.ts b/src/vs/workbench/api/electron-browser/mainThreadComments.ts index 7a6cb1ecf4a..c0c0d3d4ee8 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadComments.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadComments.ts @@ -19,6 +19,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { generateUuid } from 'vs/base/common/uuid'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ICommentsConfiguration } from 'vs/workbench/parts/comments/electron-browser/comments.contribution'; +import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; export class MainThreadDocumentCommentProvider implements modes.DocumentCommentProvider { private _proxy: ExtHostCommentsShape; @@ -125,7 +126,7 @@ export class MainThreadComments extends Disposable implements MainThreadComments } } - $registerWorkspaceCommentProvider(handle: number, extensionId: string): void { + $registerWorkspaceCommentProvider(handle: number, extensionId: CanonicalExtensionIdentifier): void { this._workspaceProviders.set(handle, undefined); const providerId = generateUuid(); @@ -165,7 +166,7 @@ export class MainThreadComments extends Disposable implements MainThreadComments } */ this._telemetryService.publicLog('comments:registerWorkspaceCommentProvider', { - extensionId: extensionId + extensionId: extensionId.value }); } diff --git a/src/vs/workbench/api/electron-browser/mainThreadExtensionService.ts b/src/vs/workbench/api/electron-browser/mainThreadExtensionService.ts index 3384488f2b4..04602c0ea61 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadExtensionService.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadExtensionService.ts @@ -9,6 +9,7 @@ import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostC import { IExtHostContext, MainContext, MainThreadExtensionServiceShape } from 'vs/workbench/api/node/extHost.protocol'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { ExtensionService } from 'vs/workbench/services/extensions/electron-browser/extensionService'; +import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; @extHostNamedCustomer(MainContext.MainThreadExtensionService) export class MainThreadExtensionService implements MainThreadExtensionServiceShape { @@ -30,13 +31,13 @@ export class MainThreadExtensionService implements MainThreadExtensionServiceSha $localShowMessage(severity: Severity, msg: string): void { this._extensionService._logOrShowMessage(severity, msg); } - $onWillActivateExtension(extensionId: string): void { + $onWillActivateExtension(extensionId: CanonicalExtensionIdentifier): void { this._extensionService._onWillActivateExtension(extensionId); } - $onDidActivateExtension(extensionId: string, startup: boolean, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationEvent: string): void { + $onDidActivateExtension(extensionId: CanonicalExtensionIdentifier, startup: boolean, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationEvent: string): void { this._extensionService._onDidActivateExtension(extensionId, startup, codeLoadingTime, activateCallTime, activateResolvedTime, activationEvent); } - $onExtensionRuntimeError(extensionId: string, data: SerializedError): void { + $onExtensionRuntimeError(extensionId: CanonicalExtensionIdentifier, data: SerializedError): void { const error = new Error(); error.name = data.name; error.message = data.message; @@ -45,9 +46,9 @@ export class MainThreadExtensionService implements MainThreadExtensionServiceSha console.error(`[${extensionId}]${error.message}`); console.error(error.stack); } - $onExtensionActivationFailed(extensionId: string): void { + $onExtensionActivationFailed(extensionId: CanonicalExtensionIdentifier): void { } - $addMessage(extensionId: string, severity: Severity, message: string): void { + $addMessage(extensionId: CanonicalExtensionIdentifier, severity: Severity, message: string): void { this._extensionService._addMessage(extensionId, severity, message); } } diff --git a/src/vs/workbench/api/electron-browser/mainThreadMessageService.ts b/src/vs/workbench/api/electron-browser/mainThreadMessageService.ts index b0ef3ef24c6..90540a4f020 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadMessageService.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadMessageService.ts @@ -14,6 +14,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { Event } from 'vs/base/common/event'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { dispose } from 'vs/base/common/lifecycle'; +import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; @extHostNamedCustomer(MainContext.MainThreadMessageService) export class MainThreadMessageService implements MainThreadMessageServiceShape { @@ -55,9 +56,9 @@ export class MainThreadMessageService implements MainThreadMessageServiceShape { } class ManageExtensionAction extends Action { - constructor(id: string, label: string, commandService: ICommandService) { - super(id, label, undefined, true, () => { - return commandService.executeCommand('_extensions.manage', id); + constructor(id: CanonicalExtensionIdentifier, label: string, commandService: ICommandService) { + super(id.value, label, undefined, true, () => { + return commandService.executeCommand('_extensions.manage', id.value); }); } } @@ -77,7 +78,7 @@ export class MainThreadMessageService implements MainThreadMessageServiceShape { const secondaryActions: IAction[] = []; if (extension && !extension.isUnderDevelopment) { - secondaryActions.push(new ManageExtensionAction(extension.id, nls.localize('manageExtension', "Manage Extension"), this._commandService)); + secondaryActions.push(new ManageExtensionAction(extension.identifier, nls.localize('manageExtension', "Manage Extension"), this._commandService)); } const messageHandle = this._notificationService.notify({ diff --git a/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts b/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts index dc19c1ada43..4439085c120 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts @@ -243,7 +243,7 @@ class FormatOnSaveParticipant implements ISaveParticipantParticipant { }, timeout); request.then(edits => this._editorWorkerService.computeMoreMinimalEdits(model.uri, edits)).then(resolve, err => { - if (!(err instanceof Error) || err.name !== NoProviderError.Name) { + if (!NoProviderError.is(err)) { reject(err); } else { resolve(); diff --git a/src/vs/workbench/api/electron-browser/mainThreadStatusBar.ts b/src/vs/workbench/api/electron-browser/mainThreadStatusBar.ts index 1dbe33201ce..3a12802a883 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadStatusBar.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadStatusBar.ts @@ -8,6 +8,7 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import { MainThreadStatusBarShape, MainContext, IExtHostContext } from '../node/extHost.protocol'; import { ThemeColor } from 'vs/platform/theme/common/themeService'; import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; +import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; @extHostNamedCustomer(MainContext.MainThreadStatusBar) export class MainThreadStatusBar implements MainThreadStatusBarShape { @@ -27,7 +28,7 @@ export class MainThreadStatusBar implements MainThreadStatusBarShape { } } - $setEntry(id: number, extensionId: string, text: string, tooltip: string, command: string, color: string | ThemeColor, alignment: MainThreadStatusBarAlignment, priority: number): void { + $setEntry(id: number, extensionId: CanonicalExtensionIdentifier, text: string, tooltip: string, command: string, color: string | ThemeColor, alignment: MainThreadStatusBarAlignment, priority: number): void { // Dispose any old this.$dispose(id); diff --git a/src/vs/workbench/api/electron-browser/mainThreadUrls.ts b/src/vs/workbench/api/electron-browser/mainThreadUrls.ts index 5d550492b29..268228eeed8 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadUrls.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadUrls.ts @@ -9,17 +9,18 @@ import { IURLService, IURLHandler } from 'vs/platform/url/common/url'; import { URI } from 'vs/base/common/uri'; import { IDisposable } from 'vs/base/common/lifecycle'; import { IExtensionUrlHandler } from 'vs/workbench/services/extensions/electron-browser/inactiveExtensionUrlHandler'; +import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; class ExtensionUrlHandler implements IURLHandler { constructor( private readonly proxy: ExtHostUrlsShape, private readonly handle: number, - readonly extensionId: string + readonly extensionId: CanonicalExtensionIdentifier ) { } handleURL(uri: URI): Promise { - if (uri.authority !== this.extensionId) { + if (!CanonicalExtensionIdentifier.equals(this.extensionId, uri.authority)) { return Promise.resolve(false); } @@ -31,7 +32,7 @@ class ExtensionUrlHandler implements IURLHandler { export class MainThreadUrls implements MainThreadUrlsShape { private readonly proxy: ExtHostUrlsShape; - private handlers = new Map(); + private handlers = new Map(); constructor( context: IExtHostContext, @@ -41,7 +42,7 @@ export class MainThreadUrls implements MainThreadUrlsShape { this.proxy = context.getProxy(ExtHostContext.ExtHostUrls); } - $registerUriHandler(handle: number, extensionId: string): Promise { + $registerUriHandler(handle: number, extensionId: CanonicalExtensionIdentifier): Promise { const handler = new ExtensionUrlHandler(this.proxy, handle, extensionId); const disposable = this.urlService.registerHandler(handler); diff --git a/src/vs/workbench/api/electron-browser/mainThreadWebview.ts b/src/vs/workbench/api/electron-browser/mainThreadWebview.ts index 054e832c0d1..8876f29adc9 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadWebview.ts @@ -20,6 +20,7 @@ import * as vscode from 'vscode'; import { extHostNamedCustomer } from './extHostCustomers'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { onUnexpectedError } from 'vs/base/common/errors'; +import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; @extHostNamedCustomer(MainContext.MainThreadWebviews) export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviver { @@ -69,7 +70,7 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv title: string, showOptions: { viewColumn: EditorViewColumn | null, preserveFocus: boolean }, options: WebviewInputOptions, - extensionId: string, + extensionId: CanonicalExtensionIdentifier, extensionLocation: UriComponents ): void { const mainThreadShowOptions: ICreateWebViewShowOptions = Object.create(null); @@ -92,7 +93,7 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv "extensionId" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } } */ - this._telemetryService.publicLog('webviews:createWebviewPanel', { extensionId: extensionId }); + this._telemetryService.publicLog('webviews:createWebviewPanel', { extensionId: extensionId.value }); } public $disposeWebview(handle: WebviewPanelHandle): void { diff --git a/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts b/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts index e22d865739c..3170bcc79b1 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts @@ -9,7 +9,6 @@ import { URI, UriComponents } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ILabelService } from 'vs/platform/label/common/label'; import { IFolderQuery, IPatternInfo, ISearchConfiguration, ISearchProgressItem, ISearchService, QueryType, IFileQuery } from 'vs/platform/search/common/search'; @@ -24,6 +23,7 @@ import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common import { ExtHostContext, ExtHostWorkspaceShape, IExtHostContext, MainContext, MainThreadWorkspaceShape } from '../node/extHost.protocol'; import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation'; import { TextSearchComplete } from 'vscode'; +import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; @extHostNamedCustomer(MainContext.MainThreadWorkspace) export class MainThreadWorkspace implements MainThreadWorkspaceShape { @@ -246,7 +246,7 @@ CommandsRegistry.registerCommand('_workbench.enterWorkspace', async function (ac if (disableExtensions && disableExtensions.length) { const runningExtensions = await extensionService.getExtensions(); // If requested extension to disable is running, then reload window with given workspace - if (disableExtensions && runningExtensions.some(runningExtension => disableExtensions.some(id => areSameExtensions({ id }, { id: runningExtension.id })))) { + if (disableExtensions && runningExtensions.some(runningExtension => disableExtensions.some(id => CanonicalExtensionIdentifier.equals(runningExtension.identifier, id)))) { return windowService.openWindow([URI.file(workspace.fsPath)], { args: { _: [], 'disable-extension': disableExtensions } }); } } diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index 286b289ab1a..7ada96d59af 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -63,6 +63,7 @@ import { IExtensionDescription, throwProposedApiError, checkProposedApiEnabled, import { ProxyIdentifier } from 'vs/workbench/services/extensions/node/proxyIdentifier'; import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry'; import * as vscode from 'vscode'; +import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; export interface IExtensionApiFactory { (extension: IExtensionDescription, registry: ExtensionDescriptionRegistry): typeof vscode; @@ -152,7 +153,7 @@ export function createApiFactory( let done = (!extension.isUnderDevelopment); function informOnce(selector: vscode.DocumentSelector) { if (!done) { - console.info(`Extension '${extension.id}' uses a document selector without scheme. Learn more about this: https://go.microsoft.com/fwlink/?linkid=872305`); + console.info(`Extension '${extension.identifier.value}' uses a document selector without scheme. Learn more about this: https://go.microsoft.com/fwlink/?linkid=872305`); done = true; } } @@ -180,7 +181,7 @@ export function createApiFactory( const informOnce = () => { if (!done) { done = true; - console.warn(`Extension '${extension.id}' uses the 'vscode.previewHtml' command which is deprecated and will be removed. Please update your extension to use the Webview API: https://go.microsoft.com/fwlink/?linkid=2039309`); + console.warn(`Extension '${extension.identifier.value}' uses the 'vscode.previewHtml' command which is deprecated and will be removed. Please update your extension to use the Webview API: https://go.microsoft.com/fwlink/?linkid=2039309`); } }; return (commandId: string) => { @@ -310,7 +311,7 @@ export function createApiFactory( return extHostLanguageFeatures.registerTypeDefinitionProvider(extension, checkSelector(selector), provider); }, registerHoverProvider(selector: vscode.DocumentSelector, provider: vscode.HoverProvider): vscode.Disposable { - return extHostLanguageFeatures.registerHoverProvider(extension, checkSelector(selector), provider, extension.id); + return extHostLanguageFeatures.registerHoverProvider(extension, checkSelector(selector), provider, extension.identifier); }, registerDocumentHighlightProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentHighlightProvider): vscode.Disposable { return extHostLanguageFeatures.registerDocumentHighlightProvider(extension, checkSelector(selector), provider); @@ -448,7 +449,7 @@ export function createApiFactory( return extHostDialogs.showSaveDialog(options); }, createStatusBarItem(position?: vscode.StatusBarAlignment, priority?: number): vscode.StatusBarItem { - return extHostStatusBar.createStatusBarEntry(extension.id, position, priority); + return extHostStatusBar.createStatusBarEntry(extension.identifier, position, priority); }, setStatusBarMessage(text: string, timeoutOrThenable?: number | Thenable): vscode.Disposable { return extHostStatusBar.setStatusBarMessage(text, timeoutOrThenable); @@ -485,16 +486,16 @@ export function createApiFactory( return extHostWebviews.registerWebviewPanelSerializer(viewType, serializer); }, registerDecorationProvider: proposedApiFunction(extension, (provider: vscode.DecorationProvider) => { - return extHostDecorations.registerDecorationProvider(provider, extension.id); + return extHostDecorations.registerDecorationProvider(provider, extension.identifier); }), registerUriHandler(handler: vscode.UriHandler) { - return extHostUrls.registerUriHandler(extension.id, handler); + return extHostUrls.registerUriHandler(extension.identifier, handler); }, createQuickPick(): vscode.QuickPick { - return extHostQuickOpen.createQuickPick(extension.id, extension.enableProposedApi); + return extHostQuickOpen.createQuickPick(extension.identifier, extension.enableProposedApi); }, createInputBox(): vscode.InputBox { - return extHostQuickOpen.createInputBox(extension.id); + return extHostQuickOpen.createInputBox(extension.identifier); }, }; @@ -528,7 +529,7 @@ export function createApiFactory( return extHostWorkspace.getRelativePath(pathOrUri, includeWorkspace); }, findFiles: (include, exclude, maxResults?, token?) => { - return extHostWorkspace.findFiles(typeConverters.GlobPattern.from(include), typeConverters.GlobPattern.from(exclude), maxResults, extension.id, token); + return extHostWorkspace.findFiles(typeConverters.GlobPattern.from(include), typeConverters.GlobPattern.from(exclude), maxResults, extension.identifier, token); }, findTextInFiles: (query: vscode.TextSearchQuery, optionsOrCallback, callbackOrToken?, token?: vscode.CancellationToken) => { let options: vscode.FindTextInFilesOptions; @@ -543,7 +544,7 @@ export function createApiFactory( token = callbackOrToken; } - return extHostWorkspace.findTextInFiles(query, options || {}, callback, extension.id, token); + return extHostWorkspace.findTextInFiles(query, options || {}, callback, extension.identifier, token); }, saveAll: (includeUntitled?) => { return extHostWorkspace.saveAll(includeUntitled); @@ -601,7 +602,7 @@ export function createApiFactory( }, getConfiguration(section?: string, resource?: vscode.Uri): vscode.WorkspaceConfiguration { resource = arguments.length === 1 ? void 0 : resource; - return extHostConfiguration.getConfiguration(section, resource, extension.id); + return extHostConfiguration.getConfiguration(section, resource, extension.identifier); }, registerTextDocumentContentProvider(scheme: string, provider: vscode.TextDocumentContentProvider) { return extHostDocumentContentProviders.registerTextDocumentContentProvider(scheme, provider); @@ -629,7 +630,7 @@ export function createApiFactory( return exthostCommentProviders.registerDocumentCommentProvider(provider); }), registerWorkspaceCommentProvider: proposedApiFunction(extension, (provider: vscode.WorkspaceCommentProvider) => { - return exthostCommentProviders.registerWorkspaceCommentProvider(extension.id, provider); + return exthostCommentProviders.registerWorkspaceCommentProvider(extension.identifier, provider); }), onDidRenameFile: proposedApiFunction(extension, (listener, thisArg?, disposables?) => { return extHostFileSystemEvent.onDidRenameFile(listener, thisArg, disposables); @@ -837,6 +838,7 @@ export function originalFSPath(uri: URI): string { class Extension implements vscode.Extension { private _extensionService: ExtHostExtensionService; + private _identifier: CanonicalExtensionIdentifier; public id: string; public extensionPath: string; @@ -844,21 +846,22 @@ class Extension implements vscode.Extension { constructor(extensionService: ExtHostExtensionService, description: IExtensionDescription) { this._extensionService = extensionService; - this.id = description.id; + this._identifier = description.identifier; + this.id = description.identifier.value; this.extensionPath = paths.normalize(originalFSPath(description.extensionLocation), true); this.packageJSON = description; } get isActive(): boolean { - return this._extensionService.isActivated(this.id); + return this._extensionService.isActivated(this._identifier); } get exports(): T { - return this._extensionService.getExtensionExports(this.id); + return this._extensionService.getExtensionExports(this._identifier); } activate(): Thenable { - return this._extensionService.activateByIdWithErrors(this.id, new ExtensionActivatedByAPI(false)).then(() => this.exports); + return this._extensionService.activateByIdWithErrors(this._identifier, new ExtensionActivatedByAPI(false)).then(() => this.exports); } } @@ -882,10 +885,10 @@ function defineAPI(factory: IExtensionApiFactory, extensionPaths: TernarySearchT // get extension id from filename and api for extension const ext = extensionPaths.findSubstr(URI.file(parent.filename).fsPath); if (ext) { - let apiImpl = extApiImpl.get(ext.id); + let apiImpl = extApiImpl.get(CanonicalExtensionIdentifier.toKey(ext.identifier)); if (!apiImpl) { apiImpl = factory(ext, extensionRegistry); - extApiImpl.set(ext.id, apiImpl); + extApiImpl.set(CanonicalExtensionIdentifier.toKey(ext.identifier), apiImpl); } return apiImpl; } @@ -893,7 +896,7 @@ function defineAPI(factory: IExtensionApiFactory, extensionPaths: TernarySearchT // fall back to a default implementation if (!defaultApiImpl) { let extensionPathsPretty = ''; - extensionPaths.forEach((value, index) => extensionPathsPretty += `\t${index} -> ${value.id}\n`); + extensionPaths.forEach((value, index) => extensionPathsPretty += `\t${index} -> ${value.identifier.value}\n`); console.warn(`Could not identify extension for 'vscode' require call from ${parent.filename}. These are the extension path mappings: \n${extensionPathsPretty}`); defaultApiImpl = factory(nullExtensionDescription, extensionRegistry); } diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 621dba4c7c3..2130c7de93b 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -43,6 +43,7 @@ import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles'; import * as vscode from 'vscode'; import { IMarkdownString } from 'vs/base/common/htmlContent'; import { ResolvedAuthority } from 'vs/platform/remote/common/remoteAuthorityResolver'; +import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; export interface IEnvironment { isExtensionDevelopmentDebug: boolean; @@ -113,7 +114,7 @@ export interface CommentProviderFeatures { export interface MainThreadCommentsShape extends IDisposable { $registerDocumentCommentProvider(handle: number, features: CommentProviderFeatures): void; $unregisterDocumentCommentProvider(handle: number): void; - $registerWorkspaceCommentProvider(handle: number, extensionId: string): void; + $registerWorkspaceCommentProvider(handle: number, extensionId: CanonicalExtensionIdentifier): void; $unregisterWorkspaceCommentProvider(handle: number): void; $onDidCommentThreadsChange(handle: number, event: modes.CommentThreadChangedEvent): void; } @@ -439,7 +440,7 @@ export interface MainThreadQuickOpenShape extends IDisposable { } export interface MainThreadStatusBarShape extends IDisposable { - $setEntry(id: number, extensionId: string, text: string, tooltip: string, command: string, color: string | ThemeColor, alignment: MainThreadStatusBarAlignment, priority: number): void; + $setEntry(id: number, extensionId: CanonicalExtensionIdentifier, text: string, tooltip: string, command: string, color: string | ThemeColor, alignment: MainThreadStatusBarAlignment, priority: number): void; $dispose(id: number): void; } @@ -460,7 +461,7 @@ export interface WebviewPanelShowOptions { } export interface MainThreadWebviewsShape extends IDisposable { - $createWebviewPanel(handle: WebviewPanelHandle, viewType: string, title: string, showOptions: WebviewPanelShowOptions, options: vscode.WebviewPanelOptions & vscode.WebviewOptions, extensionId: string, extensionLocation: UriComponents): void; + $createWebviewPanel(handle: WebviewPanelHandle, viewType: string, title: string, showOptions: WebviewPanelShowOptions, options: vscode.WebviewPanelOptions & vscode.WebviewOptions, extensionId: CanonicalExtensionIdentifier, extensionLocation: UriComponents): void; $disposeWebview(handle: WebviewPanelHandle): void; $reveal(handle: WebviewPanelHandle, showOptions: WebviewPanelShowOptions): void; $setTitle(handle: WebviewPanelHandle, value: string): void; @@ -487,7 +488,7 @@ export interface ExtHostWebviewsShape { } export interface MainThreadUrlsShape extends IDisposable { - $registerUriHandler(handle: number, extensionId: string): Promise; + $registerUriHandler(handle: number, extensionId: CanonicalExtensionIdentifier): Promise; $unregisterUriHandler(handle: number): Promise; } @@ -537,11 +538,11 @@ export interface MainThreadTaskShape extends IDisposable { export interface MainThreadExtensionServiceShape extends IDisposable { $localShowMessage(severity: Severity, msg: string): void; - $onWillActivateExtension(extensionId: string): void; - $onDidActivateExtension(extensionId: string, startup: boolean, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationEvent: string): void; - $onExtensionActivationFailed(extensionId: string): void; - $onExtensionRuntimeError(extensionId: string, error: SerializedError): void; - $addMessage(extensionId: string, severity: Severity, message: string): void; + $onWillActivateExtension(extensionId: CanonicalExtensionIdentifier): void; + $onDidActivateExtension(extensionId: CanonicalExtensionIdentifier, startup: boolean, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationEvent: string): void; + $onExtensionActivationFailed(extensionId: CanonicalExtensionIdentifier): void; + $onExtensionRuntimeError(extensionId: CanonicalExtensionIdentifier, error: SerializedError): void; + $addMessage(extensionId: CanonicalExtensionIdentifier, severity: Severity, message: string): void; } export interface SCMProviderFeatures { @@ -736,7 +737,7 @@ export interface ExtHostSearchShape { export interface ExtHostExtensionServiceShape { $resolveAuthority(remoteAuthority: string): Promise; - $startExtensionHost(enabledExtensionIds: string[]): Promise; + $startExtensionHost(enabledExtensionIds: CanonicalExtensionIdentifier[]): Promise; $activateByEvent(activationEvent: string): Promise; } diff --git a/src/vs/workbench/api/node/extHostComments.ts b/src/vs/workbench/api/node/extHostComments.ts index 761547ff64f..1a50918b72a 100644 --- a/src/vs/workbench/api/node/extHostComments.ts +++ b/src/vs/workbench/api/node/extHostComments.ts @@ -13,6 +13,7 @@ import { ExtHostCommentsShape, IMainContext, MainContext, MainThreadCommentsShap import { CommandsConverter } from './extHostCommands'; import { IRange } from 'vs/editor/common/core/range'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; export class ExtHostComments implements ExtHostCommentsShape { private static handlePool = 0; @@ -31,7 +32,7 @@ export class ExtHostComments implements ExtHostCommentsShape { } registerWorkspaceCommentProvider( - extensionId: string, + extensionId: CanonicalExtensionIdentifier, provider: vscode.WorkspaceCommentProvider ): vscode.Disposable { const handle = ExtHostComments.handlePool++; diff --git a/src/vs/workbench/api/node/extHostConfiguration.ts b/src/vs/workbench/api/node/extHostConfiguration.ts index 724bf5863ab..d6c7253da81 100644 --- a/src/vs/workbench/api/node/extHostConfiguration.ts +++ b/src/vs/workbench/api/node/extHostConfiguration.ts @@ -16,6 +16,7 @@ import { WorkspaceConfigurationChangeEvent } from 'vs/workbench/services/configu import { ResourceMap } from 'vs/base/common/map'; import { ConfigurationScope, OVERRIDE_PROPERTY_PATTERN } from 'vs/platform/configuration/common/configurationRegistry'; import { isObject } from 'vs/base/common/types'; +import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; function lookUp(tree: any, key: string) { if (key) { @@ -60,7 +61,7 @@ export class ExtHostConfiguration implements ExtHostConfigurationShape { this._onDidChangeConfiguration.fire(this._toConfigurationChangeEvent(eventData)); } - getConfiguration(section?: string, resource?: URI, extensionId?: string): vscode.WorkspaceConfiguration { + getConfiguration(section?: string, resource?: URI, extensionId?: CanonicalExtensionIdentifier): vscode.WorkspaceConfiguration { const config = this._toReadonlyValue(section ? lookUp(this._configuration.getValue(null, { resource }, this._extHostWorkspace.workspace), section) : this._configuration.getValue(null, { resource }, this._extHostWorkspace.workspace)); @@ -187,9 +188,9 @@ export class ExtHostConfiguration implements ExtHostConfigurationShape { return readonlyProxy(result); } - private _validateConfigurationAccess(key: string, resource: URI, extensionId: string): void { + private _validateConfigurationAccess(key: string, resource: URI, extensionId: CanonicalExtensionIdentifier): void { const scope = OVERRIDE_PROPERTY_PATTERN.test(key) ? ConfigurationScope.RESOURCE : this._configurationScopes[key]; - const extensionIdText = extensionId ? `[${extensionId}] ` : ''; + const extensionIdText = extensionId ? `[${extensionId.value}] ` : ''; if (ConfigurationScope.RESOURCE === scope) { if (resource === void 0) { console.warn(`${extensionIdText}Accessing a resource scoped configuration without providing a resource is not expected. To get the effective value for '${key}', provide the URI of a resource or 'null' for any resource.`); diff --git a/src/vs/workbench/api/node/extHostDebugService.ts b/src/vs/workbench/api/node/extHostDebugService.ts index 885a46ee4af..1be53428560 100644 --- a/src/vs/workbench/api/node/extHostDebugService.ts +++ b/src/vs/workbench/api/node/extHostDebugService.ts @@ -128,8 +128,8 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { const debuggers = ed.contributes['debuggers']; if (debuggers && debuggers.length > 0) { for (const dbg of debuggers) { - // only debugger contributions with a "label" are considered a "defining" debugger contribution - if (dbg.type && dbg.label) { + // only debugger contributions with a label, program, or runtime attribute are considered a "defining" debugger contribution + if (dbg.type && (dbg.label || dbg.program || dbg.runtime)) { debugTypes.push(dbg.type); if (dbg.adapterExecutableCommand) { this._aexCommands.set(dbg.type, dbg.adapterExecutableCommand); diff --git a/src/vs/workbench/api/node/extHostDecorations.ts b/src/vs/workbench/api/node/extHostDecorations.ts index 1afb2f43282..fea3abefb1e 100644 --- a/src/vs/workbench/api/node/extHostDecorations.ts +++ b/src/vs/workbench/api/node/extHostDecorations.ts @@ -8,10 +8,11 @@ import { URI } from 'vs/base/common/uri'; import { MainContext, IMainContext, ExtHostDecorationsShape, MainThreadDecorationsShape, DecorationData, DecorationRequest, DecorationReply } from 'vs/workbench/api/node/extHost.protocol'; import { Disposable } from 'vs/workbench/api/node/extHostTypes'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; interface ProviderData { provider: vscode.DecorationProvider; - extensionId: string; + extensionId: CanonicalExtensionIdentifier; } export class ExtHostDecorations implements ExtHostDecorationsShape { @@ -25,10 +26,10 @@ export class ExtHostDecorations implements ExtHostDecorationsShape { this._proxy = mainContext.getProxy(MainContext.MainThreadDecorations); } - registerDecorationProvider(provider: vscode.DecorationProvider, extensionId: string): vscode.Disposable { + registerDecorationProvider(provider: vscode.DecorationProvider, extensionId: CanonicalExtensionIdentifier): vscode.Disposable { const handle = ExtHostDecorations._handlePool++; this._provider.set(handle, { provider, extensionId }); - this._proxy.$registerDecorationProvider(handle, extensionId); + this._proxy.$registerDecorationProvider(handle, extensionId.value); const listener = provider.onDidChangeDecorations(e => { this._proxy.$onDidChange(handle, !e ? null : Array.isArray(e) ? e : [e]); @@ -52,7 +53,7 @@ export class ExtHostDecorations implements ExtHostDecorationsShape { const { provider, extensionId } = this._provider.get(handle); return Promise.resolve(provider.provideDecoration(URI.revive(uri), token)).then(data => { if (data && data.letter && data.letter.length !== 1) { - console.warn(`INVALID decoration from extension '${extensionId}'. The 'letter' must be set and be one character, not '${data.letter}'.`); + console.warn(`INVALID decoration from extension '${extensionId.value}'. The 'letter' must be set and be one character, not '${data.letter}'.`); } result[id] = data && [data.priority, data.bubble, data.title, data.letter, data.color, data.source]; }, err => { diff --git a/src/vs/workbench/api/node/extHostDocumentSaveParticipant.ts b/src/vs/workbench/api/node/extHostDocumentSaveParticipant.ts index ea3ef4d0518..226248d1699 100644 --- a/src/vs/workbench/api/node/extHostDocumentSaveParticipant.ts +++ b/src/vs/workbench/api/node/extHostDocumentSaveParticipant.ts @@ -83,7 +83,7 @@ export class ExtHostDocumentSaveParticipant implements ExtHostDocumentSavePartic }, err => { - this._logService.error(`onWillSaveTextDocument-listener from extension '${extension.id}' threw ERROR`); + this._logService.error(`onWillSaveTextDocument-listener from extension '${extension.identifier.value}' threw ERROR`); this._logService.error(err); if (!(err instanceof Error) || (err).message !== 'concurrent_edits') { @@ -91,7 +91,7 @@ export class ExtHostDocumentSaveParticipant implements ExtHostDocumentSavePartic this._badListeners.set(listener, !errors ? 1 : errors + 1); if (errors > this._thresholds.errors) { - this._logService.info(`onWillSaveTextDocument-listener from extension '${extension.id}' will now be IGNORED because of timeouts and/or errors`); + this._logService.info(`onWillSaveTextDocument-listener from extension '${extension.identifier.value}' will now be IGNORED because of timeouts and/or errors`); } } return false; @@ -132,7 +132,7 @@ export class ExtHostDocumentSaveParticipant implements ExtHostDocumentSavePartic const handle = setTimeout(() => reject(new Error('timeout')), this._thresholds.timeout); return Promise.all(promises).then(edits => { - this._logService.debug(`onWillSaveTextDocument-listener from extension '${extension.id}' finished after ${(Date.now() - t1)}ms`); + this._logService.debug(`onWillSaveTextDocument-listener from extension '${extension.identifier.value}' finished after ${(Date.now() - t1)}ms`); clearTimeout(handle); resolve(edits); }).catch(err => { diff --git a/src/vs/workbench/api/node/extHostExtensionActivator.ts b/src/vs/workbench/api/node/extHostExtensionActivator.ts index 1bc33c64cbf..c4621889421 100644 --- a/src/vs/workbench/api/node/extHostExtensionActivator.ts +++ b/src/vs/workbench/api/node/extHostExtensionActivator.ts @@ -8,8 +8,8 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import Severity from 'vs/base/common/severity'; import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry'; +import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; -const hasOwnProperty = Object.hasOwnProperty; const NO_OP_VOID_PROMISE = Promise.resolve(void 0); export interface IExtensionMemento { @@ -192,8 +192,8 @@ export class ExtensionsActivator { private readonly _registry: ExtensionDescriptionRegistry; private readonly _host: IExtensionsActivatorHost; - private readonly _activatingExtensions: { [extensionId: string]: Promise; }; - private readonly _activatedExtensions: { [extensionId: string]: ActivatedExtension; }; + private readonly _activatingExtensions: Map>; + private readonly _activatedExtensions: Map; /** * A map of already activated events to speed things up if the same activation event is triggered multiple times. */ @@ -202,20 +202,24 @@ export class ExtensionsActivator { constructor(registry: ExtensionDescriptionRegistry, host: IExtensionsActivatorHost) { this._registry = registry; this._host = host; - this._activatingExtensions = {}; - this._activatedExtensions = {}; + this._activatingExtensions = new Map>(); + this._activatedExtensions = new Map(); this._alreadyActivatedEvents = Object.create(null); } - public isActivated(extensionId: string): boolean { - return hasOwnProperty.call(this._activatedExtensions, extensionId); + public isActivated(extensionId: CanonicalExtensionIdentifier): boolean { + const extensionKey = CanonicalExtensionIdentifier.toKey(extensionId); + + return this._activatedExtensions.has(extensionKey); } - public getActivatedExtension(extensionId: string): ActivatedExtension { - if (!hasOwnProperty.call(this._activatedExtensions, extensionId)) { - throw new Error('Extension `' + extensionId + '` is not known or not activated'); + public getActivatedExtension(extensionId: CanonicalExtensionIdentifier): ActivatedExtension { + const extensionKey = CanonicalExtensionIdentifier.toKey(extensionId); + + if (!this._activatedExtensions.has(extensionKey)) { + throw new Error('Extension `' + extensionId.value + '` is not known or not activated'); } - return this._activatedExtensions[extensionId]; + return this._activatedExtensions.get(extensionKey); } public activateByEvent(activationEvent: string, reason: ExtensionActivationReason): Promise { @@ -228,7 +232,7 @@ export class ExtensionsActivator { }); } - public activateById(extensionId: string, reason: ExtensionActivationReason): Promise { + public activateById(extensionId: CanonicalExtensionIdentifier, reason: ExtensionActivationReason): Promise { let desc = this._registry.getExtensionDescription(extensionId); if (!desc) { throw new Error('Extension `' + extensionId + '` is not known'); @@ -251,31 +255,31 @@ export class ExtensionsActivator { if (!depDesc) { // Error condition 1: unknown dependency - this._host.showMessage(Severity.Error, nls.localize('unknownDep', "Cannot activate extension '{0}' as the depending extension '{1}' is not found. Please install or enable the depending extension and reload the window.", currentExtension.displayName || currentExtension.id, depId)); + this._host.showMessage(Severity.Error, nls.localize('unknownDep', "Cannot activate extension '{0}' as the depending extension '{1}' is not found. Please install or enable the depending extension and reload the window.", currentExtension.displayName || currentExtension.identifier.value, depId)); const error = new Error(`Unknown dependency '${depId}'`); - this._activatedExtensions[currentExtension.id] = new FailedExtension(error); + this._activatedExtensions.set(CanonicalExtensionIdentifier.toKey(currentExtension.identifier), new FailedExtension(error)); return; } - if (hasOwnProperty.call(this._activatedExtensions, depId)) { - let dep = this._activatedExtensions[depId]; + if (this._activatedExtensions.has(CanonicalExtensionIdentifier.toKey(depId))) { + let dep = this._activatedExtensions.get(CanonicalExtensionIdentifier.toKey(depId)); if (dep.activationFailed) { // Error condition 2: a dependency has already failed activation - this._host.showMessage(Severity.Error, nls.localize('failedDep1', "Cannot activate extension '{0}' as the depending extension '{1}' is failed to activate.", currentExtension.displayName || currentExtension.id, depId)); + this._host.showMessage(Severity.Error, nls.localize('failedDep1', "Cannot activate extension '{0}' as the depending extension '{1}' is failed to activate.", currentExtension.displayName || currentExtension.identifier.value, depId)); const error = new Error(`Dependency ${depId} failed to activate`); (error).detail = dep.activationFailedError; - this._activatedExtensions[currentExtension.id] = new FailedExtension(error); + this._activatedExtensions.set(CanonicalExtensionIdentifier.toKey(currentExtension.identifier), new FailedExtension(error)); return; } } else { // must first wait for the dependency to activate currentExtensionGetsGreenLight = false; - greenExtensions[depId] = depDesc; + greenExtensions[CanonicalExtensionIdentifier.toKey(depId)] = depDesc; } } if (currentExtensionGetsGreenLight) { - greenExtensions[currentExtension.id] = currentExtension; + greenExtensions[CanonicalExtensionIdentifier.toKey(currentExtension.identifier)] = currentExtension; } else { redExtensions.push(currentExtension); } @@ -287,7 +291,7 @@ export class ExtensionsActivator { return Promise.resolve(void 0); } - extensionDescriptions = extensionDescriptions.filter((p) => !hasOwnProperty.call(this._activatedExtensions, p.id)); + extensionDescriptions = extensionDescriptions.filter((p) => !this._activatedExtensions.has(CanonicalExtensionIdentifier.toKey(p.identifier))); if (extensionDescriptions.length === 0) { return Promise.resolve(void 0); } @@ -296,9 +300,9 @@ export class ExtensionsActivator { // More than 10 dependencies deep => most likely a dependency loop for (let i = 0, len = extensionDescriptions.length; i < len; i++) { // Error condition 3: dependency loop - this._host.showMessage(Severity.Error, nls.localize('failedDep2', "Extension '{0}' failed to activate. Reason: more than 10 levels of dependencies (most likely a dependency loop).", extensionDescriptions[i].id)); + this._host.showMessage(Severity.Error, nls.localize('failedDep2', "Extension '{0}' failed to activate. Reason: more than 10 levels of dependencies (most likely a dependency loop).", extensionDescriptions[i].identifier.value)); const error = new Error('More than 10 levels of dependencies (most likely a dependency loop)'); - this._activatedExtensions[extensionDescriptions[i].id] = new FailedExtension(error); + this._activatedExtensions.set(CanonicalExtensionIdentifier.toKey(extensionDescriptions[i].identifier), new FailedExtension(error)); } return Promise.resolve(void 0); } @@ -312,8 +316,9 @@ export class ExtensionsActivator { // Make sure no red is also green for (let i = 0, len = red.length; i < len; i++) { - if (greenMap[red[i].id]) { - delete greenMap[red[i].id]; + const redExtensionKey = CanonicalExtensionIdentifier.toKey(red[i].identifier); + if (greenMap[redExtensionKey]) { + delete greenMap[redExtensionKey]; } } @@ -333,25 +338,27 @@ export class ExtensionsActivator { } private _activateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise { - if (hasOwnProperty.call(this._activatedExtensions, extensionDescription.id)) { + const extensionKey = CanonicalExtensionIdentifier.toKey(extensionDescription.identifier); + + if (this._activatedExtensions.has(extensionKey)) { return Promise.resolve(void 0); } - if (hasOwnProperty.call(this._activatingExtensions, extensionDescription.id)) { - return this._activatingExtensions[extensionDescription.id]; + if (this._activatingExtensions.has(extensionKey)) { + return this._activatingExtensions.get(extensionKey); } - this._activatingExtensions[extensionDescription.id] = this._host.actualActivateExtension(extensionDescription, reason).then(void 0, (err) => { - this._host.showMessage(Severity.Error, nls.localize('activationError', "Activating extension '{0}' failed: {1}.", extensionDescription.id, err.message)); - console.error('Activating extension `' + extensionDescription.id + '` failed: ', err.message); + this._activatingExtensions.set(extensionKey, this._host.actualActivateExtension(extensionDescription, reason).then(void 0, (err) => { + this._host.showMessage(Severity.Error, nls.localize('activationError', "Activating extension '{0}' failed: {1}.", extensionDescription.identifier.value, err.message)); + console.error('Activating extension `' + extensionDescription.identifier.value + '` failed: ', err.message); console.log('Here is the error stack: ', err.stack); // Treat the extension as being empty return new FailedExtension(err); }).then((x: ActivatedExtension) => { - this._activatedExtensions[extensionDescription.id] = x; - delete this._activatingExtensions[extensionDescription.id]; - }); + this._activatedExtensions.set(extensionKey, x); + this._activatingExtensions.delete(extensionKey); + })); - return this._activatingExtensions[extensionDescription.id]; + return this._activatingExtensions.get(extensionKey); } } diff --git a/src/vs/workbench/api/node/extHostExtensionService.ts b/src/vs/workbench/api/node/extHostExtensionService.ts index 5157534de1d..8fb2668fae7 100644 --- a/src/vs/workbench/api/node/extHostExtensionService.ts +++ b/src/vs/workbench/api/node/extHostExtensionService.ts @@ -25,6 +25,7 @@ import { connectProxyResolver } from 'vs/workbench/node/proxyResolver'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import * as errors from 'vs/base/common/errors'; import { ResolvedAuthority } from 'vs/platform/remote/common/remoteAuthorityResolver'; +import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; class ExtensionMemento implements IExtensionMemento { @@ -97,13 +98,13 @@ class ExtensionStoragePath { workspaceValue(extension: IExtensionDescription): string { if (this._value) { - return path.join(this._value, extension.id); + return path.join(this._value, extension.identifier.value); } return undefined; } globalValue(extension: IExtensionDescription): string { - return path.join(this._environment.globalStorageHome.fsPath, extension.id); + return path.join(this._environment.globalStorageHome.fsPath, extension.identifier.value); } private async _getOrCreateWorkspaceStoragePath(): Promise { @@ -234,7 +235,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { let allPromises: Promise[] = []; try { const allExtensions = this._registry.getAllExtensionDescriptions(); - const allExtensionsIds = allExtensions.map(ext => ext.id); + const allExtensionsIds = allExtensions.map(ext => ext.identifier); const activatedExtensions = allExtensionsIds.filter(id => this.isActivated(id)); allPromises = activatedExtensions.map((extensionId) => { @@ -246,7 +247,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { await allPromises; } - public isActivated(extensionId: string): boolean { + public isActivated(extensionId: CanonicalExtensionIdentifier): boolean { if (this._barrier.isOpen()) { return this._activator.isActivated(extensionId); } @@ -258,11 +259,11 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { return this._activator.activateByEvent(activationEvent, reason); } - private _activateById(extensionId: string, reason: ExtensionActivationReason): Promise { + private _activateById(extensionId: CanonicalExtensionIdentifier, reason: ExtensionActivationReason): Promise { return this._activator.activateById(extensionId, reason); } - public activateByIdWithErrors(extensionId: string, reason: ExtensionActivationReason): Promise { + public activateByIdWithErrors(extensionId: CanonicalExtensionIdentifier, reason: ExtensionActivationReason): Promise { return this._activateById(extensionId, reason).then(() => { const extension = this._activator.getActivatedExtension(extensionId); if (extension.activationFailed) { @@ -277,7 +278,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { return this._barrier.wait().then(_ => this._registry); } - public getExtensionExports(extensionId: string): IExtensionAPI { + public getExtensionExports(extensionId: CanonicalExtensionIdentifier): IExtensionAPI { if (this._barrier.isOpen()) { return this._activator.getActivatedExtension(extensionId).exports; } else { @@ -300,7 +301,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { return this._extensionPathIndex; } - private _deactivate(extensionId: string): Promise { + private _deactivate(extensionId: CanonicalExtensionIdentifier): Promise { let result = Promise.resolve(void 0); if (!this._barrier.isOpen()) { @@ -338,22 +339,22 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { return result; } - public addMessage(extensionId: string, severity: Severity, message: string): void { + public addMessage(extensionId: CanonicalExtensionIdentifier, severity: Severity, message: string): void { this._mainThreadExtensionsProxy.$addMessage(extensionId, severity, message); } // --- impl private _activateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise { - this._mainThreadExtensionsProxy.$onWillActivateExtension(extensionDescription.id); + this._mainThreadExtensionsProxy.$onWillActivateExtension(extensionDescription.identifier); return this._doActivateExtension(extensionDescription, reason).then((activatedExtension) => { const activationTimes = activatedExtension.activationTimes; let activationEvent = (reason instanceof ExtensionActivatedByEvent ? reason.activationEvent : null); - this._mainThreadExtensionsProxy.$onDidActivateExtension(extensionDescription.id, activationTimes.startup, activationTimes.codeLoadingTime, activationTimes.activateCallTime, activationTimes.activateResolvedTime, activationEvent); + this._mainThreadExtensionsProxy.$onDidActivateExtension(extensionDescription.identifier, activationTimes.startup, activationTimes.codeLoadingTime, activationTimes.activateCallTime, activationTimes.activateResolvedTime, activationEvent); this._logExtensionActivationTimes(extensionDescription, reason, 'success', activationTimes); return activatedExtension; }, (err) => { - this._mainThreadExtensionsProxy.$onExtensionActivationFailed(extensionDescription.id); + this._mainThreadExtensionsProxy.$onExtensionActivationFailed(extensionDescription.identifier); this._logExtensionActivationTimes(extensionDescription, reason, 'failure'); throw err; }); @@ -392,23 +393,23 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { return Promise.resolve(new EmptyExtension(ExtensionActivationTimes.NONE)); } - this._extHostLogService.info(`ExtensionService#_doActivateExtension ${extensionDescription.id} ${JSON.stringify(reason)}`); + this._extHostLogService.info(`ExtensionService#_doActivateExtension ${extensionDescription.identifier.value} ${JSON.stringify(reason)}`); const activationTimesBuilder = new ExtensionActivationTimesBuilder(reason.startup); return Promise.all([ loadCommonJSModule(this._extHostLogService, extensionDescription.main, activationTimesBuilder), this._loadExtensionContext(extensionDescription) ]).then(values => { - return ExtHostExtensionService._callActivate(this._extHostLogService, extensionDescription.id, values[0], values[1], activationTimesBuilder); + return ExtHostExtensionService._callActivate(this._extHostLogService, extensionDescription.identifier, values[0], values[1], activationTimesBuilder); }); } private _loadExtensionContext(extensionDescription: IExtensionDescription): Promise { - let globalState = new ExtensionMemento(extensionDescription.id, true, this._storage); - let workspaceState = new ExtensionMemento(extensionDescription.id, false, this._storage); + let globalState = new ExtensionMemento(extensionDescription.identifier.value, true, this._storage); + let workspaceState = new ExtensionMemento(extensionDescription.identifier.value, false, this._storage); - this._extHostLogService.trace(`ExtensionService#loadExtensionContext ${extensionDescription.id}`); + this._extHostLogService.trace(`ExtensionService#loadExtensionContext ${extensionDescription.identifier.value}`); return Promise.all([ globalState.whenReady, workspaceState.whenReady, @@ -423,12 +424,12 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { storagePath: this._storagePath.workspaceValue(extensionDescription), get globalStoragePath(): string { checkProposedApiEnabled(extensionDescription); return that._storagePath.globalValue(extensionDescription); }, asAbsolutePath: (relativePath: string) => { return path.join(extensionDescription.extensionLocation.fsPath, relativePath); }, - logPath: that._extHostLogService.getLogDirectory(extensionDescription.id) + logPath: that._extHostLogService.getLogDirectory(extensionDescription.identifier) }); }); } - private static _callActivate(logService: ILogService, extensionId: string, extensionModule: IExtensionModule, context: IExtensionContext, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise { + private static _callActivate(logService: ILogService, extensionId: CanonicalExtensionIdentifier, extensionModule: IExtensionModule, context: IExtensionContext, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise { // Make sure the extension's surface is not undefined extensionModule = extensionModule || { activate: undefined, @@ -440,11 +441,11 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { }); } - private static _callActivateOptional(logService: ILogService, extensionId: string, extensionModule: IExtensionModule, context: IExtensionContext, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise { + private static _callActivateOptional(logService: ILogService, extensionId: CanonicalExtensionIdentifier, extensionModule: IExtensionModule, context: IExtensionContext, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise { if (typeof extensionModule.activate === 'function') { try { activationTimesBuilder.activateCallStart(); - logService.trace(`ExtensionService#_callActivateOptional ${extensionId}`); + logService.trace(`ExtensionService#_callActivateOptional ${extensionId.value}`); const activateResult: Promise = extensionModule.activate.apply(global, [context]); activationTimesBuilder.activateCallStop(); @@ -509,13 +510,13 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { return Promise.resolve(void 0); } - const fileNamePromise = Promise.all(fileNames.map((fileName) => this._activateIfFileName(workspace, desc.id, fileName))).then(() => { }); - const globPatternPromise = this._activateIfGlobPatterns(desc.id, globPatterns); + const fileNamePromise = Promise.all(fileNames.map((fileName) => this._activateIfFileName(workspace, desc.identifier, fileName))).then(() => { }); + const globPatternPromise = this._activateIfGlobPatterns(desc.identifier, globPatterns); return Promise.all([fileNamePromise, globPatternPromise]).then(() => { }); } - private async _activateIfFileName(workspace: IWorkspaceData, extensionId: string, fileName: string): Promise { + private async _activateIfFileName(workspace: IWorkspaceData, extensionId: CanonicalExtensionIdentifier, fileName: string): Promise { // find exact path for (const { uri } of workspace.folders) { @@ -531,8 +532,8 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { return undefined; } - private async _activateIfGlobPatterns(extensionId: string, globPatterns: string[]): Promise { - this._extHostLogService.trace(`extensionHostMain#activateIfGlobPatterns: fileSearch, extension: ${extensionId}, entryPoint: workspaceContains`); + private async _activateIfGlobPatterns(extensionId: CanonicalExtensionIdentifier, globPatterns: string[]): Promise { + this._extHostLogService.trace(`extensionHostMain#activateIfGlobPatterns: fileSearch, extension: ${extensionId.value}, entryPoint: workspaceContains`); if (globPatterns.length === 0) { return Promise.resolve(void 0); @@ -634,7 +635,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { throw new Error(`Not implemented`); } - public $startExtensionHost(enabledExtensionIds: string[]): Promise { + public $startExtensionHost(enabledExtensionIds: CanonicalExtensionIdentifier[]): Promise { this._registry.keepOnly(enabledExtensionIds); return this._startExtensionHost(); } @@ -678,7 +679,7 @@ function getTelemetryActivationEvent(extensionDescription: IExtensionDescription } */ let event = { - id: extensionDescription.id, + id: extensionDescription.identifier.value, name: extensionDescription.name, extensionVersion: extensionDescription.version, publisherDisplayName: extensionDescription.publisher, diff --git a/src/vs/workbench/api/node/extHostLanguageFeatures.ts b/src/vs/workbench/api/node/extHostLanguageFeatures.ts index 5aed24a9c16..9c0aa3f310b 100644 --- a/src/vs/workbench/api/node/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/node/extHostLanguageFeatures.ts @@ -25,6 +25,7 @@ import { ISelection, Selection } from 'vs/editor/common/core/selection'; import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import { ILogService } from 'vs/platform/log/common/log'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; // --- adapter @@ -289,7 +290,7 @@ class CodeActionAdapter { private readonly _diagnostics: ExtHostDiagnostics, private readonly _provider: vscode.CodeActionProvider, private readonly _logService: ILogService, - private readonly _extensionId: string + private readonly _extensionId: CanonicalExtensionIdentifier ) { } provideCodeActions(resource: URI, rangeOrSelection: IRange | ISelection, context: modes.CodeActionContext, token: CancellationToken): Promise { @@ -330,9 +331,9 @@ class CodeActionAdapter { } else { if (codeActionContext.only) { if (!candidate.kind) { - this._logService.warn(`${this._extensionId} - Code actions of kind '${codeActionContext.only.value} 'requested but returned code action does not have a 'kind'. Code action will be dropped. Please set 'CodeAction.kind'.`); + this._logService.warn(`${this._extensionId.value} - Code actions of kind '${codeActionContext.only.value} 'requested but returned code action does not have a 'kind'. Code action will be dropped. Please set 'CodeAction.kind'.`); } else if (!codeActionContext.only.contains(candidate.kind)) { - this._logService.warn(`${this._extensionId} -Code actions of kind '${codeActionContext.only.value} 'requested but returned code action is of kind '${candidate.kind.value}'. Code action will be dropped. Please check 'CodeActionContext.only' to only return requested code actions.`); + this._logService.warn(`${this._extensionId.value} -Code actions of kind '${codeActionContext.only.value} 'requested but returned code action is of kind '${candidate.kind.value}'. Code action will be dropped. Please check 'CodeActionContext.only' to only return requested code actions.`); } } @@ -975,14 +976,14 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { let t1: number; if (data.extension) { t1 = Date.now(); - this._logService.trace(`[${data.extension.id}] INVOKE provider '${(ctor as any).name}'`); + this._logService.trace(`[${data.extension.identifier.value}] INVOKE provider '${(ctor as any).name}'`); } let p = callback(data.adapter); if (data.extension) { Promise.resolve(p).then( - () => this._logService.trace(`[${data.extension.id}] provider DONE after ${Date.now() - t1}ms`), + () => this._logService.trace(`[${data.extension.identifier.value}] provider DONE after ${Date.now() - t1}ms`), err => { - this._logService.error(`[${data.extension.id}] provider FAILED`); + this._logService.error(`[${data.extension.identifier.value}] provider FAILED`); this._logService.error(err); } ); @@ -1081,7 +1082,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { // --- extra info - registerHoverProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.HoverProvider, extensionId?: string): vscode.Disposable { + registerHoverProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.HoverProvider, extensionId?: CanonicalExtensionIdentifier): vscode.Disposable { const handle = this._addNewAdapter(new HoverAdapter(this._documents, provider), extension); this._proxy.$registerHoverProvider(handle, this._transformDocumentSelector(selector)); return this._createDisposable(handle); @@ -1118,7 +1119,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { // --- quick fix registerCodeActionProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.CodeActionProvider, metadata?: vscode.CodeActionProviderMetadata): vscode.Disposable { - const handle = this._addNewAdapter(new CodeActionAdapter(this._documents, this._commands.converter, this._diagnostics, provider, this._logService, extension.id), extension); + const handle = this._addNewAdapter(new CodeActionAdapter(this._documents, this._commands.converter, this._diagnostics, provider, this._logService, extension.identifier), extension); this._proxy.$registerQuickFixSupport(handle, this._transformDocumentSelector(selector), metadata && metadata.providedCodeActionKinds ? metadata.providedCodeActionKinds.map(kind => kind.value) : undefined); return this._createDisposable(handle); } diff --git a/src/vs/workbench/api/node/extHostLogService.ts b/src/vs/workbench/api/node/extHostLogService.ts index 5db0676d386..f7381f5000a 100644 --- a/src/vs/workbench/api/node/extHostLogService.ts +++ b/src/vs/workbench/api/node/extHostLogService.ts @@ -9,6 +9,7 @@ import { createSpdLogService } from 'vs/platform/log/node/spdlogService'; import { ExtHostLogServiceShape } from 'vs/workbench/api/node/extHost.protocol'; import { ExtensionHostLogFileName } from 'vs/workbench/services/extensions/common/extensions'; import { URI } from 'vs/base/common/uri'; +import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; export class ExtHostLogService extends DelegatedLogService implements ILogService, ExtHostLogServiceShape { @@ -29,7 +30,7 @@ export class ExtHostLogService extends DelegatedLogService implements ILogServic this.setLevel(level); } - getLogDirectory(extensionID: string): string { - return join(this._logsPath, extensionID); + getLogDirectory(extensionID: CanonicalExtensionIdentifier): string { + return join(this._logsPath, extensionID.value); } } diff --git a/src/vs/workbench/api/node/extHostQuickOpen.ts b/src/vs/workbench/api/node/extHostQuickOpen.ts index cd4adf9759e..bc64c05120d 100644 --- a/src/vs/workbench/api/node/extHostQuickOpen.ts +++ b/src/vs/workbench/api/node/extHostQuickOpen.ts @@ -14,6 +14,7 @@ import { ExtHostQuickOpenShape, IMainContext, MainContext, MainThreadQuickOpenSh import { URI } from 'vs/base/common/uri'; import { ThemeIcon, QuickInputButtons } from 'vs/workbench/api/node/extHostTypes'; import { isPromiseCanceledError } from 'vs/base/common/errors'; +import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; export type Item = string | QuickPickItem; @@ -169,13 +170,13 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape { // ---- QuickInput - createQuickPick(extensionId: string, enableProposedApi: boolean): QuickPick { + createQuickPick(extensionId: CanonicalExtensionIdentifier, enableProposedApi: boolean): QuickPick { const session = new ExtHostQuickPick(this._proxy, extensionId, enableProposedApi, () => this._sessions.delete(session._id)); this._sessions.set(session._id, session); return session; } - createInputBox(extensionId: string): InputBox { + createInputBox(extensionId: CanonicalExtensionIdentifier): InputBox { const session = new ExtHostInputBox(this._proxy, extensionId, () => this._sessions.delete(session._id)); this._sessions.set(session._id, session); return session; @@ -256,7 +257,7 @@ class ExtHostQuickInput implements QuickInput { this._onDidChangeValueEmitter ]; - constructor(protected _proxy: MainThreadQuickOpenShape, protected _extensionId: string, private _onDidDispose: () => void) { + constructor(protected _proxy: MainThreadQuickOpenShape, protected _extensionId: CanonicalExtensionIdentifier, private _onDidDispose: () => void) { } get title() { @@ -479,7 +480,7 @@ class ExtHostQuickPick extends ExtHostQuickInput implem private _selectedItems: T[] = []; private _onDidChangeSelectionEmitter = new Emitter(); - constructor(proxy: MainThreadQuickOpenShape, extensionId: string, enableProposedApi: boolean, onDispose: () => void) { + constructor(proxy: MainThreadQuickOpenShape, extensionId: CanonicalExtensionIdentifier, enableProposedApi: boolean, onDispose: () => void) { super(proxy, extensionId, onDispose); this._disposables.push( this._onDidChangeActiveEmitter, @@ -580,7 +581,7 @@ class ExtHostInputBox extends ExtHostQuickInput implements InputBox { private _prompt: string; private _validationMessage: string; - constructor(proxy: MainThreadQuickOpenShape, extensionId: string, onDispose: () => void) { + constructor(proxy: MainThreadQuickOpenShape, extensionId: CanonicalExtensionIdentifier, onDispose: () => void) { super(proxy, extensionId, onDispose); this.update({ type: 'inputBox' }); } diff --git a/src/vs/workbench/api/node/extHostSCM.ts b/src/vs/workbench/api/node/extHostSCM.ts index 54013036c11..e6bb1e7fe33 100644 --- a/src/vs/workbench/api/node/extHostSCM.ts +++ b/src/vs/workbench/api/node/extHostSCM.ts @@ -17,6 +17,7 @@ import * as vscode from 'vscode'; import { ISplice } from 'vs/base/common/sequence'; import { ILogService } from 'vs/platform/log/common/log'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; type ProviderHandle = number; type GroupHandle = number; @@ -178,7 +179,7 @@ export class ExtHostSCMInputBox implements vscode.SourceControlInputBox { get validateInput(): IValidateInput { if (!this._extension.enableProposedApi) { - throw new Error(`[${this._extension.id}]: Proposed API is only available when running out of dev or with the following command line switch: --enable-proposed-api ${this._extension.id}`); + throw new Error(`[${this._extension.identifier.value}]: Proposed API is only available when running out of dev or with the following command line switch: --enable-proposed-api ${this._extension.identifier.value}`); } return this._validateInput; @@ -186,7 +187,7 @@ export class ExtHostSCMInputBox implements vscode.SourceControlInputBox { set validateInput(fn: IValidateInput) { if (!this._extension.enableProposedApi) { - throw new Error(`[${this._extension.id}]: Proposed API is only available when running out of dev or with the following command line switch: --enable-proposed-api ${this._extension.id}`); + throw new Error(`[${this._extension.identifier.value}]: Proposed API is only available when running out of dev or with the following command line switch: --enable-proposed-api ${this._extension.identifier.value}`); } if (fn && typeof fn !== 'function') { @@ -584,24 +585,24 @@ export class ExtHostSCM implements ExtHostSCMShape { } createSourceControl(extension: IExtensionDescription, id: string, label: string, rootUri: vscode.Uri | undefined): vscode.SourceControl { - this.logService.trace('ExtHostSCM#createSourceControl', extension.id, id, label, rootUri); + this.logService.trace('ExtHostSCM#createSourceControl', extension.identifier.value, id, label, rootUri); const handle = ExtHostSCM._handlePool++; const sourceControl = new ExtHostSourceControl(extension, this._proxy, this._commands, id, label, rootUri); this._sourceControls.set(handle, sourceControl); - const sourceControls = this._sourceControlsByExtension.get(extension.id) || []; + const sourceControls = this._sourceControlsByExtension.get(CanonicalExtensionIdentifier.toKey(extension.identifier)) || []; sourceControls.push(sourceControl); - this._sourceControlsByExtension.set(extension.id, sourceControls); + this._sourceControlsByExtension.set(CanonicalExtensionIdentifier.toKey(extension.identifier), sourceControls); return sourceControl; } // Deprecated getLastInputBox(extension: IExtensionDescription): ExtHostSCMInputBox { - this.logService.trace('ExtHostSCM#getLastInputBox', extension.id); + this.logService.trace('ExtHostSCM#getLastInputBox', extension.identifier.value); - const sourceControls = this._sourceControlsByExtension.get(extension.id); + const sourceControls = this._sourceControlsByExtension.get(CanonicalExtensionIdentifier.toKey(extension.identifier)); const sourceControl = sourceControls && sourceControls[sourceControls.length - 1]; const inputBox = sourceControl && sourceControl.inputBox; diff --git a/src/vs/workbench/api/node/extHostStatusBar.ts b/src/vs/workbench/api/node/extHostStatusBar.ts index 7531afcb45f..5e3522c1451 100644 --- a/src/vs/workbench/api/node/extHostStatusBar.ts +++ b/src/vs/workbench/api/node/extHostStatusBar.ts @@ -7,6 +7,7 @@ import { StatusbarAlignment as MainThreadStatusBarAlignment } from 'vs/platform/ import { StatusBarAlignment as ExtHostStatusBarAlignment, Disposable, ThemeColor } from './extHostTypes'; import { StatusBarItem, StatusBarAlignment } from 'vscode'; import { MainContext, MainThreadStatusBarShape, IMainContext } from './extHost.protocol'; +import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; export class ExtHostStatusBarEntry implements StatusBarItem { private static ID_GEN = 0; @@ -25,9 +26,9 @@ export class ExtHostStatusBarEntry implements StatusBarItem { private _timeoutHandle: any; private _proxy: MainThreadStatusBarShape; - private _extensionId: string; + private _extensionId: CanonicalExtensionIdentifier; - constructor(proxy: MainThreadStatusBarShape, extensionId: string, alignment: ExtHostStatusBarAlignment = ExtHostStatusBarAlignment.Left, priority?: number) { + constructor(proxy: MainThreadStatusBarShape, extensionId: CanonicalExtensionIdentifier, alignment: ExtHostStatusBarAlignment = ExtHostStatusBarAlignment.Left, priority?: number) { this._id = ExtHostStatusBarEntry.ID_GEN++; this._proxy = proxy; this._alignment = alignment; @@ -166,7 +167,7 @@ export class ExtHostStatusBar { this._statusMessage = new StatusBarMessage(this); } - createStatusBarEntry(extensionId: string, alignment?: ExtHostStatusBarAlignment, priority?: number): StatusBarItem { + createStatusBarEntry(extensionId: CanonicalExtensionIdentifier, alignment?: ExtHostStatusBarAlignment, priority?: number): StatusBarItem { return new ExtHostStatusBarEntry(this._proxy, extensionId, alignment, priority); } diff --git a/src/vs/workbench/api/node/extHostTask.ts b/src/vs/workbench/api/node/extHostTask.ts index 84224aeabe7..e5400b7e70c 100644 --- a/src/vs/workbench/api/node/extHostTask.ts +++ b/src/vs/workbench/api/node/extHostTask.ts @@ -205,7 +205,7 @@ namespace TaskDTO { definition, name: value.name, source: { - extensionId: extension.id, + extensionId: extension.identifier.value, label: value.source, scope: scope }, diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts index 4cb85d347ea..afee433bb76 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -74,7 +74,7 @@ export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Termi private _pidPromise: Promise; private _pidPromiseComplete: (value: number) => any; - private readonly _onData: Emitter = new Emitter(); + private readonly _onData = new Emitter(); public get onDidWriteData(): Event { // Tell the main side to start sending data if it's not already this._idPromise.then(c => { @@ -167,7 +167,7 @@ export class ExtHostTerminalRenderer extends BaseExtHostTerminal implements vsco this._queueApiRequest(this._proxy.$terminalRendererSetName, [this._name]); } - private readonly _onInput: Emitter = new Emitter(); + private readonly _onInput = new Emitter(); public get onDidAcceptInput(): Event { this._checkDisposed(); this._queueApiRequest(this._proxy.$terminalRendererRegisterOnInputListener, [this._id]); diff --git a/src/vs/workbench/api/node/extHostUrls.ts b/src/vs/workbench/api/node/extHostUrls.ts index e458333f517..59d417f0e1c 100644 --- a/src/vs/workbench/api/node/extHostUrls.ts +++ b/src/vs/workbench/api/node/extHostUrls.ts @@ -8,6 +8,7 @@ import { MainContext, IMainContext, ExtHostUrlsShape, MainThreadUrlsShape } from import { URI, UriComponents } from 'vs/base/common/uri'; import { toDisposable } from 'vs/base/common/lifecycle'; import { onUnexpectedError } from 'vs/base/common/errors'; +import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; export class ExtHostUrls implements ExtHostUrlsShape { @@ -23,18 +24,18 @@ export class ExtHostUrls implements ExtHostUrlsShape { this._proxy = mainContext.getProxy(MainContext.MainThreadUrls); } - registerUriHandler(extensionId: string, handler: vscode.UriHandler): vscode.Disposable { - if (this.handles.has(extensionId)) { + registerUriHandler(extensionId: CanonicalExtensionIdentifier, handler: vscode.UriHandler): vscode.Disposable { + if (this.handles.has(CanonicalExtensionIdentifier.toKey(extensionId))) { throw new Error(`Protocol handler already registered for extension ${extensionId}`); } const handle = ExtHostUrls.HandlePool++; - this.handles.add(extensionId); + this.handles.add(CanonicalExtensionIdentifier.toKey(extensionId)); this.handlers.set(handle, handler); this._proxy.$registerUriHandler(handle, extensionId); return toDisposable(() => { - this.handles.delete(extensionId); + this.handles.delete(CanonicalExtensionIdentifier.toKey(extensionId)); this.handlers.delete(handle); this._proxy.$unregisterUriHandler(handle); }); diff --git a/src/vs/workbench/api/node/extHostWebview.ts b/src/vs/workbench/api/node/extHostWebview.ts index 6fa89896fa0..06544f18846 100644 --- a/src/vs/workbench/api/node/extHostWebview.ts +++ b/src/vs/workbench/api/node/extHostWebview.ts @@ -252,7 +252,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape { }; const handle = ExtHostWebviews.newHandle(); - this._proxy.$createWebviewPanel(handle, viewType, title, webviewShowOptions, options, extension.id, extension.extensionLocation); + this._proxy.$createWebviewPanel(handle, viewType, title, webviewShowOptions, options, extension.identifier, extension.extensionLocation); const webview = new ExtHostWebview(handle, this._proxy, options); const panel = new ExtHostWebviewPanel(handle, this._proxy, viewType, title, viewColumn, options, webview); diff --git a/src/vs/workbench/api/node/extHostWorkspace.ts b/src/vs/workbench/api/node/extHostWorkspace.ts index 15cb4162e77..dbd48022228 100644 --- a/src/vs/workbench/api/node/extHostWorkspace.ts +++ b/src/vs/workbench/api/node/extHostWorkspace.ts @@ -24,6 +24,7 @@ import { ITextQueryBuilderOptions } from 'vs/workbench/parts/search/common/query import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import * as vscode from 'vscode'; import { ExtHostWorkspaceShape, IMainContext, IWorkspaceData, MainContext, MainThreadMessageServiceShape, MainThreadWorkspaceShape } from './extHost.protocol'; +import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; function isFolderEqual(folderA: URI, folderB: URI): boolean { return isEqual(folderA, folderB, !isLinux); @@ -345,8 +346,8 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape { // --- search --- - findFiles(include: string | RelativePattern, exclude: vscode.GlobPattern, maxResults: number, extensionId: string, token: vscode.CancellationToken = CancellationToken.None): Promise { - this._logService.trace(`extHostWorkspace#findFiles: fileSearch, extension: ${extensionId}, entryPoint: findFiles`); + findFiles(include: string | RelativePattern, exclude: vscode.GlobPattern, maxResults: number, extensionId: CanonicalExtensionIdentifier, token: vscode.CancellationToken = CancellationToken.None): Promise { + this._logService.trace(`extHostWorkspace#findFiles: fileSearch, extension: ${extensionId.value}, entryPoint: findFiles`); let includePattern: string; let includeFolder: URI; @@ -380,8 +381,8 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape { .then(data => Array.isArray(data) ? data.map(URI.revive) : []); } - findTextInFiles(query: vscode.TextSearchQuery, options: vscode.FindTextInFilesOptions, callback: (result: vscode.TextSearchResult) => void, extensionId: string, token: vscode.CancellationToken = CancellationToken.None): Promise { - this._logService.trace(`extHostWorkspace#findTextInFiles: textSearch, extension: ${extensionId}, entryPoint: findTextInFiles`); + findTextInFiles(query: vscode.TextSearchQuery, options: vscode.FindTextInFilesOptions, callback: (result: vscode.TextSearchResult) => void, extensionId: CanonicalExtensionIdentifier, token: vscode.CancellationToken = CancellationToken.None): Promise { + this._logService.trace(`extHostWorkspace#findTextInFiles: textSearch, extension: ${extensionId.value}, entryPoint: findTextInFiles`); const requestId = this._requestIdProvider.getNext(); diff --git a/src/vs/workbench/browser/composite.ts b/src/vs/workbench/browser/composite.ts index c338654106d..3076e2891f3 100644 --- a/src/vs/workbench/browser/composite.ts +++ b/src/vs/workbench/browser/composite.ts @@ -32,6 +32,9 @@ export abstract class Composite extends Component implements IComposite { private readonly _onTitleAreaUpdate: Emitter = this._register(new Emitter()); get onTitleAreaUpdate(): Event { return this._onTitleAreaUpdate.event; } + private readonly _onDidChangeVisibility: Emitter = this._register(new Emitter()); + get onDidChangeVisibility(): Event { return this._onDidChangeVisibility.event; } + private _onDidFocus: Emitter; get onDidFocus(): Event { if (!this._onDidFocus) { @@ -64,9 +67,6 @@ export abstract class Composite extends Component implements IComposite { private visible: boolean; private parent: HTMLElement; - /** - * Create a new composite with the given ID and context. - */ constructor( id: string, private _telemetryService: ITelemetryService, @@ -122,7 +122,11 @@ export abstract class Composite extends Component implements IComposite { * If there is a long running opertaion it is fine to have it running in the background asyncly and return before. */ setVisible(visible: boolean): void { - this.visible = visible; + if (this.visible !== !!visible) { + this.visible = visible; + + this._onDidChangeVisibility.fire(visible); + } } /** diff --git a/src/vs/workbench/browser/labels.ts b/src/vs/workbench/browser/labels.ts index d5afcc62217..7a113d8126a 100644 --- a/src/vs/workbench/browser/labels.ts +++ b/src/vs/workbench/browser/labels.ts @@ -22,11 +22,13 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { Event, Emitter } from 'vs/base/common/event'; import { ILabelService } from 'vs/platform/label/common/label'; import { getIconClasses, getConfiguredLangId } from 'vs/editor/common/services/getIconClasses'; +import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -export interface IResourceLabel { +export interface IResourceLabelProps { + resource?: uri; name: string; description?: string; - resource?: uri; } export interface IResourceLabelOptions extends IIconLabelValueOptions { @@ -34,68 +36,231 @@ export interface IResourceLabelOptions extends IIconLabelValueOptions { fileDecorations?: { colors: boolean, badges: boolean, data?: IDecorationData }; } -export class ResourceLabel extends IconLabel { +export interface IFileLabelOptions extends IResourceLabelOptions { + hideLabel?: boolean; + hidePath?: boolean; +} + +export interface IResourceLabel extends IDisposable { + readonly element: HTMLElement; + readonly onDidRender: Event; + + /** + * Most generic way to apply a label with raw information. + */ + setLabel(label?: string, description?: string, options?: IIconLabelValueOptions): void; + + /** + * Convinient method to apply a label by passing a resource along. + * + * Note: for file resources consider to use the #setFile() method instead. + */ + setResource(label: IResourceLabelProps, options?: IResourceLabelOptions): void; + + /** + * Convinient method to render a file label based on a resource. + */ + setFile(resource: uri, options?: IFileLabelOptions): void; + + /** + * Convinient method to apply a label by passing an editor along. + */ + setEditor(editor: IEditorInput, options?: IResourceLabelOptions): void; + + /** + * Resets the label to be empty. + */ + clear(): void; +} + +export interface IResourceLabelsContainer { + readonly onDidChangeVisibility: Event; +} + +export const DEFAULT_LABELS_CONTAINER: IResourceLabelsContainer = { + onDidChangeVisibility: Event.None +}; + +export class ResourceLabels extends Disposable { + private _widgets: ResourceLabelWidget[] = []; + private _labels: IResourceLabel[] = []; + + constructor( + container: IResourceLabelsContainer, + @IInstantiationService private instantiationService: IInstantiationService, + @IExtensionService private extensionService: IExtensionService, + @IConfigurationService private configurationService: IConfigurationService, + @IModelService private modelService: IModelService, + @IDecorationsService private decorationsService: IDecorationsService, + @IThemeService private themeService: IThemeService + ) { + super(); + + this.registerListeners(container); + } + + private registerListeners(container: IResourceLabelsContainer): void { + + // notify when visibility changes + this._register(container.onDidChangeVisibility(visible => { + this._widgets.forEach(widget => widget.notifyVisibilityChanged(visible)); + })); + + // notify when extensions are registered with potentially new languages + this._register(this.extensionService.onDidRegisterExtensions(() => this._widgets.forEach(widget => widget.notifyExtensionsRegistered()))); + + // notify when model mode changes + this._register(this.modelService.onModelModeChanged(e => { + if (!e.model.uri) { + return; // we need the resource to compare + } + + if (e.model.uri.scheme === Schemas.file && e.oldModeId === PLAINTEXT_MODE_ID) { // todo@remote does this apply? + return; // ignore transitions in files from no mode to specific mode because this happens each time a model is created + } + + this._widgets.forEach(widget => widget.notifyModelModeChanged(e)); + })); + + // notify when file decoration changes + this._register(this.decorationsService.onDidChangeDecorations(e => this._widgets.forEach(widget => widget.notifyFileDecorationsChanges(e)))); + + // notify when theme changes + this._register(this.themeService.onThemeChange(() => this._widgets.forEach(widget => widget.notifyThemeChange()))); + + // notify when files.associations changes + this._register(this.configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration(FILES_ASSOCIATIONS_CONFIG)) { + this._widgets.forEach(widget => widget.notifyFileAssociationsChange()); + } + })); + } + + get(index: number): IResourceLabel { + return this._labels[index]; + } + + create(container: HTMLElement, options?: IIconLabelCreationOptions): IResourceLabel { + const widget = this.instantiationService.createInstance(ResourceLabelWidget, container, options); + + // Only expose a handle to the outside + const label: IResourceLabel = { + element: widget.element, + onDidRender: widget.onDidRender, + setLabel: (label?: string, description?: string, options?: IIconLabelValueOptions) => widget.setLabel(label, description, options), + setResource: (label: IResourceLabelProps, options?: IResourceLabelOptions) => widget.setResource(label, options), + setEditor: (editor: IEditorInput, options?: IResourceLabelOptions) => widget.setEditor(editor, options), + setFile: (resource: uri, options?: IFileLabelOptions) => widget.setFile(resource, options), + clear: () => widget.clear(), + dispose: () => this.disposeWidget(widget) + }; + + // Store + this._labels.push(label); + this._widgets.push(widget); + + return label; + } + + private disposeWidget(widget: ResourceLabelWidget): void { + const index = this._widgets.indexOf(widget); + if (index > -1) { + this._widgets.splice(index, 1); + this._labels.splice(index, 1); + } + + dispose(widget); + } + + clear(): void { + this._widgets = dispose(this._widgets); + this._labels = []; + } + + dispose(): void { + super.dispose(); + + this.clear(); + } +} + +/** + * Note: please consider to use ResourceLabels if you are in need + * of more than one label for your widget. + */ +export class ResourceLabel extends ResourceLabels { + + private _label: IResourceLabel; + + constructor( + container: HTMLElement, + options: IIconLabelCreationOptions, + @IInstantiationService instantiationService: IInstantiationService, + @IExtensionService extensionService: IExtensionService, + @IConfigurationService configurationService: IConfigurationService, + @IModelService modelService: IModelService, + @IDecorationsService decorationsService: IDecorationsService, + @IThemeService themeService: IThemeService, + @ILabelService labelService: ILabelService + ) { + super(DEFAULT_LABELS_CONTAINER, instantiationService, extensionService, configurationService, modelService, decorationsService, themeService); + + this._label = this._register(this.create(container, options)); + } + + get element(): IResourceLabel { + return this._label; + } +} + +enum Redraw { + Basic = 1, + Full = 2 +} + +class ResourceLabelWidget extends IconLabel { private _onDidRender = this._register(new Emitter()); get onDidRender(): Event { return this._onDidRender.event; } - private label: IResourceLabel; + private label: IResourceLabelProps; private options: IResourceLabelOptions; private computedIconClasses: string[]; private lastKnownConfiguredLangId: string; private computedPathLabel: string; + private needsRedraw: Redraw; + private isHidden: boolean = false; + constructor( container: HTMLElement, options: IIconLabelCreationOptions, - @IExtensionService private extensionService: IExtensionService, - @IConfigurationService private configurationService: IConfigurationService, @IModeService private modeService: IModeService, @IModelService private modelService: IModelService, - @IDecorationsService protected decorationsService: IDecorationsService, - @IThemeService private themeService: IThemeService, - @ILabelService protected labelService: ILabelService + @IDecorationsService private decorationsService: IDecorationsService, + @ILabelService private labelService: ILabelService, + @IUntitledEditorService private untitledEditorService: IUntitledEditorService, + @IWorkspaceContextService private contextService: IWorkspaceContextService ) { super(container, options); - - this.registerListeners(); } - private registerListeners(): void { + notifyVisibilityChanged(visible: boolean): void { + if (visible === this.isHidden) { + this.isHidden = !visible; - // update when extensions are registered with potentially new languages - this._register(this.extensionService.onDidRegisterExtensions(() => this.render(true /* clear cache */))); - - // react to model mode changes - this._register(this.modelService.onModelModeChanged(e => this.onModelModeChanged(e))); - - // react to file decoration changes - this._register(this.decorationsService.onDidChangeDecorations(this.onFileDecorationsChanges, this)); - - // react to theme changes - this._register(this.themeService.onThemeChange(() => this.render(false))); - - // react to files.associations changes - this._register(this.configurationService.onDidChangeConfiguration(e => { - if (e.affectsConfiguration(FILES_ASSOCIATIONS_CONFIG)) { - this.render(true /* clear cache */); + if (visible && this.needsRedraw) { + this.render(this.needsRedraw === Redraw.Basic ? false : true); + this.needsRedraw = void 0; } - })); + } } - private onModelModeChanged(e: { model: ITextModel; oldModeId: string; }): void { + notifyModelModeChanged(e: { model: ITextModel; oldModeId: string; }): void { if (!this.label || !this.label.resource) { return; // only update if label exists } - if (!e.model.uri) { - return; // we need the resource to compare - } - - if (e.model.uri.scheme === Schemas.file && e.oldModeId === PLAINTEXT_MODE_ID) { // todo@remote does this apply? - return; // ignore transitions in files from no mode to specific mode because this happens each time a model is created - } - if (e.model.uri.toString() === this.label.resource.toString()) { if (this.lastKnownConfiguredLangId !== e.model.getLanguageIdentifier().language) { this.render(true); // update if the language id of the model has changed from our last known state @@ -103,7 +268,7 @@ export class ResourceLabel extends IconLabel { } } - private onFileDecorationsChanges(e: IResourceDecorationChangeEvent): void { + notifyFileDecorationsChanges(e: IResourceDecorationChangeEvent): void { if (!this.options || !this.label || !this.label.resource) { return; } @@ -113,7 +278,19 @@ export class ResourceLabel extends IconLabel { } } - setLabel(label: IResourceLabel, options?: IResourceLabelOptions): void { + notifyExtensionsRegistered(): void { + this.render(true); + } + + notifyThemeChange(): void { + this.render(false); + } + + notifyFileAssociationsChange(): void { + this.render(true); + } + + setResource(label: IResourceLabelProps, options?: IResourceLabelOptions): void { const hasResourceChanged = this.hasResourceChanged(label, options); this.label = label; @@ -126,7 +303,7 @@ export class ResourceLabel extends IconLabel { this.render(hasResourceChanged); } - private hasResourceChanged(label: IResourceLabel, options: IResourceLabelOptions): boolean { + private hasResourceChanged(label: IResourceLabelProps, options: IResourceLabelOptions): boolean { const newResource = label ? label.resource : void 0; const oldResource = this.label ? this.label.resource : void 0; @@ -152,6 +329,39 @@ export class ResourceLabel extends IconLabel { return true; } + setEditor(editor: IEditorInput, options?: IResourceLabelOptions): void { + this.setResource({ + resource: toResource(editor, { supportSideBySide: true }), + name: editor.getName(), + description: editor.getDescription() + }, options); + } + + setFile(resource: uri, options?: IFileLabelOptions): void { + const hideLabel = options && options.hideLabel; + let name: string; + if (!hideLabel) { + if (options && options.fileKind === FileKind.ROOT_FOLDER) { + const workspaceFolder = this.contextService.getWorkspaceFolder(resource); + if (workspaceFolder) { + name = workspaceFolder.name; + } + } + + if (!name) { + name = resources.basenameOrAuthority(resource); + } + } + + let description: string; + const hidePath = (options && options.hidePath) || (resource.scheme === Schemas.untitled && !this.untitledEditorService.hasAssociatedFilePath(resource)); + if (!hidePath) { + description = this.labelService.getUriLabel(resources.dirname(resource), { relative: true }); + } + + this.setResource({ resource, name, description }, options); + } + clear(): void { this.label = void 0; this.options = void 0; @@ -159,10 +369,22 @@ export class ResourceLabel extends IconLabel { this.computedIconClasses = void 0; this.computedPathLabel = void 0; - this.setValue(); + this.setLabel(); } private render(clearIconCache: boolean): void { + if (this.isHidden) { + if (!this.needsRedraw) { + this.needsRedraw = clearIconCache ? Redraw.Full : Redraw.Basic; + } + + if (this.needsRedraw === Redraw.Basic && clearIconCache) { + this.needsRedraw = Redraw.Full; + } + + return; + } + if (this.label) { const configuredLangId = getConfiguredLangId(this.modelService, this.label.resource); if (this.lastKnownConfiguredLangId !== configuredLangId) { @@ -231,7 +453,7 @@ export class ResourceLabel extends IconLabel { } } - this.setValue(label, this.label.description, iconLabelOptions); + this.setLabel(label, this.label.description, iconLabelOptions); this._onDidRender.fire(); } @@ -246,63 +468,3 @@ export class ResourceLabel extends IconLabel { this.computedPathLabel = void 0; } } - -export class EditorLabel extends ResourceLabel { - - setEditor(editor: IEditorInput, options?: IResourceLabelOptions): void { - this.setLabel({ - resource: toResource(editor, { supportSideBySide: true }), - name: editor.getName(), - description: editor.getDescription() - }, options); - } -} - -export interface IFileLabelOptions extends IResourceLabelOptions { - hideLabel?: boolean; - hidePath?: boolean; -} - -export class FileLabel extends ResourceLabel { - - constructor( - container: HTMLElement, - options: IIconLabelCreationOptions, - @IExtensionService extensionService: IExtensionService, - @IWorkspaceContextService private contextService: IWorkspaceContextService, - @IConfigurationService configurationService: IConfigurationService, - @IModeService modeService: IModeService, - @IModelService modelService: IModelService, - @IDecorationsService decorationsService: IDecorationsService, - @IThemeService themeService: IThemeService, - @IUntitledEditorService private untitledEditorService: IUntitledEditorService, - @ILabelService labelService: ILabelService - ) { - super(container, options, extensionService, configurationService, modeService, modelService, decorationsService, themeService, labelService); - } - - setFile(resource: uri, options?: IFileLabelOptions): void { - const hideLabel = options && options.hideLabel; - let name: string; - if (!hideLabel) { - if (options && options.fileKind === FileKind.ROOT_FOLDER) { - const workspaceFolder = this.contextService.getWorkspaceFolder(resource); - if (workspaceFolder) { - name = workspaceFolder.name; - } - } - - if (!name) { - name = resources.basenameOrAuthority(resource); - } - } - - let description: string; - const hidePath = (options && options.hidePath) || (resource.scheme === Schemas.untitled && !this.untitledEditorService.hasAssociatedFilePath(resource)); - if (!hidePath) { - description = this.labelService.getUriLabel(resources.dirname(resource), { relative: true }); - } - - this.setLabel({ resource, name, description }, options); - } -} diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts index ab096bf28c5..c8d4a5e084c 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts @@ -35,7 +35,7 @@ import { ColorIdentifier, ColorFunction } from 'vs/platform/theme/common/colorRe import { attachBreadcrumbsStyler } from 'vs/platform/theme/common/styler'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { FileLabel } from 'vs/workbench/browser/labels'; +import { ResourceLabel } from 'vs/workbench/browser/labels'; import { BreadcrumbsConfig, IBreadcrumbsService } from 'vs/workbench/browser/parts/editor/breadcrumbs'; import { BreadcrumbElement, EditorBreadcrumbsModel, FileElement } from 'vs/workbench/browser/parts/editor/breadcrumbsModel'; import { BreadcrumbsPicker, createBreadcrumbsPicker } from 'vs/workbench/browser/parts/editor/breadcrumbsPicker'; @@ -78,8 +78,8 @@ class Item extends BreadcrumbsItem { render(container: HTMLElement): void { if (this.element instanceof FileElement) { // file/folder - let label = this._instantiationService.createInstance(FileLabel, container, {}); - label.setFile(this.element.uri, { + let label = this._instantiationService.createInstance(ResourceLabel, container, {}); + label.element.setFile(this.element.uri, { hidePath: true, hideIcon: this.element.kind === FileKind.FOLDER || !this.options.showFileIcons, fileKind: this.element.kind, @@ -98,7 +98,7 @@ class Item extends BreadcrumbsItem { } else if (this.element instanceof OutlineGroup) { // provider let label = new IconLabel(container); - label.setValue(this.element.provider.displayName); + label.setLabel(this.element.provider.displayName); this._disposables.push(label); } else if (this.element instanceof OutlineElement) { @@ -111,7 +111,7 @@ class Item extends BreadcrumbsItem { } let label = new IconLabel(container); let title = this.element.symbol.name.replace(/\r|\n|\r\n/g, '\u23CE'); - label.setValue(title); + label.setLabel(title); this._disposables.push(label); } } diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts index 88055da1e0e..c42cea4a15d 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts @@ -25,7 +25,7 @@ import { IConstructorSignature1, IInstantiationService } from 'vs/platform/insta import { HighlightingWorkbenchTree, IHighlighter, IHighlightingTreeConfiguration, IHighlightingTreeOptions } from 'vs/platform/list/browser/listService'; import { breadcrumbsPickerBackground, widgetShadow } from 'vs/platform/theme/common/colorRegistry'; import { IWorkspace, IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; -import { FileLabel } from 'vs/workbench/browser/labels'; +import { ResourceLabels, IResourceLabel, DEFAULT_LABELS_CONTAINER } from 'vs/workbench/browser/labels'; import { BreadcrumbsConfig } from 'vs/workbench/browser/parts/editor/breadcrumbs'; import { BreadcrumbElement, FileElement } from 'vs/workbench/browser/parts/editor/breadcrumbsModel'; import { IFileIconTheme, IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; @@ -329,7 +329,7 @@ export class FileHighlighter implements IHighlighter { export class FileRenderer implements IRenderer { constructor( - @IInstantiationService private readonly _instantiationService: IInstantiationService, + private readonly _labels: ResourceLabels, @IConfigurationService private readonly _configService: IConfigurationService, ) { } @@ -342,10 +342,10 @@ export class FileRenderer implements IRenderer { } renderTemplate(tree: ITree, templateId: string, container: HTMLElement) { - return this._instantiationService.createInstance(FileLabel, container, { supportHighlights: true }); + return this._labels.create(container, { supportHighlights: true }); } - renderElement(tree: ITree, element: IFileStat | IWorkspaceFolder, templateId: string, templateData: FileLabel): void { + renderElement(tree: ITree, element: IFileStat | IWorkspaceFolder, templateId: string, templateData: IResourceLabel): void { let fileDecorations = this._configService.getValue<{ colors: boolean, badges: boolean }>('explorer.decorations'); let resource: URI; let fileKind: FileKind; @@ -365,7 +365,7 @@ export class FileRenderer implements IRenderer { }); } - disposeTemplate(tree: ITree, templateId: string, templateData: FileLabel): void { + disposeTemplate(tree: ITree, templateId: string, templateData: IResourceLabel): void { templateData.dispose(); } } @@ -427,7 +427,9 @@ export class BreadcrumbsFilePicker extends BreadcrumbsPicker { this._disposables.push(filter); config.dataSource = this._instantiationService.createInstance(FileDataSource); - config.renderer = this._instantiationService.createInstance(FileRenderer); + const labels = this._instantiationService.createInstance(ResourceLabels, DEFAULT_LABELS_CONTAINER /* TODO@Jo visibility propagation */); + this._disposables.push(labels); + config.renderer = this._instantiationService.createInstance(FileRenderer, labels); config.sorter = new FileSorter(); config.highlighter = new FileHighlighter(); config.filter = filter; diff --git a/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts index 7eddf53b4f0..0f3d668b463 100644 --- a/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts @@ -6,7 +6,7 @@ import 'vs/css!./media/notabstitlecontrol'; import { toResource, Verbosity, IEditorInput } from 'vs/workbench/common/editor'; import { TitleControl, IToolbarActions } from 'vs/workbench/browser/parts/editor/titleControl'; -import { ResourceLabel } from 'vs/workbench/browser/labels'; +import { ResourceLabel, IResourceLabel } from 'vs/workbench/browser/labels'; import { TAB_ACTIVE_FOREGROUND, TAB_UNFOCUSED_ACTIVE_FOREGROUND } from 'vs/workbench/common/theme'; import { EventType as TouchEventType, GestureEvent, Gesture } from 'vs/base/browser/touch'; import { addDisposableListener, EventType, addClass, EventHelper, removeClass, toggleClass } from 'vs/base/browser/dom'; @@ -22,7 +22,7 @@ interface IRenderedEditorLabel { export class NoTabsTitleControl extends TitleControl { private titleContainer: HTMLElement; - private editorLabel: ResourceLabel; + private editorLabel: IResourceLabel; private activeLabel: IRenderedEditorLabel = Object.create(null); protected create(parent: HTMLElement): void { @@ -40,8 +40,8 @@ export class NoTabsTitleControl extends TitleControl { this.titleContainer.appendChild(labelContainer); // Editor Label - this.editorLabel = this._register(this.instantiationService.createInstance(ResourceLabel, labelContainer, void 0)); - this._register(this.editorLabel.onClick(e => this.onTitleLabelClick(e))); + this.editorLabel = this._register(this.instantiationService.createInstance(ResourceLabel, labelContainer, void 0)).element; + this._register(addDisposableListener(this.editorLabel.element, EventType.CLICK, e => this.onTitleLabelClick(e))); // Breadcrumbs this.createBreadcrumbsControl(labelContainer, { showFileIcons: false, showSymbolIcons: true, showDecorationColors: false, breadcrumbsBackground: () => Color.transparent }); @@ -244,7 +244,7 @@ export class NoTabsTitleControl extends TitleControl { title = ''; // dont repeat what is already shown } - this.editorLabel.setLabel({ name, description, resource }, { title, italic: !isEditorPinned, extraClasses: ['no-tabs', 'title-label'] }); + this.editorLabel.setResource({ name, description, resource }, { title, italic: !isEditorPinned, extraClasses: ['no-tabs', 'title-label'] }); if (isGroupActive) { this.editorLabel.element.style.color = this.getColor(TAB_ACTIVE_FOREGROUND); } else { diff --git a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts index 94e3f71e332..f9bee82ba94 100644 --- a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts @@ -10,7 +10,7 @@ import { toResource, GroupIdentifier, IEditorInput, Verbosity, EditorCommandsCon import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { EventType as TouchEventType, GestureEvent, Gesture } from 'vs/base/browser/touch'; import { KeyCode } from 'vs/base/common/keyCodes'; -import { ResourceLabel } from 'vs/workbench/browser/labels'; +import { ResourceLabels, IResourceLabel, DEFAULT_LABELS_CONTAINER } from 'vs/workbench/browser/labels'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -57,7 +57,7 @@ export class TabsTitleControl extends TitleControl { private tabsScrollbar: ScrollableElement; private closeOneEditorAction: CloseOneEditorAction; - private tabLabelWidgets: ResourceLabel[] = []; + private tabResourceLabels: ResourceLabels; private tabLabels: IEditorInputLabel[] = []; private tabDisposeables: IDisposable[] = []; @@ -123,6 +123,9 @@ export class TabsTitleControl extends TitleControl { addClass(breadcrumbsContainer, 'tabs-breadcrumbs'); this.titleContainer.appendChild(breadcrumbsContainer); this.createBreadcrumbsControl(breadcrumbsContainer, { showFileIcons: true, showSymbolIcons: true, showDecorationColors: false, breadcrumbsBackground: breadcrumbsBackground }); + + // Tab Labels + this.tabResourceLabels = this._register(this.instantiationService.createInstance(ResourceLabels, DEFAULT_LABELS_CONTAINER)); } private createTabsScrollbar(scrollable: HTMLElement): ScrollableElement { @@ -295,7 +298,6 @@ export class TabsTitleControl extends TitleControl { (this.tabsContainer.lastChild as HTMLElement).remove(); // Remove associated tab label and widget - this.tabLabelWidgets.pop(); this.tabDisposeables.pop().dispose(); } @@ -311,7 +313,7 @@ export class TabsTitleControl extends TitleControl { clearNode(this.tabsContainer); this.tabDisposeables = dispose(this.tabDisposeables); - this.tabLabelWidgets = []; + this.tabResourceLabels.clear(); this.tabLabels = []; this.clearEditorActionsToolbar(); @@ -395,12 +397,12 @@ export class TabsTitleControl extends TitleControl { this.redraw(); } - private withTab(editor: IEditorInput, fn: (tabContainer: HTMLElement, tabLabelWidget: ResourceLabel, tabLabel: IEditorInputLabel) => void): void { + private withTab(editor: IEditorInput, fn: (tabContainer: HTMLElement, tabLabelWidget: IResourceLabel, tabLabel: IEditorInputLabel) => void): void { const editorIndex = this.group.getIndexOfEditor(editor); const tabContainer = this.tabsContainer.children[editorIndex] as HTMLElement; if (tabContainer) { - fn(tabContainer, this.tabLabelWidgets[editorIndex], this.tabLabels[editorIndex]); + fn(tabContainer, this.tabResourceLabels.get(editorIndex), this.tabLabels[editorIndex]); } } @@ -422,8 +424,7 @@ export class TabsTitleControl extends TitleControl { tabContainer.appendChild(tabBorderTopContainer); // Tab Editor Label - const editorLabel = this.instantiationService.createInstance(ResourceLabel, tabContainer, void 0); - this.tabLabelWidgets.push(editorLabel); + const editorLabel = this.tabResourceLabels.create(tabContainer); // Tab Close Button const tabCloseContainer = document.createElement('div'); @@ -806,16 +807,16 @@ export class TabsTitleControl extends TitleControl { this.layout(this.dimension); } - private forEachTab(fn: (editor: IEditorInput, index: number, tabContainer: HTMLElement, tabLabelWidget: ResourceLabel, tabLabel: IEditorInputLabel) => void): void { + private forEachTab(fn: (editor: IEditorInput, index: number, tabContainer: HTMLElement, tabLabelWidget: IResourceLabel, tabLabel: IEditorInputLabel) => void): void { this.group.editors.forEach((editor, index) => { const tabContainer = this.tabsContainer.children[index] as HTMLElement; if (tabContainer) { - fn(editor, index, tabContainer, this.tabLabelWidgets[index], this.tabLabels[index]); + fn(editor, index, tabContainer, this.tabResourceLabels.get(index), this.tabLabels[index]); } }); } - private redrawTab(editor: IEditorInput, index: number, tabContainer: HTMLElement, tabLabelWidget: ResourceLabel, tabLabel: IEditorInputLabel): void { + private redrawTab(editor: IEditorInput, index: number, tabContainer: HTMLElement, tabLabelWidget: IResourceLabel, tabLabel: IEditorInputLabel): void { // Label this.redrawLabel(editor, tabContainer, tabLabelWidget, tabLabel); @@ -848,7 +849,7 @@ export class TabsTitleControl extends TitleControl { this.redrawEditorActiveAndDirty(this.accessor.activeGroup === this.group, editor, tabContainer, tabLabelWidget); } - private redrawLabel(editor: IEditorInput, tabContainer: HTMLElement, tabLabelWidget: ResourceLabel, tabLabel: IEditorInputLabel): void { + private redrawLabel(editor: IEditorInput, tabContainer: HTMLElement, tabLabelWidget: IResourceLabel, tabLabel: IEditorInputLabel): void { const name = tabLabel.name; const description = tabLabel.description || ''; const title = tabLabel.title || ''; @@ -858,10 +859,10 @@ export class TabsTitleControl extends TitleControl { tabContainer.title = title; // Label - tabLabelWidget.setLabel({ name, description, resource: toResource(editor, { supportSideBySide: true }) }, { title, extraClasses: ['tab-label'], italic: !this.group.isPinned(editor) }); + tabLabelWidget.setResource({ name, description, resource: toResource(editor, { supportSideBySide: true }) }, { title, extraClasses: ['tab-label'], italic: !this.group.isPinned(editor) }); } - private redrawEditorActiveAndDirty(isGroupActive: boolean, editor: IEditorInput, tabContainer: HTMLElement, tabLabelWidget: ResourceLabel): void { + private redrawEditorActiveAndDirty(isGroupActive: boolean, editor: IEditorInput, tabContainer: HTMLElement, tabLabelWidget: IResourceLabel): void { const isTabActive = this.group.isActive(editor); const hasModifiedBorderTop = this.doRedrawEditorDirty(isGroupActive, isTabActive, editor, tabContainer); @@ -869,7 +870,7 @@ export class TabsTitleControl extends TitleControl { this.doRedrawEditorActive(isGroupActive, !hasModifiedBorderTop, editor, tabContainer, tabLabelWidget); } - private doRedrawEditorActive(isGroupActive: boolean, allowBorderTop: boolean, editor: IEditorInput, tabContainer: HTMLElement, tabLabelWidget: ResourceLabel): void { + private doRedrawEditorActive(isGroupActive: boolean, allowBorderTop: boolean, editor: IEditorInput, tabContainer: HTMLElement, tabLabelWidget: IResourceLabel): void { // Tab is active if (this.group.isActive(editor)) { diff --git a/src/vs/workbench/browser/parts/quickinput/quickInputList.ts b/src/vs/workbench/browser/parts/quickinput/quickInputList.ts index 05c1ba74d3a..4682fd42f75 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInputList.ts +++ b/src/vs/workbench/browser/parts/quickinput/quickInputList.ts @@ -144,7 +144,7 @@ class ListElementRenderer implements IListRenderer this.updateActions(), this, this.disposables); this.disposables.push(toDisposable(() => this.treeView.setVisibility(false))); - this.updateTreeVisibility(); - } - - setVisible(visible: boolean): void { - super.setVisible(visible); + this.disposables.push(this.onDidChangeBodyVisibility(() => this.updateTreeVisibility())); this.updateTreeVisibility(); } @@ -79,11 +75,6 @@ export class CustomTreeViewPanel extends ViewletPanel { this.treeView.show(container); } - setExpanded(expanded: boolean): void { - this.treeView.setVisibility(this.isVisible() && expanded); - super.setExpanded(expanded); - } - layoutBody(size: number): void { this.treeView.layout(size); } @@ -105,7 +96,7 @@ export class CustomTreeViewPanel extends ViewletPanel { } private updateTreeVisibility(): void { - this.treeView.setVisibility(this.isVisible() && this.isExpanded()); + this.treeView.setVisibility(this.isBodyVisible()); } dispose(): void { @@ -194,6 +185,7 @@ export class CustomTreeView extends Disposable implements ITreeView { private _messageValue: string | IMarkdownString | undefined; private messageElement: HTMLDivElement; private tree: FileIconThemableWorkbenchTree; + private treeLabels: ResourceLabels; private root: ITreeItem; private elementsToRefresh: ITreeItem[] = []; private menus: TitleMenus; @@ -382,13 +374,13 @@ export class CustomTreeView extends Disposable implements ITreeView { private createTree() { const actionItemProvider = (action: IAction) => action instanceof MenuItemAction ? this.instantiationService.createInstance(ContextAwareMenuItemActionItem, action) : undefined; - const menus = this.instantiationService.createInstance(TreeMenus, this.id); + const menus = this._register(this.instantiationService.createInstance(TreeMenus, this.id)); + this.treeLabels = this._register(this.instantiationService.createInstance(ResourceLabels, this)); const dataSource = this.instantiationService.createInstance(TreeDataSource, this, this.container); - const renderer = this.instantiationService.createInstance(TreeRenderer, this.id, menus, actionItemProvider); + const renderer = this.instantiationService.createInstance(TreeRenderer, this.id, menus, this.treeLabels, actionItemProvider); const controller = this.instantiationService.createInstance(TreeController, this.id, menus); - this.tree = this.instantiationService.createInstance(FileIconThemableWorkbenchTree, this.treeContainer, { dataSource, renderer, controller }, {}); + this.tree = this._register(this.instantiationService.createInstance(FileIconThemableWorkbenchTree, this.treeContainer, { dataSource, renderer, controller }, {})); this.tree.contextKeyService.createKey(this.id, true); - this._register(this.tree); this._register(this.tree.onDidChangeSelection(e => this.onSelection(e))); this._register(this.tree.onDidExpandItem(e => this._onDidExpandItem.fire(e.item.getElement()))); this._register(this.tree.onDidCollapseItem(e => this._onDidCollapseItem.fire(e.item.getElement()))); @@ -593,7 +585,7 @@ class TreeDataSource implements IDataSource { } interface ITreeExplorerTemplateData { - resourceLabel: ResourceLabel; + resourceLabel: IResourceLabel; icon: HTMLElement; actionBar: ActionBar; aligner: Aligner; @@ -632,8 +624,8 @@ class TreeRenderer implements IRenderer { constructor( private treeViewId: string, private menus: TreeMenus, + private labels: ResourceLabels, private actionItemProvider: IActionItemProvider, - @IInstantiationService private instantiationService: IInstantiationService, @IWorkbenchThemeService private themeService: IWorkbenchThemeService, @IConfigurationService private configurationService: IConfigurationService, @ILabelService private labelService: ILabelService @@ -652,7 +644,7 @@ class TreeRenderer implements IRenderer { DOM.addClass(container, 'custom-view-tree-node-item'); const icon = DOM.append(container, DOM.$('.custom-view-tree-node-item-icon')); - const resourceLabel = this.instantiationService.createInstance(ResourceLabel, container, { supportHighlights: true, donotSupportOcticons: true }); + const resourceLabel = this.labels.create(container, { supportHighlights: true, donotSupportOcticons: true }); DOM.addClass(resourceLabel.element, 'custom-view-tree-node-item-resourceLabel'); const actionsContainer = DOM.append(resourceLabel.element, DOM.$('.actions')); const actionBar = new ActionBar(actionsContainer, { @@ -674,14 +666,13 @@ class TreeRenderer implements IRenderer { const title = node.tooltip ? node.tooltip : resource ? void 0 : label; // reset - templateData.resourceLabel.clear(); templateData.actionBar.clear(); if (resource || node.themeIcon) { const fileDecorations = this.configurationService.getValue<{ colors: boolean, badges: boolean }>('explorer.decorations'); - templateData.resourceLabel.setLabel({ name: label, description, resource: resource ? resource : URI.parse('missing:_icon_resource') }, { fileKind: this.getFileKind(node), title, hideIcon: !!iconUrl, fileDecorations, extraClasses: ['custom-view-tree-node-item-resourceLabel'], matches }); + templateData.resourceLabel.setResource({ name: label, description, resource: resource ? resource : URI.parse('missing:_icon_resource') }, { fileKind: this.getFileKind(node), title, hideIcon: !!iconUrl, fileDecorations, extraClasses: ['custom-view-tree-node-item-resourceLabel'], matches }); } else { - templateData.resourceLabel.setLabel({ name: label, description }, { title, hideIcon: true, extraClasses: ['custom-view-tree-node-item-resourceLabel'], matches }); + templateData.resourceLabel.setResource({ name: label, description }, { title, hideIcon: true, extraClasses: ['custom-view-tree-node-item-resourceLabel'], matches }); } templateData.icon.style.backgroundImage = iconUrl ? `url('${iconUrl.toString(true)}')` : ''; diff --git a/src/vs/workbench/browser/parts/views/panelViewlet.ts b/src/vs/workbench/browser/parts/views/panelViewlet.ts index 068e36fbb85..6e20761985a 100644 --- a/src/vs/workbench/browser/parts/views/panelViewlet.ts +++ b/src/vs/workbench/browser/parts/views/panelViewlet.ts @@ -52,10 +52,13 @@ export abstract class ViewletPanel extends Panel implements IView { private _onDidBlur = new Emitter(); readonly onDidBlur: Event = this._onDidBlur.event; + private _onDidChangeBodyVisibility = new Emitter(); + readonly onDidChangeBodyVisibility: Event = this._onDidChangeBodyVisibility.event; + protected _onDidChangeTitleArea = new Emitter(); readonly onDidChangeTitleArea: Event = this._onDidChangeTitleArea.event; - private _isVisible: boolean = true; + private _isVisible: boolean = false; readonly id: string; readonly title: string; @@ -74,11 +77,17 @@ export abstract class ViewletPanel extends Panel implements IView { this.id = options.id; this.title = options.title; this.actionRunner = options.actionRunner; + + this.disposables.push(this._onDidFocus, this._onDidBlur, this._onDidChangeBodyVisibility, this._onDidChangeTitleArea); } setVisible(visible: boolean): void { if (this._isVisible !== visible) { this._isVisible = visible; + + if (this.isExpanded()) { + this._onDidChangeBodyVisibility.fire(visible); + } } } @@ -86,6 +95,19 @@ export abstract class ViewletPanel extends Panel implements IView { return this._isVisible; } + isBodyVisible(): boolean { + return this._isVisible && this.isExpanded(); + } + + setExpanded(expanded: boolean): boolean { + const changed = super.setExpanded(expanded); + if (changed) { + this._onDidChangeBodyVisibility.fire(expanded); + } + + return changed; + } + render(): void { super.render(); diff --git a/src/vs/workbench/browser/parts/views/viewsViewlet.ts b/src/vs/workbench/browser/parts/views/viewsViewlet.ts index cd256a1288f..9c2acd531ea 100644 --- a/src/vs/workbench/browser/parts/views/viewsViewlet.ts +++ b/src/vs/workbench/browser/parts/views/viewsViewlet.ts @@ -34,11 +34,13 @@ export abstract class TreeViewsViewletPanel extends ViewletPanel { protected tree: WorkbenchTree; - setExpanded(expanded: boolean): void { - if (this.isExpanded() !== expanded) { + setExpanded(expanded: boolean): boolean { + const changed = super.setExpanded(expanded); + if (changed) { this.updateTreeVisibility(this.tree, expanded); - super.setExpanded(expanded); } + + return changed; } setVisible(visible: boolean): void { diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index d6756663f48..379098ff9f0 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -18,6 +18,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { IKeybindings } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IAction } from 'vs/base/common/actions'; import { IMarkdownString } from 'vs/base/common/htmlContent'; +import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; export const TEST_VIEW_CONTAINER_ID = 'workbench.view.extension.test'; @@ -44,7 +45,7 @@ export interface IViewContainersRegistry { * * @returns the registered ViewContainer. */ - registerViewContainer(id: string, extensionId?: string): ViewContainer; + registerViewContainer(id: string, extensionId?: CanonicalExtensionIdentifier): ViewContainer; /** * Returns the view container with given id. @@ -56,12 +57,12 @@ export interface IViewContainersRegistry { } export class ViewContainer { - protected constructor(readonly id: string, readonly extensionId: string) { } + protected constructor(readonly id: string, readonly extensionId: CanonicalExtensionIdentifier) { } } class ViewContainersRegistryImpl implements IViewContainersRegistry { - private readonly _onDidRegister: Emitter = new Emitter(); + private readonly _onDidRegister = new Emitter(); readonly onDidRegister: Event = this._onDidRegister.event; private viewContainers: Map = new Map(); @@ -70,7 +71,7 @@ class ViewContainersRegistryImpl implements IViewContainersRegistry { return values(this.viewContainers); } - registerViewContainer(id: string, extensionId: string): ViewContainer { + registerViewContainer(id: string, extensionId: CanonicalExtensionIdentifier): ViewContainer { if (!this.viewContainers.has(id)) { const viewContainer = new class extends ViewContainer { constructor() { diff --git a/src/vs/workbench/electron-browser/shell.ts b/src/vs/workbench/electron-browser/shell.ts index 9adee7e1d1a..3e48bf5772b 100644 --- a/src/vs/workbench/electron-browser/shell.ts +++ b/src/vs/workbench/electron-browser/shell.ts @@ -394,7 +394,7 @@ export class WorkbenchShell extends Disposable { serviceCollection.set(IWorkspaceContextService, this.contextService); serviceCollection.set(IConfigurationService, this.configurationService); serviceCollection.set(IEnvironmentService, this.environmentService); - serviceCollection.set(ILabelService, new SyncDescriptor(LabelService)); + serviceCollection.set(ILabelService, new SyncDescriptor(LabelService, undefined, true)); serviceCollection.set(ILogService, this._register(this.logService)); serviceCollection.set(IStorageService, this.storageService); @@ -420,7 +420,7 @@ export class WorkbenchShell extends Disposable { }); // Hash - serviceCollection.set(IHashService, new SyncDescriptor(HashService)); + serviceCollection.set(IHashService, new SyncDescriptor(HashService, undefined, true)); // Telemetry if (!this.environmentService.isExtensionDevelopment && !this.environmentService.args['disable-telemetry'] && !!product.enableTelemetry) { @@ -454,9 +454,9 @@ export class WorkbenchShell extends Disposable { serviceCollection.set(ILifecycleService, lifecycleService); this.lifecycleService = lifecycleService; - serviceCollection.set(IRequestService, new SyncDescriptor(RequestService)); - serviceCollection.set(IDownloadService, new SyncDescriptor(DownloadService)); - serviceCollection.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService)); + serviceCollection.set(IRequestService, new SyncDescriptor(RequestService, undefined, true)); + serviceCollection.set(IDownloadService, new SyncDescriptor(DownloadService, undefined, true)); + serviceCollection.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService, undefined, true)); const remoteAuthorityResolverService = new RemoteAuthorityResolverService(); serviceCollection.set(IRemoteAuthorityResolverService, remoteAuthorityResolverService); @@ -484,9 +484,9 @@ export class WorkbenchShell extends Disposable { this.themeService = instantiationService.createInstance(WorkbenchThemeService, document.body); serviceCollection.set(IWorkbenchThemeService, this.themeService); - serviceCollection.set(ICommandService, new SyncDescriptor(CommandService)); + serviceCollection.set(ICommandService, new SyncDescriptor(CommandService, undefined, true)); - serviceCollection.set(IMarkerService, new SyncDescriptor(MarkerService)); + serviceCollection.set(IMarkerService, new SyncDescriptor(MarkerService, undefined, true)); serviceCollection.set(IModeService, new SyncDescriptor(WorkbenchModeServiceImpl)); @@ -494,11 +494,11 @@ export class WorkbenchShell extends Disposable { serviceCollection.set(ITextResourcePropertiesService, new SyncDescriptor(TextResourcePropertiesService)); - serviceCollection.set(IModelService, new SyncDescriptor(ModelServiceImpl)); + serviceCollection.set(IModelService, new SyncDescriptor(ModelServiceImpl, undefined, true)); serviceCollection.set(IEditorWorkerService, new SyncDescriptor(EditorWorkerServiceImpl)); - serviceCollection.set(IUntitledEditorService, new SyncDescriptor(UntitledEditorService)); + serviceCollection.set(IUntitledEditorService, new SyncDescriptor(UntitledEditorService, undefined, true)); serviceCollection.set(ITextMateService, new SyncDescriptor(TextMateService)); @@ -510,7 +510,7 @@ export class WorkbenchShell extends Disposable { serviceCollection.set(ICodeEditorService, new SyncDescriptor(CodeEditorService)); - serviceCollection.set(IOpenerService, new SyncDescriptor(OpenerService)); + serviceCollection.set(IOpenerService, new SyncDescriptor(OpenerService, undefined, true)); serviceCollection.set(IIntegrityService, new SyncDescriptor(IntegrityServiceImpl)); diff --git a/src/vs/workbench/electron-browser/window.ts b/src/vs/workbench/electron-browser/window.ts index c3baf5df98e..bf90bb2f253 100644 --- a/src/vs/workbench/electron-browser/window.ts +++ b/src/vs/workbench/electron-browser/window.ts @@ -15,7 +15,7 @@ import { toResource, IUntitledResourceInput } from 'vs/workbench/common/editor'; import { IEditorService, IResourceEditor } from 'vs/workbench/services/editor/common/editorService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; -import { IWindowsService, IWindowService, IWindowSettings, IOpenFileRequest, IWindowsConfiguration, IAddFoldersRequest, IRunActionInWindowRequest, IPathData } from 'vs/platform/windows/common/windows'; +import { IWindowsService, IWindowService, IWindowSettings, IOpenFileRequest, IWindowsConfiguration, IAddFoldersRequest, IRunActionInWindowRequest, IPathData, IRunKeybindingInWindowRequest } from 'vs/platform/windows/common/windows'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { ITitleService } from 'vs/workbench/services/title/common/titleService'; import { IWorkbenchThemeService, VS_HC_THEME } from 'vs/workbench/services/themes/common/workbenchThemeService'; @@ -38,6 +38,7 @@ import { AccessibilitySupport, isRootUser, isWindows, isMacintosh } from 'vs/bas import product from 'vs/platform/node/product'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { EditorServiceImpl } from 'vs/workbench/browser/parts/editor/editor'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; const TextInputActions: IAction[] = [ new Action('undo', nls.localize('undo', "Undo"), null, true, () => document.execCommand('undo') && Promise.resolve(true)), @@ -71,6 +72,7 @@ export class ElectronWindow extends Themable { @IWorkbenchThemeService protected themeService: IWorkbenchThemeService, @INotificationService private notificationService: INotificationService, @ICommandService private commandService: ICommandService, + @IKeybindingService private keybindingService: IKeybindingService, @IContextMenuService private contextMenuService: IContextMenuService, @ITelemetryService private telemetryService: ITelemetryService, @IWorkspaceEditingService private workspaceEditingService: IWorkspaceEditingService, @@ -133,6 +135,11 @@ export class ElectronWindow extends Themable { }); }); + // Support runKeybinding event + ipc.on('vscode:runKeybinding', (event: any, request: IRunKeybindingInWindowRequest) => { + this.keybindingService.dispatchByUserSettingsLabel(request.userSettingsLabel, document.activeElement); + }); + // Error reporting from main ipc.on('vscode:reportError', (event: any, error: string) => { if (error) { diff --git a/src/vs/workbench/node/extensionHostMain.ts b/src/vs/workbench/node/extensionHostMain.ts index 54999cd59c5..af73f59ce6b 100644 --- a/src/vs/workbench/node/extensionHostMain.ts +++ b/src/vs/workbench/node/extensionHostMain.ts @@ -107,7 +107,7 @@ export class ExtensionHostMain { const data = errors.transformErrorForSerialization(err); const extension = extensionErrors.get(err); if (extension) { - mainThreadExtensions.$onExtensionRuntimeError(extension.id, data); + mainThreadExtensions.$onExtensionRuntimeError(extension.identifier, data); } else { mainThreadErrors.$onUnexpectedError(data); } diff --git a/src/vs/workbench/node/proxyResolver.ts b/src/vs/workbench/node/proxyResolver.ts index 72534ea9ad5..737923ec8b8 100644 --- a/src/vs/workbench/node/proxyResolver.ts +++ b/src/vs/workbench/node/proxyResolver.ts @@ -206,6 +206,7 @@ function createPatchedModules(extHostConfiguration: ExtHostConfiguration, agent: } function patches(originals: typeof http | typeof https, agent: http.Agent, setting: { config: string; }, onRequest: boolean) { + const defaultPort = originals === https ? 443 : 80; return { get: patch(originals.get), @@ -244,6 +245,7 @@ function patches(originals: typeof http | typeof https, agent: http.Agent, setti options = { ...options }; } options.agent = agent; + options.defaultPort = defaultPort; // Lets Node's http module omit the port, if it is the default port, in the Host header. (https://github.com/Microsoft/vscode/issues/65118) return original(options, callback); } diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.ts b/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.ts index 8fa373d027d..df08685c640 100644 --- a/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.ts +++ b/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.ts @@ -261,11 +261,16 @@ class InspectTMScopesWidget extends Disposable implements IContentWidget { let metadata = this._decodeMetadata(data.tokens2[(token2Index << 1) + 1]); result += ``; - result += ``; - result += ``; - result += ``; - result += ``; - result += ``; + result += ``; + result += ``; + result += ``; + result += ``; + result += ``; + if (metadata.background.isOpaque() && metadata.foreground.isOpaque()) { + result += ``; + } else { + result += ''; + } result += ``; let theme = this._themeService.getColorTheme(); diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.ts b/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.ts index f79615775aa..9a696853577 100644 --- a/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.ts +++ b/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.ts @@ -41,7 +41,7 @@ interface IWordWrapState { /** * Store (in memory) the word wrap state for a particular model. */ -function writeTransientState(model: ITextModel, state: IWordWrapTransientState | null, codeEditorService: ICodeEditorService): void { +export function writeTransientState(model: ITextModel, state: IWordWrapTransientState | null, codeEditorService: ICodeEditorService): void { codeEditorService.setTransientModelProperty(model, transientWordWrapState, state); } diff --git a/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts b/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts index 365ec21bca4..1b2a173272d 100644 --- a/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts +++ b/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts @@ -429,7 +429,9 @@ export class ReviewZoneWidget extends ZoneWidget { submitdraftButton.onDidClick(async () => { try { let lineNumber = this._commentGlyph.getPosition().position.lineNumber; - await this.createComment(lineNumber); + if (this._commentEditor.getValue()) { + await this.createComment(lineNumber); + } await this.commentService.finishDraft(this._owner, this.editor.getModel().uri); } catch (e) { this.handleError(e); diff --git a/src/vs/workbench/parts/comments/electron-browser/commentsPanel.ts b/src/vs/workbench/parts/comments/electron-browser/commentsPanel.ts index 5745d58e4f5..c1b0f53f877 100644 --- a/src/vs/workbench/parts/comments/electron-browser/commentsPanel.ts +++ b/src/vs/workbench/parts/comments/electron-browser/commentsPanel.ts @@ -23,11 +23,13 @@ import { ICommandService } from 'vs/platform/commands/common/commands'; import { textLinkForeground, textLinkActiveForeground, focusBorder } from 'vs/platform/theme/common/colorRegistry'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IStorageService } from 'vs/platform/storage/common/storage'; +import { ResourceLabels } from 'vs/workbench/browser/labels'; export const COMMENTS_PANEL_ID = 'workbench.panel.comments'; export const COMMENTS_PANEL_TITLE = 'Comments'; export class CommentsPanel extends Panel { + private treeLabels: ResourceLabels; private tree: WorkbenchTree; private treeContainer: HTMLElement; private messageBoxContainer: HTMLElement; @@ -60,14 +62,18 @@ export class CommentsPanel extends Panel { this.createTree(); this.createMessageBox(container); - this.commentService.onDidSetAllCommentThreads(this.onAllCommentsChanged, this); - this.commentService.onDidUpdateCommentThreads(this.onCommentsUpdated, this); + this._register(this.commentService.onDidSetAllCommentThreads(this.onAllCommentsChanged, this)); + this._register(this.commentService.onDidUpdateCommentThreads(this.onCommentsUpdated, this)); const styleElement = dom.createStyleSheet(parent); this.applyStyles(styleElement); - this.themeService.onThemeChange(_ => { - this.applyStyles(styleElement); - }); + this._register(this.themeService.onThemeChange(_ => this.applyStyles(styleElement))); + + this._register(this.onDidChangeVisibility(visible => { + if (visible) { + this.refresh(); + } + })); this.render(); } @@ -129,9 +135,11 @@ export class CommentsPanel extends Panel { } private createTree(): void { - this.tree = this.instantiationService.createInstance(WorkbenchTree, this.treeContainer, { + this.treeLabels = this._register(this.instantiationService.createInstance(ResourceLabels, this)); + + this.tree = this._register(this.instantiationService.createInstance(WorkbenchTree, this.treeContainer, { dataSource: new CommentsDataSource(), - renderer: new CommentsModelRenderer(this.instantiationService, this.openerService), + renderer: new CommentsModelRenderer(this.treeLabels, this.openerService), accessibilityProvider: new DefaultAccessibilityProvider, controller: new DefaultController(), dnd: new DefaultDragAndDrop(), @@ -139,7 +147,7 @@ export class CommentsPanel extends Panel { }, { twistiePixels: 20, ariaLabel: COMMENTS_PANEL_TITLE - }); + })); const commentsNavigator = this._register(new TreeResourceNavigator(this.tree, { openOnFocus: true })); this._register(Event.debounce(commentsNavigator.openResource, (last, event) => event, 100, true)(options => { @@ -172,7 +180,6 @@ export class CommentsPanel extends Panel { return true; } - const threadToReveal = element instanceof ResourceWithCommentThreads ? element.commentThreads[0].threadId : element.threadId; const commentToReveal = element instanceof ResourceWithCommentThreads ? element.commentThreads[0].comment : element.comment; @@ -229,16 +236,6 @@ export class CommentsPanel extends Panel { return true; } - public setVisible(visible: boolean): void { - const wasVisible = this.isVisible(); - super.setVisible(visible); - if (this.isVisible()) { - if (!wasVisible) { - this.refresh(); - } - } - } - private refresh(): void { if (this.isVisible()) { this.collapseAllAction.enabled = this.commentsModel.hasCommentThreads(); diff --git a/src/vs/workbench/parts/comments/electron-browser/commentsTreeViewer.ts b/src/vs/workbench/parts/comments/electron-browser/commentsTreeViewer.ts index 8bf50628901..473341920a2 100644 --- a/src/vs/workbench/parts/comments/electron-browser/commentsTreeViewer.ts +++ b/src/vs/workbench/parts/comments/electron-browser/commentsTreeViewer.ts @@ -10,9 +10,8 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { Disposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { IDataSource, IFilter, IRenderer as ITreeRenderer, ITree } from 'vs/base/parts/tree/browser/tree'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { FileLabel } from 'vs/workbench/browser/labels'; +import { IResourceLabel, ResourceLabels } from 'vs/workbench/browser/labels'; import { CommentNode, CommentsModel, ResourceWithCommentThreads } from 'vs/workbench/parts/comments/common/commentModel'; export class CommentsDataSource implements IDataSource { @@ -56,7 +55,7 @@ export class CommentsDataSource implements IDataSource { } interface IResourceTemplateData { - resourceLabel: FileLabel; + resourceLabel: IResourceLabel; } interface ICommentThreadTemplateData { @@ -70,9 +69,8 @@ export class CommentsModelRenderer implements ITreeRenderer { private static RESOURCE_ID = 'resource-with-comments'; private static COMMENT_ID = 'comment-node'; - constructor( - @IInstantiationService private instantiationService: IInstantiationService, + private labels: ResourceLabels, @IOpenerService private openerService: IOpenerService ) { } @@ -124,7 +122,7 @@ export class CommentsModelRenderer implements ITreeRenderer { private renderResourceTemplate(container: HTMLElement): IResourceTemplateData { const data = Object.create(null); const labelContainer = dom.append(container, dom.$('.resource-container')); - data.resourceLabel = this.instantiationService.createInstance(FileLabel, labelContainer, {}); + data.resourceLabel = this.labels.create(labelContainer); return data; } diff --git a/src/vs/workbench/parts/debug/browser/breakpointsView.ts b/src/vs/workbench/parts/debug/browser/breakpointsView.ts index 44614a05825..723fa3a7d5b 100644 --- a/src/vs/workbench/parts/debug/browser/breakpointsView.ts +++ b/src/vs/workbench/parts/debug/browser/breakpointsView.ts @@ -119,6 +119,12 @@ export class BreakpointsView extends ViewletPanel { })); this.list.splice(0, this.list.length, this.elements); + + this.disposables.push(this.onDidChangeBodyVisibility(visible => { + if (visible && this.needsRefresh) { + this.onBreakpointsChange(); + } + })); } public focus(): void { @@ -191,22 +197,8 @@ export class BreakpointsView extends ViewletPanel { ]; } - public setExpanded(expanded: boolean): void { - super.setExpanded(expanded); - if (expanded && this.needsRefresh) { - this.onBreakpointsChange(); - } - } - - public setVisible(visible: boolean): void { - super.setVisible(visible); - if (visible && this.needsRefresh) { - this.onBreakpointsChange(); - } - } - private onBreakpointsChange(): void { - if (this.isExpanded() && this.isVisible()) { + if (this.isBodyVisible()) { this.minimumBodySize = this.getExpandedBodySize(); if (this.maximumBodySize < Number.POSITIVE_INFINITY) { this.maximumBodySize = this.minimumBodySize; diff --git a/src/vs/workbench/parts/debug/browser/loadedScriptsView.ts b/src/vs/workbench/parts/debug/browser/loadedScriptsView.ts index 7d9a3c90613..3c9ca928d17 100644 --- a/src/vs/workbench/parts/debug/browser/loadedScriptsView.ts +++ b/src/vs/workbench/parts/debug/browser/loadedScriptsView.ts @@ -23,7 +23,7 @@ import { isWindows } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; import { ltrim } from 'vs/base/common/strings'; import { RunOnceScheduler } from 'vs/base/common/async'; -import { ResourceLabel, IResourceLabel, IResourceLabelOptions } from 'vs/workbench/browser/labels'; +import { ResourceLabels, IResourceLabelProps, IResourceLabelOptions, IResourceLabel, IResourceLabelsContainer } from 'vs/workbench/browser/labels'; import { FileKind } from 'vs/platform/files/common/files'; import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { ITreeRenderer, ITreeNode, ITreeFilter, TreeVisibility, TreeFilterResult, IAsyncDataSource } from 'vs/base/browser/ui/tree/tree'; @@ -32,6 +32,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { WorkbenchAsyncDataTree, IListService, TreeResourceNavigator2 } from 'vs/platform/list/browser/listService'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { DebugContentProvider } from 'vs/workbench/parts/debug/browser/debugContentProvider'; +import { dispose } from 'vs/base/common/lifecycle'; const SMART = true; @@ -363,6 +364,7 @@ export class LoadedScriptsView extends ViewletPanel { private treeContainer: HTMLElement; private loadedScriptsItemType: IContextKey; private tree: WorkbenchAsyncDataTree; + private treeLabels: ResourceLabels; private changeScheduler: RunOnceScheduler; private treeNeedsRefreshOnVisible: boolean; private filter: LoadedScriptsFilter; @@ -395,10 +397,11 @@ export class LoadedScriptsView extends ViewletPanel { const root = new RootTreeItem(this.debugService.getModel(), this.environmentService, this.contextService); + this.treeLabels = this.instantiationService.createInstance(ResourceLabels, { onDidChangeVisibility: this.onDidChangeBodyVisibility } as IResourceLabelsContainer); + this.disposables.push(this.treeLabels); + this.tree = new WorkbenchAsyncDataTree(this.treeContainer, new LoadedScriptsDelegate(), - [ - this.instantiationService.createInstance(LoadedScriptsRenderer) - ], + [new LoadedScriptsRenderer(this.treeLabels)], new LoadedScriptsDataSource(), { identityProvider: { @@ -453,7 +456,7 @@ export class LoadedScriptsView extends ViewletPanel { case 'changed': sessionRoot = root.add(session); sessionRoot.addPath(event.source); - if (this.isVisible) { + if (this.isBodyVisible()) { this.changeScheduler.schedule(); } else { this.treeNeedsRefreshOnVisible = true; @@ -465,7 +468,7 @@ export class LoadedScriptsView extends ViewletPanel { case 'removed': sessionRoot = root.find(session); if (sessionRoot && sessionRoot.removePath(event.source)) { - if (this.isVisible) { + if (this.isBodyVisible()) { this.changeScheduler.schedule(); } else { this.treeNeedsRefreshOnVisible = true; @@ -489,36 +492,21 @@ export class LoadedScriptsView extends ViewletPanel { })); this.changeScheduler.schedule(0); + + this.disposables.push(this.onDidChangeBodyVisibility(visible => { + if (visible && this.treeNeedsRefreshOnVisible) { + this.changeScheduler.schedule(); + } + })); } layoutBody(size: number): void { this.tree.layout(size); } - setExpanded(expanded: boolean): void { - super.setExpanded(expanded); - if (expanded && this.treeNeedsRefreshOnVisible) { - this.changeScheduler.schedule(); - } - } - - setVisible(visible: boolean): void { - super.setVisible(visible); - if (visible && this.treeNeedsRefreshOnVisible) { - this.changeScheduler.schedule(); - } - } - - /* - private tryToExpand(element: LoadedScriptsItem): void { - try { - this.tree.expand(element); - } catch (e) { } - } - */ - dispose(): void { - this.tree = undefined; + this.tree = dispose(this.tree); + this.treeLabels = dispose(this.treeLabels); super.dispose(); } } @@ -549,7 +537,7 @@ class LoadedScriptsDataSource implements IAsyncDataSource { @@ -557,7 +545,7 @@ class LoadedScriptsRenderer implements ITreeRenderer { - if (!this.isVisible()) { + if (!this.isBodyVisible()) { this.needsRefresh = true; return; } @@ -202,7 +202,7 @@ export class CallStackView extends ViewletPanel { if (this.ignoreFocusStackFrameEvent) { return; } - if (!this.isVisible) { + if (!this.isBodyVisible()) { this.needsRefresh = true; return; } @@ -215,6 +215,12 @@ export class CallStackView extends ViewletPanel { if (this.debugService.state === State.Stopped) { this.onCallStackChangeScheduler.schedule(0); } + + this.disposables.push(this.onDidChangeBodyVisibility(visible => { + if (visible && this.needsRefresh) { + this.onCallStackChangeScheduler.schedule(); + } + })); } layoutBody(size: number): void { @@ -256,13 +262,6 @@ export class CallStackView extends ViewletPanel { } } - setVisible(visible: boolean): void { - super.setVisible(visible); - if (visible && this.needsRefresh) { - this.onCallStackChangeScheduler.schedule(); - } - } - private onContextMenu(e: ITreeContextMenuEvent): void { const actions: IAction[] = []; const element = e.element; diff --git a/src/vs/workbench/parts/debug/electron-browser/debugService.ts b/src/vs/workbench/parts/debug/electron-browser/debugService.ts index 886396ea8e6..562c430b66d 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugService.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugService.ts @@ -1030,7 +1030,7 @@ export class DebugService implements IDebugService { breakpointCount: this.model.getBreakpoints().length, exceptionBreakpoints: this.model.getExceptionBreakpoints(), watchExpressionsCount: this.model.getWatchExpressions().length, - extensionName: extension.id, + extensionName: extension.identifier.value, isBuiltin: extension.isBuiltin, launchJsonExists: root && !!this.configurationService.getValue('launch', { resource: root.uri }) }); diff --git a/src/vs/workbench/parts/debug/electron-browser/repl.ts b/src/vs/workbench/parts/debug/electron-browser/repl.ts index 2d84efb6581..da6c7fd507c 100644 --- a/src/vs/workbench/parts/debug/electron-browser/repl.ts +++ b/src/vs/workbench/parts/debug/electron-browser/repl.ts @@ -149,18 +149,16 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati this.updateInputDecoration(); } })); - } - - setVisible(visible: boolean): void { - super.setVisible(visible); - if (!visible) { - dispose(this.model); - } else { - this.model = this.modelService.createModel('', null, uri.parse(`${DEBUG_SCHEME}:replinput`), true); - this.replInput.setModel(this.model); - this.updateInputDecoration(); - this.refreshReplElements(true); - } + this._register(this.onDidChangeVisibility(visible => { + if (!visible) { + dispose(this.model); + } else { + this.model = this.modelService.createModel('', null, uri.parse(`${DEBUG_SCHEME}:replinput`), true); + this.replInput.setModel(this.model); + this.updateInputDecoration(); + this.refreshReplElements(true); + } + })); } get isReadonly(): boolean { diff --git a/src/vs/workbench/parts/debug/electron-browser/variablesView.ts b/src/vs/workbench/parts/debug/electron-browser/variablesView.ts index 28f567f0c9f..312eb618d1f 100644 --- a/src/vs/workbench/parts/debug/electron-browser/variablesView.ts +++ b/src/vs/workbench/parts/debug/electron-browser/variablesView.ts @@ -92,7 +92,7 @@ export class VariablesView extends ViewletPanel { this.tree.refresh(); this.disposables.push(this.debugService.getViewModel().onDidFocusStackFrame(sf => { - if (!this.isVisible() || !this.isExpanded()) { + if (!this.isBodyVisible()) { this.needsRefresh = true; return; } @@ -105,26 +105,18 @@ export class VariablesView extends ViewletPanel { this.disposables.push(variableSetEmitter.event(() => this.tree.refresh())); this.disposables.push(this.tree.onMouseDblClick(e => this.onMouseDblClick(e))); this.disposables.push(this.tree.onContextMenu(e => this.onContextMenu(e))); + + this.disposables.push(this.onDidChangeBodyVisibility(visible => { + if (visible && this.needsRefresh) { + this.onFocusStackFrameScheduler.schedule(); + } + })); } layoutBody(size: number): void { this.tree.layout(size); } - setExpanded(expanded: boolean): void { - super.setExpanded(expanded); - if (expanded && this.needsRefresh) { - this.onFocusStackFrameScheduler.schedule(); - } - } - - setVisible(visible: boolean): void { - super.setVisible(visible); - if (visible && this.needsRefresh) { - this.onFocusStackFrameScheduler.schedule(); - } - } - private onMouseDblClick(e: ITreeMouseEvent): void { const session = this.debugService.getViewModel().focusedSession; if (e.element instanceof Variable && session.capabilities.supportsSetVariable) { diff --git a/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts b/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts index feaa138016e..0512fa8bb90 100644 --- a/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts +++ b/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts @@ -81,14 +81,14 @@ export class WatchExpressionsView extends ViewletPanel { this.disposables.push(this.tree.onContextMenu(e => this.onContextMenu(e))); this.disposables.push(this.tree.onMouseDblClick(e => this.onMouseDblClick(e))); this.disposables.push(this.debugService.getModel().onDidChangeWatchExpressions(we => { - if (!this.isExpanded() || !this.isVisible()) { + if (!this.isBodyVisible()) { this.needsRefresh = true; } else { this.tree.refresh(); } })); this.disposables.push(this.debugService.getViewModel().onDidFocusStackFrame(() => { - if (!this.isExpanded() || !this.isVisible()) { + if (!this.isBodyVisible()) { this.needsRefresh = true; return; } @@ -98,26 +98,18 @@ export class WatchExpressionsView extends ViewletPanel { } })); this.disposables.push(variableSetEmitter.event(() => this.tree.refresh())); + + this.disposables.push(this.onDidChangeBodyVisibility(visible => { + if (visible && this.needsRefresh) { + this.onWatchExpressionsUpdatedScheduler.schedule(); + } + })); } layoutBody(size: number): void { this.tree.layout(size); } - setExpanded(expanded: boolean): void { - super.setExpanded(expanded); - if (expanded && this.needsRefresh) { - this.onWatchExpressionsUpdatedScheduler.schedule(); - } - } - - setVisible(visible: boolean): void { - super.setVisible(visible); - if (visible && this.needsRefresh) { - this.onWatchExpressionsUpdatedScheduler.schedule(); - } - } - private onMouseDblClick(e: ITreeMouseEvent): void { if ((e.browserEvent.target as HTMLElement).className.indexOf('twistie') >= 0) { // Ignore double click events on twistie diff --git a/src/vs/workbench/parts/debug/node/debugAdapter.ts b/src/vs/workbench/parts/debug/node/debugAdapter.ts index 27779eb9e95..d82da6c3386 100644 --- a/src/vs/workbench/parts/debug/node/debugAdapter.ts +++ b/src/vs/workbench/parts/debug/node/debugAdapter.ts @@ -332,13 +332,17 @@ export class ExecutableDebugAdapter extends StreamDebugAdapter { "Cannot determine executable for debug adapter '{0}'.", this.debugType))); } + let env = objects.mixin({}, process.env); + if (this.adapterExecutable.options && this.adapterExecutable.options.env) { + env = objects.mixin(env, this.adapterExecutable.options.env); + } + delete env.VSCODE_PREVENT_FOREIGN_INSPECT; + if (this.adapterExecutable.command === 'node') { if (Array.isArray(this.adapterExecutable.args) && this.adapterExecutable.args.length > 0) { const isElectron = !!process.env['ELECTRON_RUN_AS_NODE'] || !!process.versions['electron']; const options: cp.ForkOptions = { - env: this.adapterExecutable.options && this.adapterExecutable.options.env - ? objects.mixin(objects.mixin({}, process.env), this.adapterExecutable.options.env) - : process.env, + env: env, execArgv: isElectron ? ['-e', 'delete process.env.ELECTRON_RUN_AS_NODE;require(process.argv[1])'] : [], silent: true }; @@ -356,9 +360,7 @@ export class ExecutableDebugAdapter extends StreamDebugAdapter { } } else { const options: cp.SpawnOptions = { - env: this.adapterExecutable.options && this.adapterExecutable.options.env - ? objects.mixin(objects.mixin({}, process.env), this.adapterExecutable.options.env) - : process.env + env: env }; if (this.adapterExecutable.options && this.adapterExecutable.options.cwd) { options.cwd = this.adapterExecutable.options.cwd; diff --git a/src/vs/workbench/parts/debug/node/debugger.ts b/src/vs/workbench/parts/debug/node/debugger.ts index 5384fa697b9..8fccfd8b79d 100644 --- a/src/vs/workbench/parts/debug/node/debugger.ts +++ b/src/vs/workbench/parts/debug/node/debugger.ts @@ -42,39 +42,37 @@ export class Debugger implements IDebugger { } public createDebugAdapter(session: IDebugSession, outputService: IOutputService): Promise { - return new Promise((resolve, reject) => { - this.configurationManager.activateDebuggers('onDebugAdapterProtocolTracker', this.type).then(_ => { - resolve(this._createDebugAdapter(session, outputService)); - }, reject); + return this.configurationManager.activateDebuggers('onDebugAdapterProtocolTracker', this.type).then(_ => { + if (this.inExtHost()) { + const da = this.configurationManager.createDebugAdapter(session); + if (da) { + return Promise.resolve(da); + } + throw new Error(nls.localize('cannot.find.da', "Cannot find debug adapter for type '{0}'.", this.type)); + } else { + return this.getAdapterDescriptor(session).then(adapterDescriptor => { + switch (adapterDescriptor.type) { + case 'executable': + return new ExecutableDebugAdapter(adapterDescriptor, this.type, outputService); + case 'server': + return new SocketDebugAdapter(adapterDescriptor); + case 'implementation': + // TODO@AW: this.inExtHost() should now return true + return Promise.resolve(this.configurationManager.createDebugAdapter(session)); + default: + throw new Error('unknown descriptor type'); + } + }).catch(err => { + if (err && err.message) { + throw new Error(nls.localize('cannot.create.da.with.err', "Cannot create debug adapter ({0}).", err.message)); + } else { + throw new Error(nls.localize('cannot.create.da', "Cannot create debug adapter.")); + } + }); + } }); } - private _createDebugAdapter(session: IDebugSession, outputService: IOutputService): Promise { - if (this.inExtHost()) { - return Promise.resolve(this.configurationManager.createDebugAdapter(session)); - } else { - return this.getAdapterDescriptor(session).then(adapterDescriptor => { - switch (adapterDescriptor.type) { - case 'executable': - return new ExecutableDebugAdapter(adapterDescriptor, this.type, outputService); - case 'server': - return new SocketDebugAdapter(adapterDescriptor); - case 'implementation': - // TODO@AW: this.inExtHost() should now return true - return Promise.resolve(this.configurationManager.createDebugAdapter(session)); - default: - throw new Error('unknown type'); - } - }).catch(err => { - if (err && err.message) { - throw new Error(nls.localize('cannot.create.da.with.err', "Cannot create debug adapter ({0}).", err.message)); - } else { - throw new Error(nls.localize('cannot.create.da', "Cannot create debug adapter.")); - } - }); - } - } - private getAdapterDescriptor(session: IDebugSession): Promise { // a "debugServer" attribute in the launch config takes precedence diff --git a/src/vs/workbench/parts/debug/test/node/debugger.test.ts b/src/vs/workbench/parts/debug/test/node/debugger.test.ts index 5dc901b29c0..d4f9160f8dd 100644 --- a/src/vs/workbench/parts/debug/test/node/debugger.test.ts +++ b/src/vs/workbench/parts/debug/test/node/debugger.test.ts @@ -12,6 +12,7 @@ import { TestConfigurationService } from 'vs/platform/configuration/test/common/ import { URI } from 'vs/base/common/uri'; import { ExecutableDebugAdapter } from 'vs/workbench/parts/debug/node/debugAdapter'; import { TestTextResourcePropertiesService } from 'vs/workbench/test/workbenchTestServices'; +import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; suite('Debug - Debugger', () => { @@ -49,6 +50,7 @@ suite('Debug - Debugger', () => { const extensionDescriptor0 = { id: 'adapter', + identifier: new CanonicalExtensionIdentifier('adapter'), name: 'myAdapter', version: '1.0.0', publisher: 'vscode', @@ -65,6 +67,7 @@ suite('Debug - Debugger', () => { const extensionDescriptor1 = { id: 'extension1', + identifier: new CanonicalExtensionIdentifier('extension1'), name: 'extension1', version: '1.0.0', publisher: 'vscode', @@ -87,6 +90,7 @@ suite('Debug - Debugger', () => { const extensionDescriptor2 = { id: 'extension2', + identifier: new CanonicalExtensionIdentifier('extension2'), name: 'extension2', version: '1.0.0', publisher: 'vscode', diff --git a/src/vs/workbench/parts/experiments/electron-browser/experiments.contribution.ts b/src/vs/workbench/parts/experiments/electron-browser/experiments.contribution.ts index 651f64522ed..db03272bfcb 100644 --- a/src/vs/workbench/parts/experiments/electron-browser/experiments.contribution.ts +++ b/src/vs/workbench/parts/experiments/electron-browser/experiments.contribution.ts @@ -10,6 +10,6 @@ import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } fr import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { ExperimentalPrompts } from 'vs/workbench/parts/experiments/electron-browser/experimentalPrompt'; -registerSingleton(IExperimentService, ExperimentService); +registerSingleton(IExperimentService, ExperimentService, true); Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(ExperimentalPrompts, LifecyclePhase.Eventually); diff --git a/src/vs/workbench/parts/experiments/node/experimentService.ts b/src/vs/workbench/parts/experiments/node/experimentService.ts index 8ba937b43a1..b3761fc0344 100644 --- a/src/vs/workbench/parts/experiments/node/experimentService.ts +++ b/src/vs/workbench/parts/experiments/node/experimentService.ts @@ -114,7 +114,7 @@ export class ExperimentService extends Disposable implements IExperimentService private _curatedMapping = Object.create(null); private _disposables: IDisposable[] = []; - private readonly _onExperimentEnabled: Emitter = new Emitter(); + private readonly _onExperimentEnabled = new Emitter(); onExperimentEnabled: Event = this._onExperimentEnabled.event; constructor( diff --git a/src/vs/workbench/parts/extensions/browser/extensionsViewer.ts b/src/vs/workbench/parts/extensions/browser/extensionsViewer.ts index 9dc6705e5be..27d82abbe09 100644 --- a/src/vs/workbench/parts/extensions/browser/extensionsViewer.ts +++ b/src/vs/workbench/parts/extensions/browser/extensionsViewer.ts @@ -42,7 +42,7 @@ export interface IExtensionData { export class DataSource implements IDataSource { public getId(tree: ITree, { extension, parent }: IExtensionData): string { - return parent ? this.getId(tree, parent) + '/' + extension.id : extension.id; + return parent ? this.getId(tree, parent) + '/' + extension.identifier.id : extension.identifier.id; } public hasChildren(tree: ITree, { hasChildren }: IExtensionData): boolean { @@ -144,13 +144,13 @@ export class Renderer implements IRenderer { } data.name.textContent = extension.displayName; - data.identifier.textContent = extension.id; + data.identifier.textContent = extension.identifier.id; data.author.textContent = extension.publisherDisplayName; data.extensionData = extensionData; } private renderUnknownExtension(tree: ITree, { extension }: IExtensionData, data: IUnknownExtensionTemplateData): void { - data.identifier.textContent = extension.id; + data.identifier.textContent = extension.identifier.id; } public disposeTemplate(tree: ITree, templateId: string, templateData: any): void { diff --git a/src/vs/workbench/parts/extensions/common/extensions.ts b/src/vs/workbench/parts/extensions/common/extensions.ts index e1e4bf832d9..59736f2ae12 100644 --- a/src/vs/workbench/parts/extensions/common/extensions.ts +++ b/src/vs/workbench/parts/extensions/common/extensions.ts @@ -7,7 +7,7 @@ import { IViewlet } from 'vs/workbench/common/viewlet'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { Event } from 'vs/base/common/event'; import { IPager } from 'vs/base/common/paging'; -import { IQueryOptions, IExtensionManifest, LocalExtensionType, EnablementState, ILocalExtension, IGalleryExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IQueryOptions, IExtensionManifest, LocalExtensionType, EnablementState, ILocalExtension, IGalleryExtension, IExtensionIdentifier } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IViewContainersRegistry, ViewContainer, Extensions as ViewContainerExtensions } from 'vs/workbench/common/views'; import { Registry } from 'vs/platform/registry/common/platform'; import { CancellationToken } from 'vs/base/common/cancellation'; @@ -31,8 +31,7 @@ export interface IExtension { state: ExtensionState; name: string; displayName: string; - id: string; - uuid?: string; + identifier: IExtensionIdentifier; publisher: string; publisherDisplayName: string; version: string; diff --git a/src/vs/workbench/parts/extensions/common/extensionsInput.ts b/src/vs/workbench/parts/extensions/common/extensionsInput.ts index e3f5e16c522..7ca9f6ec200 100644 --- a/src/vs/workbench/parts/extensions/common/extensionsInput.ts +++ b/src/vs/workbench/parts/extensions/common/extensionsInput.ts @@ -49,7 +49,7 @@ export class ExtensionsInput extends EditorInput { getResource(): URI { return URI.from({ scheme: 'extension', - path: this.extension.id + path: this.extension.identifier.id }); } } \ No newline at end of file diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts index 66c611888fb..7eea5f370fe 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts @@ -289,7 +289,7 @@ export class ExtensionEditor extends BaseEditor { this.icon.src = extension.iconUrl; this.name.textContent = extension.displayName; - this.identifier.textContent = extension.id; + this.identifier.textContent = extension.identifier.id; this.preview.style.display = extension.preview ? 'inherit' : 'none'; this.builtin.style.display = extension.type === LocalExtensionType.System ? 'inherit' : 'none'; @@ -298,8 +298,8 @@ export class ExtensionEditor extends BaseEditor { const extRecommendations = this.extensionTipsService.getAllRecommendationsWithReason(); let recommendationsData = {}; - if (extRecommendations[extension.id.toLowerCase()]) { - recommendationsData = { recommendationReason: extRecommendations[extension.id.toLowerCase()].reasonId }; + if (extRecommendations[extension.identifier.id.toLowerCase()]) { + recommendationsData = { recommendationReason: extRecommendations[extension.identifier.id.toLowerCase()].reasonId }; } /* __GDPR__ @@ -418,11 +418,11 @@ export class ExtensionEditor extends BaseEditor { this.transientDisposables.push(ignoreAction, undoIgnoreAction); const extRecommendations = this.extensionTipsService.getAllRecommendationsWithReason(); - if (extRecommendations[extension.id.toLowerCase()]) { + if (extRecommendations[extension.identifier.id.toLowerCase()]) { ignoreAction.enabled = true; - this.subtext.textContent = extRecommendations[extension.id.toLowerCase()].reasonText; + this.subtext.textContent = extRecommendations[extension.identifier.id.toLowerCase()].reasonText; show(this.subtextContainer); - } else if (this.extensionTipsService.getAllIgnoredRecommendations().global.indexOf(extension.id.toLowerCase()) !== -1) { + } else if (this.extensionTipsService.getAllIgnoredRecommendations().global.indexOf(extension.identifier.id.toLowerCase()) !== -1) { undoIgnoreAction.enabled = true; this.subtext.textContent = localize('recommendationHasBeenIgnored', "You have chosen not to receive recommendations for this extension."); show(this.subtextContainer); @@ -432,13 +432,13 @@ export class ExtensionEditor extends BaseEditor { } this.extensionTipsService.onRecommendationChange(change => { - if (change.extensionId.toLowerCase() === extension.id.toLowerCase()) { + if (change.extensionId.toLowerCase() === extension.identifier.id.toLowerCase()) { if (change.isRecommended) { undoIgnoreAction.enabled = false; const extRecommendations = this.extensionTipsService.getAllRecommendationsWithReason(); - if (extRecommendations[extension.id.toLowerCase()]) { + if (extRecommendations[extension.identifier.id.toLowerCase()]) { ignoreAction.enabled = true; - this.subtext.textContent = extRecommendations[extension.id.toLowerCase()].reasonText; + this.subtext.textContent = extRecommendations[extension.identifier.id.toLowerCase()].reasonText; } } else { undoIgnoreAction.enabled = true; diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionProfileService.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionProfileService.ts index 134e2bfe27b..f1c87433c21 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionProfileService.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionProfileService.ts @@ -20,6 +20,7 @@ import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { randomPort } from 'vs/base/node/ports'; import product from 'vs/platform/node/product'; import { RuntimeExtensionsInput } from 'vs/workbench/services/extensions/electron-browser/runtimeExtensionsInput'; +import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; export class ExtensionHostProfileService extends Disposable implements IExtensionHostProfileService { @@ -121,12 +122,12 @@ export class ExtensionHostProfileService extends Disposable implements IExtensio this._onDidChangeLastProfile.fire(void 0); } - getUnresponsiveProfile(extensionId: string): IExtensionHostProfile | undefined { - return this._unresponsiveProfiles.get(extensionId); + getUnresponsiveProfile(extensionId: CanonicalExtensionIdentifier): IExtensionHostProfile | undefined { + return this._unresponsiveProfiles.get(CanonicalExtensionIdentifier.toKey(extensionId)); } - setUnresponsiveProfile(extensionId: string, profile: IExtensionHostProfile): void { - this._unresponsiveProfiles.set(extensionId, profile); + setUnresponsiveProfile(extensionId: CanonicalExtensionIdentifier, profile: IExtensionHostProfile): void { + this._unresponsiveProfiles.set(CanonicalExtensionIdentifier.toKey(extensionId), profile); this._setLastProfile(profile); } diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts index 0d385e957c0..d21d6ec7905 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts @@ -85,7 +85,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe public loadWorkspaceConfigPromise: Promise; private proactiveRecommendationsFetched: boolean = false; - private readonly _onRecommendationChange: Emitter = new Emitter(); + private readonly _onRecommendationChange = new Emitter(); onRecommendationChange: Event = this._onRecommendationChange.event; private sessionSeed: number; @@ -635,8 +635,8 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe recommendationsToSuggest = recommendationsToSuggest.filter(id => importantRecommendationsIgnoreList.indexOf(id) === -1 && this.isExtensionAllowedToBeRecommended(id)); const importantTipsPromise = recommendationsToSuggest.length === 0 ? Promise.resolve(null) : this.extensionWorkbenchService.queryLocal().then(local => { - const localExtensions = local.map(e => e.id); - recommendationsToSuggest = recommendationsToSuggest.filter(id => localExtensions.every(local => local !== id.toLowerCase())); + const localExtensions = local.map(e => e.identifier); + recommendationsToSuggest = recommendationsToSuggest.filter(id => localExtensions.every(local => !areSameExtensions(local, { id }))); if (!recommendationsToSuggest.length) { return; } diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts b/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts index 0076f1a28d1..9d5f08e733b 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts @@ -50,7 +50,7 @@ import { ExtensionsAutoProfiler } from 'vs/workbench/parts/extensions/electron-b // Singletons registerSingleton(IExtensionsWorkbenchService, ExtensionsWorkbenchService); registerSingleton(IExtensionTipsService, ExtensionTipsService); -registerSingleton(IExtensionHostProfileService, ExtensionHostProfileService); +registerSingleton(IExtensionHostProfileService, ExtensionHostProfileService, true); const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); workbenchRegistry.registerWorkbenchContribution(StatusUpdater, LifecyclePhase.Restored); @@ -247,7 +247,7 @@ jsonRegistry.registerSchema(ExtensionsConfigurationSchemaId, ExtensionsConfigura // Register Commands CommandsRegistry.registerCommand('_extensions.manage', (accessor: ServicesAccessor, extensionId: string) => { const extensionService = accessor.get(IExtensionsWorkbenchService); - const extension = extensionService.local.filter(e => areSameExtensions(e, { id: extensionId })); + const extension = extensionService.local.filter(e => areSameExtensions(e.identifier, { id: extensionId })); if (extension.length === 1) { extensionService.open(extension[0]); } diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts index e1e6a53162c..2adce4e694b 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts @@ -18,7 +18,7 @@ import { IExtension, ExtensionState, IExtensionsWorkbenchService, VIEWLET_ID, IE import { ExtensionsConfigurationInitialContent } from 'vs/workbench/parts/extensions/common/extensionsFileTemplate'; import { LocalExtensionType, IExtensionEnablementService, IExtensionTipsService, EnablementState, ExtensionsLabel, IExtensionRecommendation, IGalleryExtension, IExtensionsConfigContent, IExtensionManagementServerService, IExtensionGalleryService, INSTALL_ERROR_MALICIOUS, INSTALL_ERROR_INCOMPATIBLE, IGalleryExtensionVersion } from 'vs/platform/extensionManagement/common/extensionManagement'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; -import { isUIExtension } from 'vs/platform/extensions/common/extensions'; +import { isUIExtension, CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ShowViewletAction } from 'vs/workbench/browser/viewlet'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; @@ -158,7 +158,7 @@ export class InstallAction extends Action { return; } - if (extension && !areSameExtensions(this.extension, extension)) { + if (extension && !areSameExtensions(this.extension.identifier, extension.identifier)) { return; } @@ -193,7 +193,7 @@ export class InstallAction extends Action { console.error(err); - return promptDownloadManually(extension.gallery, localize('failedToInstall', "Failed to install \'{0}\'.", extension.id), err, this.instantiationService, this.notificationService, this.openerService); + return promptDownloadManually(extension.gallery, localize('failedToInstall', "Failed to install \'{0}\'.", extension.identifier.id), err, this.instantiationService, this.notificationService, this.openerService); }); } @@ -231,7 +231,7 @@ export class UninstallAction extends Action { return; } - if (extension && !areSameExtensions(this.extension, extension)) { + if (extension && !areSameExtensions(this.extension.identifier, extension.identifier)) { return; } @@ -247,7 +247,7 @@ export class UninstallAction extends Action { this.label = UninstallAction.UninstallLabel; this.class = UninstallAction.UninstallClass; - const installedExtensions = this.extensionsWorkbenchService.local.filter(e => e.id === this.extension.id); + const installedExtensions = this.extensionsWorkbenchService.local.filter(e => areSameExtensions(e.identifier, this.extension.identifier)); if (!installedExtensions.length) { this.enabled = false; @@ -386,7 +386,7 @@ export class UpdateAction extends Action { return; } - if (extension && !areSameExtensions(this.extension, extension)) { + if (extension && !areSameExtensions(this.extension.identifier, extension.identifier)) { return; } @@ -420,7 +420,7 @@ export class UpdateAction extends Action { console.error(err); - return promptDownloadManually(extension.gallery, localize('failedToUpdate', "Failed to update \'{0}\'.", extension.id), err, this.instantiationService, this.notificationService, this.openerService); + return promptDownloadManually(extension.gallery, localize('failedToUpdate', "Failed to update \'{0}\'.", extension.identifier.id), err, this.instantiationService, this.notificationService, this.openerService); }); } @@ -568,7 +568,7 @@ export class ManageExtensionAction extends DropDownAction { this.disposables.push(extensionsWorkbenchService.onChange(extension => { if (extension && this.extension) { - if (areSameExtensions(this.extension, extension)) { + if (areSameExtensions(this.extension.identifier, extension.identifier)) { this.extension = extension; } } else { @@ -650,7 +650,7 @@ export class InstallAnotherVersionAction extends Action { console.error(err); - return promptDownloadManually(this.extension.gallery, localize('failedToInstall', "Failed to install \'{0}\'.", this.extension.id), err, this.instantiationService, this.notificationService, this.openerService); + return promptDownloadManually(this.extension.gallery, localize('failedToInstall', "Failed to install \'{0}\'.", this.extension.identifier.id), err, this.instantiationService, this.notificationService, this.openerService); }); } return null; @@ -673,14 +673,14 @@ export class ExtensionInfoAction extends Action implements IExtensionAction { } run(): Promise { - const { description, version, publisherDisplayName, id, displayName } = this.extension; + const { description, version, publisherDisplayName, identifier, displayName } = this.extension; const localizedExtension = localize('extensionInfoName', 'Name') + ': ' + displayName; - const localizedExtensionId = localize('extensionInfoId', 'Id') + ': ' + id; + const localizedExtensionId = localize('extensionInfoId', 'Id') + ': ' + identifier.id; const localizedDescription = localize('extensionInfoDescription', 'Description') + ': ' + description; const localizedVersion = localize('extensionInfoVersion', 'Version') + ': ' + version; const localizedPublisher = localize('extensionInfoPublisher', 'Publisher') + ': ' + publisherDisplayName; - const localizedVSMarketplaceLink = localize('extensionInfoVSMarketplaceLink', 'VS Marketplace Link') + ': https://marketplace.visualstudio.com/items?itemName=' + id; + const localizedVSMarketplaceLink = localize('extensionInfoVSMarketplaceLink', 'VS Marketplace Link') + ': https://marketplace.visualstudio.com/items?itemName=' + identifier.id; const clipboardStr = `${localizedExtension}\n${localizedExtensionId}\n${localizedDescription}\n${localizedVersion}\n${localizedPublisher}\n${localizedVSMarketplaceLink}`; @@ -749,7 +749,7 @@ export class EnableForWorkspaceAction extends Action implements IExtensionAction } private update(extension?: IExtension): void { - if (extension && this.extension && !areSameExtensions(this.extension, extension)) { + if (extension && this.extension && !areSameExtensions(this.extension.identifier, extension.identifier)) { return; } this.enabled = false; @@ -791,7 +791,7 @@ export class InstallInRemoteServerAction extends Action implements IExtensionAct } private update(extension?: IExtension): void { - if (extension && this.extension && !areSameExtensions(this.extension, extension)) { + if (extension && this.extension && !areSameExtensions(this.extension.identifier, extension.identifier)) { return; } this.enabled = false; @@ -804,7 +804,7 @@ export class InstallInRemoteServerAction extends Action implements IExtensionAct return !!server && server.authority === this.extensionManagementServerService.remoteExtensionManagementServer!.authority; }); if (!installedInRemoteServer) { - this.enabled = !this.runningExtensions.some(e => areSameExtensions({ id: e.id }, { id: this.extension.id })); + this.enabled = !this.runningExtensions.some(e => areSameExtensions({ id: e.identifier.value }, this.extension.identifier)); } } } @@ -857,7 +857,7 @@ export class EnableGloballyAction extends Action implements IExtensionAction { } private update(extension?: IExtension): void { - if (extension && this.extension && !areSameExtensions(this.extension, extension)) { + if (extension && this.extension && !areSameExtensions(this.extension.identifier, extension.identifier)) { return; } this.enabled = false; @@ -902,11 +902,11 @@ export class DisableForWorkspaceAction extends Action implements IExtensionActio } private update(extension?: IExtension): void { - if (extension && this.extension && !areSameExtensions(this.extension, extension)) { + if (extension && this.extension && !areSameExtensions(this.extension.identifier, extension.identifier)) { return; } this.enabled = false; - if (this.extension && this.runningExtensions.some(e => areSameExtensions({ id: e.id }, { id: this.extension.id }) && this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY)) { + if (this.extension && this.runningExtensions.some(e => areSameExtensions({ id: e.identifier.value }, this.extension.identifier) && this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY)) { this.enabled = this.extension.state === ExtensionState.Installed && (this.extension.enablementState === EnablementState.Enabled || this.extension.enablementState === EnablementState.WorkspaceEnabled) && !!this.extension.local && this.extensionEnablementService.canChangeEnablement(this.extension.local); } } @@ -938,11 +938,11 @@ export class DisableGloballyAction extends Action implements IExtensionAction { } private update(extension?: IExtension): void { - if (extension && this.extension && !areSameExtensions(this.extension, extension)) { + if (extension && this.extension && !areSameExtensions(this.extension.identifier, extension.identifier)) { return; } this.enabled = false; - if (this.extension && this.runningExtensions.some(e => areSameExtensions({ id: e.id }, { id: this.extension.id }))) { + if (this.extension && this.runningExtensions.some(e => areSameExtensions({ id: e.identifier.value }, this.extension.identifier))) { this.enabled = this.extension.state === ExtensionState.Installed && (this.extension.enablementState === EnablementState.Enabled || this.extension.enablementState === EnablementState.WorkspaceEnabled) && !!this.extension.local && this.extensionEnablementService.canChangeEnablement(this.extension.local); } } @@ -1180,7 +1180,7 @@ export class UpdateAllAction extends Action { console.error(err); - return promptDownloadManually(extension.gallery, localize('failedToUpdate', "Failed to update \'{0}\'.", extension.id), err, this.instantiationService, this.notificationService, this.openerService); + return promptDownloadManually(extension.gallery, localize('failedToUpdate', "Failed to update \'{0}\'.", extension.identifier.id), err, this.instantiationService, this.notificationService, this.openerService); }); } @@ -1218,7 +1218,7 @@ export class ReloadAction extends Action { } private update(extension?: IExtension): void { - if (extension && this.extension && !areSameExtensions(this.extension, extension)) { + if (extension && this.extension && !areSameExtensions(this.extension.identifier, extension.identifier)) { return; } this.throttler.queue(() => { @@ -1231,7 +1231,7 @@ export class ReloadAction extends Action { if (state === ExtensionState.Installing || state === ExtensionState.Uninstalling) { return Promise.resolve(); } - const installed = this.extensionsWorkbenchService.local.filter(e => e.id === this.extension.id)[0]; + const installed = this.extensionsWorkbenchService.local.filter(e => areSameExtensions(e.identifier, this.extension.identifier))[0]; const local = this.extension.local || (installed && installed.local); if (local && local.manifest && local.manifest.contributes && local.manifest.contributes.localizations && local.manifest.contributes.localizations.length > 0) { return Promise.resolve(); @@ -1248,7 +1248,7 @@ export class ReloadAction extends Action { const isUninstalled = this.extension.state === ExtensionState.Uninstalled; const isDisabled = this.extension.local ? !this.extensionEnablementService.isEnabled(this.extension.local) : false; const isEnabled = this.extension.local ? this.extensionEnablementService.isEnabled(this.extension.local) : false; - const runningExtension = runningExtensions.filter(e => areSameExtensions(e, this.extension))[0]; + const runningExtension = runningExtensions.filter(e => areSameExtensions({ id: e.identifier.value }, this.extension.identifier))[0]; if (installed && installed.local) { if (runningExtension) { @@ -1566,7 +1566,7 @@ export class InstallWorkspaceRecommendedExtensionsAction extends Action { installPromises.push(model.resolve(i, CancellationToken.None).then(e => { return this.extensionWorkbenchService.install(e).then(void 0, err => { console.error(err); - return promptDownloadManually(e.gallery, localize('failedToInstall', "Failed to install \'{0}\'.", e.id), err, this.instantiationService, this.notificationService, this.openerService); + return promptDownloadManually(e.gallery, localize('failedToInstall', "Failed to install \'{0}\'.", e.identifier.id), err, this.instantiationService, this.notificationService, this.openerService); }); })); } @@ -1608,7 +1608,7 @@ export class InstallRecommendedExtensionAction extends Action { return this.extensionWorkbenchService.install(extension) .then(() => null, err => { console.error(err); - return promptDownloadManually(extension.gallery, localize('failedToInstall', "Failed to install \'{0}\'.", extension.id), err, this.instantiationService, this.notificationService, this.openerService); + return promptDownloadManually(extension.gallery, localize('failedToInstall', "Failed to install \'{0}\'.", extension.identifier.id), err, this.instantiationService, this.notificationService, this.openerService); }); } return null; @@ -1637,7 +1637,7 @@ export class IgnoreExtensionRecommendationAction extends Action { } public run(): Promise { - this.extensionsTipsService.toggleIgnoredRecommendation(this.extension.id, true); + this.extensionsTipsService.toggleIgnoredRecommendation(this.extension.identifier.id, true); return Promise.resolve(null); } @@ -1667,7 +1667,7 @@ export class UndoIgnoreExtensionRecommendationAction extends Action { } public run(): Promise { - this.extensionsTipsService.toggleIgnoredRecommendation(this.extension.id, false); + this.extensionsTipsService.toggleIgnoredRecommendation(this.extension.identifier.id, false); return Promise.resolve(null); } @@ -2215,7 +2215,7 @@ export class AddToWorkspaceFolderRecommendationsAction extends AbstractConfigure return Promise.resolve(); } - const extensionId = this.editorService.activeEditor.extension.id; + const extensionId = this.editorService.activeEditor.extension.identifier; const pickFolderPromise = folders.length === 1 ? Promise.resolve(folders[0]) : this.commandService.executeCommand(PICK_WORKSPACE_FOLDER_COMMAND_ID); @@ -2226,14 +2226,14 @@ export class AddToWorkspaceFolderRecommendationsAction extends AbstractConfigure } const configurationFile = workspaceFolder.toResource(paths.join('.vscode', 'extensions.json')); return this.getWorkspaceFolderExtensionsConfigContent(configurationFile).then(content => { - const extensionIdLowerCase = extensionId.toLowerCase(); + const extensionIdLowerCase = extensionId.id.toLowerCase(); if (shouldRecommend) { if ((content.recommendations || []).some(e => e.toLowerCase() === extensionIdLowerCase)) { this.notificationService.info(localize('AddToWorkspaceFolderRecommendations.alreadyExists', 'This extension is already present in this workspace folder\'s recommendations.')); return Promise.resolve(); } - return this.addExtensionToWorkspaceFolderConfig(configurationFile, extensionId, shouldRecommend).then(() => { + return this.addExtensionToWorkspaceFolderConfig(configurationFile, extensionId.id, shouldRecommend).then(() => { this.notificationService.prompt(Severity.Info, localize('AddToWorkspaceFolderRecommendations.success', 'The extension was successfully added to this workspace folder\'s recommendations.'), [{ @@ -2250,7 +2250,7 @@ export class AddToWorkspaceFolderRecommendationsAction extends AbstractConfigure return Promise.resolve(); } - return this.addExtensionToWorkspaceFolderConfig(configurationFile, extensionId, shouldRecommend).then(() => { + return this.addExtensionToWorkspaceFolderConfig(configurationFile, extensionId.id, shouldRecommend).then(() => { this.notificationService.prompt(Severity.Info, localize('AddToWorkspaceFolderIgnoredRecommendations.success', 'The extension was successfully added to this workspace folder\'s unwanted recommendations.'), [{ @@ -2294,17 +2294,17 @@ export class AddToWorkspaceRecommendationsAction extends AbstractConfigureRecomm return Promise.resolve(); } - const extensionId = this.editorService.activeEditor.extension.id; + const extensionId = this.editorService.activeEditor.extension.identifier; return this.getWorkspaceExtensionsConfigContent(workspaceConfig).then(content => { - const extensionIdLowerCase = extensionId.toLowerCase(); + const extensionIdLowerCase = extensionId.id.toLowerCase(); if (shouldRecommend) { if ((content.recommendations || []).some(e => e.toLowerCase() === extensionIdLowerCase)) { this.notificationService.info(localize('AddToWorkspaceRecommendations.alreadyExists', 'This extension is already present in workspace recommendations.')); return Promise.resolve(); } - return this.addExtensionToWorkspaceConfig(workspaceConfig, extensionId, shouldRecommend).then(() => { + return this.addExtensionToWorkspaceConfig(workspaceConfig, extensionId.id, shouldRecommend).then(() => { this.notificationService.prompt(Severity.Info, localize('AddToWorkspaceRecommendations.success', 'The extension was successfully added to this workspace\'s recommendations.'), [{ @@ -2321,7 +2321,7 @@ export class AddToWorkspaceRecommendationsAction extends AbstractConfigureRecomm return Promise.resolve(); } - return this.addExtensionToWorkspaceConfig(workspaceConfig, extensionId, shouldRecommend).then(() => { + return this.addExtensionToWorkspaceConfig(workspaceConfig, extensionId.id, shouldRecommend).then(() => { this.notificationService.prompt(Severity.Info, localize('AddToWorkspaceUnwantedRecommendations.success', 'The extension was successfully added to this workspace\'s unwanted recommendations.'), [{ @@ -2387,12 +2387,12 @@ export class DisabledStatusLabelAction extends Action { } private update(extension?: IExtension): void { - if (extension && this.extension && !areSameExtensions(this.extension, extension)) { + if (extension && this.extension && !areSameExtensions(this.extension.identifier, extension.identifier)) { return; } this.class = `${DisabledStatusLabelAction.Class} hide`; this.tooltip = ''; - if (this.extension && this.extension.local && !this.extension.isMalicious && !this.runningExtensions.some(e => e.id === this.extension.id)) { + if (this.extension && this.extension.local && !this.extension.isMalicious && !this.runningExtensions.some(e => CanonicalExtensionIdentifier.equals(e.identifier, this.extension.identifier.id))) { if (this.extensionManagementServerService.remoteExtensionManagementServer && !isUIExtension(this.extension.local.manifest, this.configurationService) && this.extension.locals) { const installedInRemoteServer = this.extension.locals.some(local => { const server = this.extensionManagementServerService.getExtensionManagementServer(local.location); @@ -2643,9 +2643,9 @@ export class ReinstallAction extends Action { .filter(extension => extension.type === LocalExtensionType.User) .map(extension => { return { - id: extension.id, + id: extension.identifier.id, label: extension.displayName, - description: extension.id, + description: extension.identifier.id, extension, } as (IQuickPickItem & { extension: IExtension }); }); @@ -2725,9 +2725,9 @@ export class InstallSpecificVersionOfExtensionAction extends Action { .sort((e1, e2) => e1.extension.displayName.localeCompare(e2.extension.displayName)) .map(({ extension, versions }) => { return { - id: extension.id, - label: extension.displayName || extension.id, - description: extension.id, + id: extension.identifier.id, + label: extension.displayName || extension.identifier.id, + description: extension.identifier.id, extension, versions } as (IQuickPickItem & { extension: IExtension, versions: IGalleryExtensionVersion[] }); diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsAutoProfiler.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsAutoProfiler.ts index 925e7370d31..3fc50a54fd1 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsAutoProfiler.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsAutoProfiler.ts @@ -20,6 +20,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { RuntimeExtensionsInput } from 'vs/workbench/services/extensions/electron-browser/runtimeExtensionsInput'; import { generateUuid } from 'vs/base/common/uuid'; import { IExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/common/extensions'; +import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; export class ExtensionsAutoProfiler extends Disposable implements IWorkbenchContribution { @@ -132,7 +133,7 @@ export class ExtensionsAutoProfiler extends Disposable implements IWorkbenchCont } // add to running extensions view - this._extensionProfileService.setUnresponsiveProfile(extension.id, profile); + this._extensionProfileService.setUnresponsiveProfile(extension.identifier, profile); // print message to log const path = join(tmpdir(), `exthost-${Math.random().toString(16).slice(2, 8)}.cpuprofile`); @@ -162,7 +163,7 @@ export class ExtensionsAutoProfiler extends Disposable implements IWorkbenchCont // prompt: only when you can file an issue const reportAction = new ReportExtensionIssueAction({ - marketplaceInfo: this._anotherExtensionService.local.filter(value => value.id === extension.id)[0], + marketplaceInfo: this._anotherExtensionService.local.filter(value => CanonicalExtensionIdentifier.equals(value.identifier.id, extension.identifier))[0], description: extension, unresponsiveProfile: profile, status: undefined, @@ -172,10 +173,10 @@ export class ExtensionsAutoProfiler extends Disposable implements IWorkbenchCont } // only blame once per extension, don't blame too often - if (this._blame.has(extension.id) || this._blame.size >= 3) { + if (this._blame.has(CanonicalExtensionIdentifier.toKey(extension.identifier)) || this._blame.size >= 3) { return; } - this._blame.add(extension.id); + this._blame.add(CanonicalExtensionIdentifier.toKey(extension.identifier)); // user-facing message when very bad... this._notificationService.prompt( diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts index e7403fe12fa..2d566e09e56 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts @@ -11,7 +11,7 @@ import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { IPagedRenderer } from 'vs/base/browser/ui/list/listPaging'; -import { Emitter, Event } from 'vs/base/common/event'; +import { Event } from 'vs/base/common/event'; import { domEvent } from 'vs/base/browser/event'; import { IExtension, IExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/common/extensions'; import { InstallAction, UpdateAction, ManageExtensionAction, ReloadAction, extensionButtonProminentBackground, extensionButtonProminentForeground, MaliciousStatusLabelAction, ExtensionActionItem } from 'vs/workbench/parts/extensions/electron-browser/extensionsActions'; @@ -22,9 +22,9 @@ import { IExtensionTipsService, IExtensionManagementServerService } from 'vs/pla import { IThemeService } from 'vs/platform/theme/common/themeService'; import { INotificationService } from 'vs/platform/notification/common/notification'; -export interface IExtensionWithFocus extends IExtension { - onDidFocusChange?: Event; - onFocusChangeEventEmitter?: Emitter; +export interface IExtensionsViewState { + onFocus: Event; + onBlur: Event; } export interface ITemplateData { @@ -52,6 +52,7 @@ const actionOptions = { icon: true, label: true, tabOnlyOnFocus: true }; export class Renderer implements IPagedRenderer { constructor( + private extensionViewState: IExtensionsViewState, @IInstantiationService private instantiationService: IInstantiationService, @INotificationService private notificationService: INotificationService, @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, @@ -141,17 +142,17 @@ export class Renderer implements IPagedRenderer { data.extension = null; } - renderElement(extension: IExtensionWithFocus, index: number, data: ITemplateData): void { + renderElement(extension: IExtension, index: number, data: ITemplateData): void { removeClass(data.element, 'loading'); data.extensionDisposables = dispose(data.extensionDisposables); - const installed = this.extensionsWorkbenchService.local.filter(e => e.id === extension.id)[0]; + const installed = this.extensionsWorkbenchService.local.filter(e => areSameExtensions(e.identifier, extension.identifier))[0]; this.extensionService.getExtensions().then(runningExtensions => { if (installed && installed.local) { const installedExtensionServer = this.extensionManagementServerService.getExtensionManagementServer(installed.local.location); const isSameExtensionRunning = runningExtensions.some(e => { - if (!areSameExtensions(e, extension)) { + if (!areSameExtensions({ id: e.identifier.value }, extension.identifier)) { return false; } const runningExtensionServer = this.extensionManagementServerService.getExtensionManagementServer(e.extensionLocation); @@ -179,7 +180,7 @@ export class Renderer implements IPagedRenderer { this.updateRecommendationStatus(extension, data); data.extensionDisposables.push(this.extensionTipsService.onRecommendationChange(change => { - if (change.extensionId.toLowerCase() === extension.id.toLowerCase()) { + if (areSameExtensions({ id: change.extensionId }, extension.identifier)) { this.updateRecommendationStatus(extension, data); } })); @@ -195,26 +196,30 @@ export class Renderer implements IPagedRenderer { data.description.textContent = extension.gallery.properties.localizedLanguages.map(name => name[0].toLocaleUpperCase() + name.slice(1)).join(', '); } - if (extension.onDidFocusChange) { - data.extensionDisposables.push(extension.onDidFocusChange(hasFocus => { - data.actionbar.items.forEach(item => { - (item).setFocus(hasFocus); - }); - })); - } + this.extensionViewState.onFocus(e => { + if (areSameExtensions(extension.identifier, e.identifier)) { + data.actionbar.items.forEach(item => (item).setFocus(true)); + } + }, this, data.extensionDisposables); + + this.extensionViewState.onBlur(e => { + if (areSameExtensions(extension.identifier, e.identifier)) { + data.actionbar.items.forEach(item => (item).setFocus(false)); + } + }, this, data.extensionDisposables); } private updateRecommendationStatus(extension: IExtension, data: ITemplateData) { const extRecommendations = this.extensionTipsService.getAllRecommendationsWithReason(); let ariaLabel = extension.displayName + '. '; - if (!extRecommendations[extension.id.toLowerCase()]) { + if (!extRecommendations[extension.identifier.id.toLowerCase()]) { removeClass(data.root, 'recommended'); data.root.title = ''; } else { addClass(data.root, 'recommended'); - ariaLabel += extRecommendations[extension.id.toLowerCase()].reasonText + ' '; - data.root.title = extRecommendations[extension.id.toLowerCase()].reasonText; + ariaLabel += extRecommendations[extension.identifier.id.toLowerCase()].reasonText + ' '; + data.root.title = extRecommendations[extension.identifier.id.toLowerCase()].reasonText; } ariaLabel += localize('viewExtensionDetailsAria', "Press enter for extension details."); diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts index 024c14b98b7..99492408d99 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts @@ -370,18 +370,14 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio this.searchBox.onShouldFocusResults(() => this.focusListView(), this, this.disposables); - this.extensionsBox = append(this.root, $('.extensions')); - super.create(this.extensionsBox); - } - - setVisible(visible: boolean): void { - const isVisibilityChanged = this.isVisible() !== visible; - super.setVisible(visible); - if (isVisibilityChanged) { + this._register(this.onDidChangeVisibility(visible => { if (visible) { this.searchBox.focus(); } - } + })); + + this.extensionsBox = append(this.root, $('.extensions')); + super.create(this.extensionsBox); } focus(): void { diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts index af793327171..e138c76269e 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; -import { dispose } from 'vs/base/common/lifecycle'; +import { dispose, Disposable } from 'vs/base/common/lifecycle'; import { assign } from 'vs/base/common/objects'; import { Event, Emitter } from 'vs/base/common/event'; import { isPromiseCanceledError } from 'vs/base/common/errors'; @@ -15,8 +15,8 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { append, $, toggleClass } from 'vs/base/browser/dom'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { Delegate, Renderer, IExtensionWithFocus as IExtension } from 'vs/workbench/parts/extensions/electron-browser/extensionsList'; -import { IExtension as IExtensionBase, IExtensionsWorkbenchService } from '../common/extensions'; +import { Delegate, Renderer, IExtensionsViewState } from 'vs/workbench/parts/extensions/electron-browser/extensionsList'; +import { IExtension, IExtensionsWorkbenchService } from '../common/extensions'; import { Query } from '../common/extensionQuery'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IThemeService } from 'vs/platform/theme/common/themeService'; @@ -37,12 +37,29 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace import { distinct } from 'vs/base/common/arrays'; import { IExperimentService, IExperiment, ExperimentActionType } from 'vs/workbench/parts/experiments/node/experimentService'; import { alert } from 'vs/base/browser/ui/aria/aria'; -import { IListContextMenuEvent, IListEvent } from 'vs/base/browser/ui/list/list'; +import { IListContextMenuEvent } from 'vs/base/browser/ui/list/list'; import { createErrorWithActions } from 'vs/base/common/errorsWithActions'; import { CancellationToken } from 'vs/base/common/cancellation'; import { getKeywordsForExtension } from 'vs/workbench/parts/extensions/electron-browser/extensionsUtils'; import { IAction } from 'vs/base/common/actions'; +class ExtensionsViewState extends Disposable implements IExtensionsViewState { + + private readonly _onFocus: Emitter = this._register(new Emitter()); + readonly onFocus: Event = this._onFocus.event; + + private readonly _onBlur: Emitter = this._register(new Emitter()); + readonly onBlur: Event = this._onBlur.event; + + private currentlyFocusedItems: IExtension[] = []; + + onFocusChange(extensions: IExtension[]): void { + this.currentlyFocusedItems.forEach(extension => this._onBlur.fire(extension)); + this.currentlyFocusedItems = extensions; + this.currentlyFocusedItems.forEach(extension => this._onFocus.fire(extension)); + } +} + export class ExtensionsListView extends ViewletPanel { private messageBox: HTMLElement; @@ -87,15 +104,17 @@ export class ExtensionsListView extends ViewletPanel { this.extensionsList = append(container, $('.extensions-list')); this.messageBox = append(container, $('.message')); const delegate = new Delegate(); - const renderer = this.instantiationService.createInstance(Renderer); + const extensionsViewState = new ExtensionsViewState(); + const renderer = this.instantiationService.createInstance(Renderer, extensionsViewState); this.list = this.instantiationService.createInstance(WorkbenchPagedList, this.extensionsList, delegate, [renderer], { ariaLabel: localize('extensions', "Extensions"), multipleSelectionSupport: false, setRowLineHeight: false }) as WorkbenchPagedList; this.list.onContextMenu(e => this.onContextMenu(e), this, this.disposables); - this.list.onFocusChange(e => this.onFocusChange(e), this, this.disposables); + this.list.onFocusChange(e => extensionsViewState.onFocusChange(e.elements), this, this.disposables); this.disposables.push(this.list); + this.disposables.push(extensionsViewState); Event.chain(this.list.onOpen) .map(e => e.elements[0]) @@ -154,13 +173,6 @@ export class ExtensionsListView extends ViewletPanel { return Promise.resolve(emptyModel); } - private currentlyFocusedItems: IExtension[] = []; - private onFocusChange(e: IListEvent): void { - this.currentlyFocusedItems.forEach(item => item.onFocusChangeEventEmitter.fire(false)); - this.currentlyFocusedItems = e.elements; - this.currentlyFocusedItems.forEach(item => item.onFocusChangeEventEmitter.fire(true)); - } - private onContextMenu(e: IListContextMenuEvent): void { if (e.element) { this.extensionService.getExtensions() @@ -281,7 +293,7 @@ export class ExtensionsListView extends ViewletPanel { const result = local .sort((e1, e2) => e1.displayName.localeCompare(e2.displayName)) - .filter(e => runningExtensions.every(r => !areSameExtensions(r, e)) + .filter(e => runningExtensions.every(r => !areSameExtensions({ id: r.identifier.value }, e.identifier)) && (e.name.toLowerCase().indexOf(value) > -1 || e.displayName.toLowerCase().indexOf(value) > -1) && (!categories.length || categories.some(category => (e.local.manifest.categories || []).some(c => c.toLowerCase() === category)))); @@ -296,7 +308,7 @@ export class ExtensionsListView extends ViewletPanel { const result = local .sort((e1, e2) => e1.displayName.localeCompare(e2.displayName)) - .filter(e => runningExtensions.some(r => areSameExtensions(r, e)) + .filter(e => runningExtensions.some(r => areSameExtensions({ id: r.identifier.value }, e.identifier)) && (e.name.toLowerCase().indexOf(value) > -1 || e.displayName.toLowerCase().indexOf(value) > -1) && (!categories.length || categories.some(category => (e.local.manifest.categories || []).some(c => c.toLowerCase() === category)))); @@ -387,7 +399,7 @@ export class ExtensionsListView extends ViewletPanel { let positionToUpdate = 0; for (let i = 0; i < preferredResults.length; i++) { for (let j = positionToUpdate; j < pager.firstPage.length; j++) { - if (pager.firstPage[j].id === preferredResults[i]) { + if (areSameExtensions(pager.firstPage[j].identifier, { id: preferredResults[i] })) { if (positionToUpdate !== j) { const preferredExtension = pager.firstPage.splice(j, 1)[0]; pager.firstPage.splice(positionToUpdate, 0, preferredExtension); @@ -561,7 +573,7 @@ export class ExtensionsListView extends ViewletPanel { } private isRecommendationInstalled(recommendation: IExtensionRecommendation, installed: IExtension[]): boolean { - return installed.some(i => areSameExtensions({ id: i.id }, { id: recommendation.extensionId })); + return installed.some(i => areSameExtensions(i.identifier, { id: recommendation.extensionId })); } private getWorkspaceRecommendationsModel(query: Query, options: IQueryOptions): Promise> { @@ -602,7 +614,7 @@ export class ExtensionsListView extends ViewletPanel { private sortFirstPage(pager: IPager, ids: string[]) { ids = ids.map(x => x.toLowerCase()); pager.firstPage.sort((a, b) => { - return ids.indexOf(a.id.toLowerCase()) < ids.indexOf(b.id.toLowerCase()) ? -1 : 1; + return ids.indexOf(a.identifier.id.toLowerCase()) < ids.indexOf(b.identifier.id.toLowerCase()) ? -1 : 1; }); } @@ -616,7 +628,7 @@ export class ExtensionsListView extends ViewletPanel { toggleClass(this.messageBox, 'hidden', count > 0); this.badge.setCount(count); - if (count === 0 && this.isVisible()) { + if (count === 0 && this.isBodyVisible()) { this.messageBox.textContent = isGalleryError ? localize('galleryError', "We cannot connect to the Extensions Marketplace at this time, please try again later.") : localize('no extensions found', "No extensions found."); if (isGalleryError) { alert(this.messageBox.textContent); @@ -660,28 +672,15 @@ export class ExtensionsListView extends ViewletPanel { this.notificationService.error(err); } - private getPagedModel(arg: IPager | IExtensionBase[]): IPagedModel { - const convert = (ext: IExtension): IExtension => { - const eventEmitter = new Emitter(); - const extFocus: IExtension = ext; - extFocus.onFocusChangeEventEmitter = eventEmitter; - extFocus.onDidFocusChange = eventEmitter.event; - return extFocus; - }; - + private getPagedModel(arg: IPager | IExtension[]): IPagedModel { if (Array.isArray(arg)) { - return new PagedModel(arg.map(ext => convert(ext))); + return new PagedModel(arg); } - const pager = { total: arg.total, pageSize: arg.pageSize, - firstPage: arg.firstPage.map(ext => convert(ext)), - getPage: (pageIndex: number, cancellationToken: CancellationToken) => { - return arg.getPage(pageIndex, cancellationToken).then(extensions => { - return extensions.map(ext => convert(ext)); - }); - } + firstPage: arg.firstPage, + getPage: (pageIndex: number, cancellationToken: CancellationToken) => arg.getPage(pageIndex, cancellationToken) }; return new PagedModel(pager); } @@ -870,6 +869,6 @@ export class WorkspaceRecommendedExtensionsView extends ExtensionsListView { private getRecommendationsToInstall(): Promise { return this.tipsService.getWorkspaceRecommendations() - .then(recommendations => recommendations.filter(({ extensionId }) => !this.extensionsWorkbenchService.local.some(i => areSameExtensions({ id: extensionId }, { id: i.id })))); + .then(recommendations => recommendations.filter(({ extensionId }) => !this.extensionsWorkbenchService.local.some(i => areSameExtensions({ id: extensionId }, i.identifier)))); } } diff --git a/src/vs/workbench/parts/extensions/electron-browser/runtimeExtensionsEditor.ts b/src/vs/workbench/parts/extensions/electron-browser/runtimeExtensionsEditor.ts index 6bff006de03..42d8379eb06 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/runtimeExtensionsEditor.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/runtimeExtensionsEditor.ts @@ -42,6 +42,7 @@ import { ILabelService } from 'vs/platform/label/common/label'; import { renderOcticons } from 'vs/base/browser/ui/octiconLabel/octiconLabel'; import { join } from 'path'; import { onUnexpectedError } from 'vs/base/common/errors'; +import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; export const IExtensionHostProfileService = createDecorator('extensionHostProfileService'); export const CONTEXT_PROFILE_SESSION_STATE = new RawContextKey('profileSessionState', 'none'); @@ -66,8 +67,8 @@ export interface IExtensionHostProfileService { startProfiling(): void; stopProfiling(): void; - getUnresponsiveProfile(extensionId: string): IExtensionHostProfile; - setUnresponsiveProfile(extensionId: string, profile: IExtensionHostProfile): void; + getUnresponsiveProfile(extensionId: CanonicalExtensionIdentifier): IExtensionHostProfile; + setUnresponsiveProfile(extensionId: CanonicalExtensionIdentifier, profile: IExtensionHostProfile): void; } interface IExtensionProfileInformation { @@ -163,7 +164,7 @@ export class RuntimeExtensionsEditor extends BaseEditor { private _resolveExtensions(): IRuntimeExtension[] { let marketplaceMap: { [id: string]: IExtension; } = Object.create(null); for (let extension of this._extensionsWorkbenchService.local) { - marketplaceMap[extension.id] = extension; + marketplaceMap[CanonicalExtensionIdentifier.toKey(extension.identifier.id)] = extension; } let statusMap = this._extensionService.getExtensionsStatus(); @@ -177,10 +178,10 @@ export class RuntimeExtensionsEditor extends BaseEditor { const id = this._profileInfo.ids[i]; const delta = this._profileInfo.deltas[i]; - let extensionSegments = segments[id]; + let extensionSegments = segments[CanonicalExtensionIdentifier.toKey(id)]; if (!extensionSegments) { extensionSegments = []; - segments[id] = extensionSegments; + segments[CanonicalExtensionIdentifier.toKey(id)] = extensionSegments; } extensionSegments.push(currentStartTime); @@ -195,7 +196,7 @@ export class RuntimeExtensionsEditor extends BaseEditor { let profileInfo: IExtensionProfileInformation | null = null; if (this._profileInfo) { - let extensionSegments = segments[extensionDescription.id] || []; + let extensionSegments = segments[CanonicalExtensionIdentifier.toKey(extensionDescription.identifier)] || []; let extensionTotalTime = 0; for (let j = 0, lenJ = extensionSegments.length / 2; j < lenJ; j++) { const startTime = extensionSegments[2 * j]; @@ -211,10 +212,10 @@ export class RuntimeExtensionsEditor extends BaseEditor { result[i] = { originalIndex: i, description: extensionDescription, - marketplaceInfo: marketplaceMap[extensionDescription.id], - status: statusMap[extensionDescription.id], + marketplaceInfo: marketplaceMap[CanonicalExtensionIdentifier.toKey(extensionDescription.identifier)], + status: statusMap[extensionDescription.identifier.value], profileInfo: profileInfo, - unresponsiveProfile: this._extensionHostProfileService.getUnresponsiveProfile(extensionDescription.id) + unresponsiveProfile: this._extensionHostProfileService.getUnresponsiveProfile(extensionDescription.identifier) }; } @@ -352,7 +353,7 @@ export class RuntimeExtensionsEditor extends BaseEditor { clearNode(data.msgContainer); - if (this._extensionHostProfileService.getUnresponsiveProfile(element.description.id)) { + if (this._extensionHostProfileService.getUnresponsiveProfile(element.description.identifier)) { const el = $('span'); el.innerHTML = renderOcticons(` $(alert) Unresponsive`); el.title = nls.localize('unresponsive.title', "Extension has caused the extension host to freeze."); @@ -512,7 +513,7 @@ export class ReportExtensionIssueAction extends Action { // unresponsive extension host caused reason = 'Performance'; title = 'Extension causes high cpu load'; - let path = join(os.homedir(), `${extension.description.id}-unresponsive.cpuprofile.txt`); + let path = join(os.homedir(), `${extension.description.identifier.value}-unresponsive.cpuprofile.txt`); task = async () => { const profiler = await import('v8-inspect-profiler'); const data = profiler.rewriteAbsolutePaths({ profile: extension.unresponsiveProfile.data }, 'pii_removed'); diff --git a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts index 94bc7fad276..50788052d72 100644 --- a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts +++ b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts @@ -72,11 +72,11 @@ class Extension implements IExtension { return this.local.manifest.displayName || this.local.manifest.name; } - get id(): string { + get identifier(): IExtensionIdentifier { if (this.gallery) { - return this.gallery.identifier.id; + return this.gallery.identifier; } - return getGalleryExtensionIdFromLocal(this.local); + return { id: getGalleryExtensionIdFromLocal(this.local), uuid: this.local.identifier.uuid }; } get uuid(): string | undefined { @@ -209,7 +209,7 @@ class Extension implements IExtension { if (this.gallery.assets.manifest) { return this.galleryService.getManifest(this.gallery, token); } - this.logService.error(nls.localize('Manifest is not found', "Manifest is not found"), this.id); + this.logService.error(nls.localize('Manifest is not found', "Manifest is not found"), this.identifier.id); return Promise.resolve(null); } @@ -371,7 +371,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, private autoUpdateDelayer: ThrottledDelayer; private disposables: IDisposable[] = []; - private readonly _onChange: Emitter = new Emitter({ leakWarningThreshold: 200 }); + private readonly _onChange: Emitter = new Emitter(); get onChange(): Event { return this._onChange.event; } private _extensionAllowedBadgeProviders: string[]; @@ -427,7 +427,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, get local(): IExtension[] { const installing = this.installing - .filter(e => !this.installed.some(installed => installed.id === e.id)) + .filter(e => !this.installed.some(installed => areSameExtensions(installed.identifier, e.identifier))) .map(e => e); return [...this.installed, ...installing]; @@ -485,9 +485,9 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, const extensions: IExtension[] = [...this.local, ...galleryExtensions.map(galleryExtension => this.fromGallery(galleryExtension, maliciousSet))]; const map = new Map(); for (const extension of extensions) { - map.set(extension.id, extension); + map.set(extension.identifier.id, extension); } - return new ExtensionDependencies(extension, extension.id, map); + return new ExtensionDependencies(extension, extension.identifier.id, map); }); }); } @@ -564,7 +564,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, result = new Extension(this.galleryService, this.stateProvider, [], gallery, this.telemetryService, this.logService); } - if (maliciousExtensionSet.has(result.id)) { + if (maliciousExtensionSet.has(result.identifier.id)) { result.isMalicious = true; } @@ -578,7 +578,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, return installed; } } else { - if (installed.id === gallery.identifier.id) { // Installed from other sources + if (areSameExtensions(installed.identifier, gallery.identifier)) { // Installed from other sources return installed; } } @@ -625,7 +625,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, if (installed.uuid) { ids.push(installed.uuid); } else { - names.push(installed.id); + names.push(installed.identifier.id); } } } @@ -706,13 +706,13 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, } const ext = extension as Extension; - const toUninstall: ILocalExtension[] = ext.locals.length ? ext.locals : this.installed.filter(e => e.id === extension.id)[0].locals; + const toUninstall: ILocalExtension[] = ext.locals.length ? ext.locals : this.installed.filter(e => areSameExtensions(e.identifier, extension.identifier))[0].locals; - if (!toUninstall.length) { + if (!toUninstall || !toUninstall.length) { return Promise.reject(new Error('Missing local')); } - this.logService.info(`Requested uninstalling the extension ${extension.id} from window ${this.windowService.getCurrentWindowId()}`); + this.logService.info(`Requested uninstalling the extension ${extension.identifier.id} from window ${this.windowService.getCurrentWindowId()}`); return this.progressService.withProgress({ location: ProgressLocation.Extensions, title: nls.localize('uninstallingExtension', 'Uninstalling extension....'), @@ -751,9 +751,9 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, } const ext = extension as Extension; - const toReinstall: ILocalExtension[] = ext.locals.length ? ext.locals : this.installed.filter(e => e.id === extension.id)[0].locals; + const toReinstall: ILocalExtension[] = ext.locals.length ? ext.locals : this.installed.filter(e => areSameExtensions(e.identifier, extension.identifier))[0].locals; - if (!toReinstall.length) { + if (!toReinstall || !toReinstall.length) { return Promise.reject(new Error('Missing local')); } @@ -827,8 +827,8 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, return (enable || i.type === LocalExtensionType.User) // Include all Extensions for enablement and only user extensions for disablement && (options.dependencies || options.pack) && extensions.some(extension => - (options.dependencies && extension.dependencies.some(id => areSameExtensions({ id }, i))) - || (options.pack && extension.extensionPack.some(id => areSameExtensions({ id }, i))) + (options.dependencies && extension.dependencies.some(id => areSameExtensions({ id }, i.identifier))) + || (options.pack && extension.extensionPack.some(id => areSameExtensions({ id }, i.identifier))) ); }); if (extensionsToDisable.length) { @@ -853,13 +853,13 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, if (extensionsToDisable.indexOf(i) !== -1) { return false; } - return i.dependencies.some(dep => [extension, ...extensionsToDisable].some(d => d.id === dep)); + return i.dependencies.some(dep => [extension, ...extensionsToDisable].some(d => areSameExtensions(d.identifier, { id: dep }))); }); } private getDependentsErrorMessage(extension: IExtension, allDisabledExtensions: IExtension[], dependents: IExtension[]): string { for (const e of [extension, ...allDisabledExtensions]) { - let dependentsOfTheExtension = dependents.filter(d => d.dependencies.some(id => areSameExtensions({ id }, e))); + let dependentsOfTheExtension = dependents.filter(d => d.dependencies.some(id => areSameExtensions({ id }, e.identifier))); if (dependentsOfTheExtension.length) { return this.getErrorMessageForDisablingAnExtensionWithDependents(e, dependentsOfTheExtension); } @@ -917,7 +917,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, return; } - let extension = this.installed.filter(e => areSameExtensions(e, gallery.identifier))[0]; + let extension = this.installed.filter(e => areSameExtensions(e.identifier, gallery.identifier))[0]; if (!extension) { extension = new Extension(this.galleryService, this.stateProvider, [], gallery, this.telemetryService, this.logService); @@ -930,12 +930,12 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, private onDidInstallExtension(event: DidInstallExtensionEvent): void { const { local, zipPath, error, gallery } = event; - const installingExtension = gallery ? this.installing.filter(e => areSameExtensions(e, gallery.identifier))[0] : null; + const installingExtension = gallery ? this.installing.filter(e => areSameExtensions(e.identifier, gallery.identifier))[0] : null; let extension: Extension | undefined = installingExtension ? installingExtension : zipPath ? new Extension(this.galleryService, this.stateProvider, local ? [local] : [], void 0, this.telemetryService, this.logService) : undefined; if (extension) { this.installing = installingExtension ? this.installing.filter(e => e !== installingExtension) : this.installing; if (local) { - const installed = this.installed.filter(e => e.id === extension!.id)[0]; + const installed = this.installed.filter(e => areSameExtensions(e.identifier, extension!.identifier))[0]; if (installed) { extension = installed; const newServer = this.extensionManagementServerService.getExtensionManagementServer(local.location); @@ -990,7 +990,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, } private onEnablementChanged(identifier: IExtensionIdentifier) { - const [extension] = this.local.filter(e => areSameExtensions(e, identifier)); + const [extension] = this.local.filter(e => areSameExtensions(e.identifier, identifier)); if (extension && extension.local) { const enablementState = this.extensionEnablementService.getEnablementState(extension.local); if (enablementState !== extension.enablementState) { @@ -1005,7 +1005,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, return ExtensionState.Installing; } - if (this.uninstalling.some(e => e.id === extension.id)) { + if (this.uninstalling.some(e => areSameExtensions(e.identifier, extension.identifier))) { return ExtensionState.Uninstalling; } @@ -1046,7 +1046,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, const extensionId = match[1]; this.queryLocal().then(local => { - const extension = local.filter(local => areSameExtensions({ id: local.id }, { id: extensionId }))[0]; + const extension = local.filter(local => areSameExtensions(local.identifier, { id: extensionId }))[0]; if (extension) { return this.windowService.show() @@ -1110,4 +1110,4 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, this.syncDelayer.cancel(); this.disposables = dispose(this.disposables); } -} \ No newline at end of file +} diff --git a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsActions.test.ts b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsActions.test.ts index 71efe2a83c6..b0dd6e41b18 100644 --- a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsActions.test.ts +++ b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsActions.test.ts @@ -36,6 +36,7 @@ import { TestConfigurationService } from 'vs/platform/configuration/test/common/ import { ExtensionManagementServerService } from 'vs/workbench/services/extensions/node/extensionManagementServerService'; import { IRemoteAgentService } from 'vs/workbench/services/remote/node/remoteAgentService'; import { RemoteAgentService } from 'vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl'; +import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; suite('ExtensionsActions Test', () => { @@ -778,7 +779,7 @@ suite('ExtensionsActions Test', () => { return instantiationService.get(IExtensionsWorkbenchService).queryLocal() .then(extensions => { - const testObject: ExtensionsActions.DisableForWorkspaceAction = instantiationService.createInstance(ExtensionsActions.DisableForWorkspaceAction, extensions[0], [{ id: 'pub.a', extensionLocation: URI.file('pub.a') }]); + const testObject: ExtensionsActions.DisableForWorkspaceAction = instantiationService.createInstance(ExtensionsActions.DisableForWorkspaceAction, extensions[0], [{ identifier: new CanonicalExtensionIdentifier('pub.a'), extensionLocation: URI.file('pub.a') }]); assert.ok(testObject.enabled); }); }); @@ -823,7 +824,7 @@ suite('ExtensionsActions Test', () => { return instantiationService.get(IExtensionsWorkbenchService).queryLocal() .then(extensions => { - const testObject: ExtensionsActions.DisableGloballyAction = instantiationService.createInstance(ExtensionsActions.DisableGloballyAction, extensions[0], [{ id: 'pub.a', extensionLocation: URI.file('pub.a') }]); + const testObject: ExtensionsActions.DisableGloballyAction = instantiationService.createInstance(ExtensionsActions.DisableGloballyAction, extensions[0], [{ identifier: new CanonicalExtensionIdentifier('pub.a'), extensionLocation: URI.file('pub.a') }]); assert.ok(testObject.enabled); }); }); @@ -840,7 +841,7 @@ suite('ExtensionsActions Test', () => { return instantiationService.get(IExtensionsWorkbenchService).queryLocal() .then(extensions => { - const testObject: ExtensionsActions.DisableDropDownAction = instantiationService.createInstance(ExtensionsActions.DisableDropDownAction, extensions[0], [{ id: 'pub.a', extensionLocation: URI.file('pub.a') }]); + const testObject: ExtensionsActions.DisableDropDownAction = instantiationService.createInstance(ExtensionsActions.DisableDropDownAction, extensions[0], [{ identifier: new CanonicalExtensionIdentifier('pub.a'), extensionLocation: URI.file('pub.a') }]); assert.ok(testObject.enabled); }); }); @@ -853,7 +854,7 @@ suite('ExtensionsActions Test', () => { return instantiationService.get(IExtensionsWorkbenchService).queryLocal() .then(extensions => { - const testObject: ExtensionsActions.DisableDropDownAction = instantiationService.createInstance(ExtensionsActions.DisableDropDownAction, extensions[0], [{ id: 'pub.a', extensionLocation: URI.file('pub.a') }]); + const testObject: ExtensionsActions.DisableDropDownAction = instantiationService.createInstance(ExtensionsActions.DisableDropDownAction, extensions[0], [{ identifier: new CanonicalExtensionIdentifier('pub.a'), extensionLocation: URI.file('pub.a') }]); assert.ok(!testObject.enabled); }); }); @@ -867,7 +868,7 @@ suite('ExtensionsActions Test', () => { return instantiationService.get(IExtensionsWorkbenchService).queryLocal() .then(extensions => { - const testObject: ExtensionsActions.DisableDropDownAction = instantiationService.createInstance(ExtensionsActions.DisableDropDownAction, extensions[0], [{ id: 'pub.a', extensionLocation: URI.file('pub.a') }]); + const testObject: ExtensionsActions.DisableDropDownAction = instantiationService.createInstance(ExtensionsActions.DisableDropDownAction, extensions[0], [{ identifier: new CanonicalExtensionIdentifier('pub.a'), extensionLocation: URI.file('pub.a') }]); assert.ok(!testObject.enabled); }); }); @@ -879,7 +880,7 @@ suite('ExtensionsActions Test', () => { return instantiationService.get(IExtensionsWorkbenchService).queryGallery() .then(page => { - const testObject: ExtensionsActions.DisableDropDownAction = instantiationService.createInstance(ExtensionsActions.DisableDropDownAction, page.firstPage[0], [{ id: 'pub.a', extensionLocation: URI.file('pub.a') }]); + const testObject: ExtensionsActions.DisableDropDownAction = instantiationService.createInstance(ExtensionsActions.DisableDropDownAction, page.firstPage[0], [{ identifier: new CanonicalExtensionIdentifier('pub.a'), extensionLocation: URI.file('pub.a') }]); assert.ok(!testObject.enabled); }); }); @@ -890,7 +891,7 @@ suite('ExtensionsActions Test', () => { return instantiationService.get(IExtensionsWorkbenchService).queryGallery() .then(page => { - const testObject: ExtensionsActions.DisableDropDownAction = instantiationService.createInstance(ExtensionsActions.DisableDropDownAction, page.firstPage[0], [{ id: 'pub.a', extensionLocation: URI.file('pub.a') }]); + const testObject: ExtensionsActions.DisableDropDownAction = instantiationService.createInstance(ExtensionsActions.DisableDropDownAction, page.firstPage[0], [{ identifier: new CanonicalExtensionIdentifier('pub.a'), extensionLocation: URI.file('pub.a') }]); installEvent.fire({ identifier: gallery.identifier, gallery }); assert.ok(!testObject.enabled); @@ -903,7 +904,7 @@ suite('ExtensionsActions Test', () => { return instantiationService.get(IExtensionsWorkbenchService).queryLocal() .then(extensions => { - const testObject: ExtensionsActions.DisableDropDownAction = instantiationService.createInstance(ExtensionsActions.DisableDropDownAction, extensions[0], [{ id: 'pub.a', extensionLocation: URI.file('pub.a') }]); + const testObject: ExtensionsActions.DisableDropDownAction = instantiationService.createInstance(ExtensionsActions.DisableDropDownAction, extensions[0], [{ identifier: new CanonicalExtensionIdentifier('pub.a'), extensionLocation: URI.file('pub.a') }]); uninstallEvent.fire(local.identifier); assert.ok(!testObject.enabled); }); @@ -1016,7 +1017,7 @@ suite('ExtensionsActions Test', () => { }); test('Test ReloadAction when extension is newly installed', async () => { - instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ id: 'pub.b', extensionLocation: URI.file('pub.b') }]); + instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new CanonicalExtensionIdentifier('pub.b'), extensionLocation: URI.file('pub.b') }]); const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); const gallery = aGalleryExtension('a'); instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery)); @@ -1037,7 +1038,7 @@ suite('ExtensionsActions Test', () => { }); test('Test ReloadAction when extension is installed and uninstalled', () => { - instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ id: 'pub.b', extensionLocation: URI.file('pub.b') }]); + instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new CanonicalExtensionIdentifier('pub.b'), extensionLocation: URI.file('pub.b') }]); const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); const gallery = aGalleryExtension('a'); instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery)); @@ -1055,7 +1056,7 @@ suite('ExtensionsActions Test', () => { }); test('Test ReloadAction when extension is uninstalled', async () => { - instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ id: 'pub.a', extensionLocation: URI.file('pub.a') }]); + instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new CanonicalExtensionIdentifier('pub.a'), extensionLocation: URI.file('pub.a') }]); const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); const local = aLocalExtension('a'); instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); @@ -1074,7 +1075,7 @@ suite('ExtensionsActions Test', () => { }); test('Test ReloadAction when extension is uninstalled and installed', () => { - instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ id: 'pub.a', version: '1.0.0', extensionLocation: URI.file('pub.a') }]); + instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new CanonicalExtensionIdentifier('pub.a'), version: '1.0.0', extensionLocation: URI.file('pub.a') }]); const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); const local = aLocalExtension('a'); instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); @@ -1094,7 +1095,7 @@ suite('ExtensionsActions Test', () => { }); test('Test ReloadAction when extension is updated while running', async () => { - instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ id: 'pub.a', version: '1.0.1', extensionLocation: URI.file('pub.a') }]); + instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new CanonicalExtensionIdentifier('pub.a'), version: '1.0.1', extensionLocation: URI.file('pub.a') }]); const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); const local = aLocalExtension('a', { version: '1.0.1' }); const workbenchService = instantiationService.get(IExtensionsWorkbenchService); @@ -1115,7 +1116,7 @@ suite('ExtensionsActions Test', () => { }); test('Test ReloadAction when extension is updated when not running', () => { - instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ id: 'pub.b', extensionLocation: URI.file('pub.b') }]); + instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new CanonicalExtensionIdentifier('pub.b'), extensionLocation: URI.file('pub.b') }]); const local = aLocalExtension('a', { version: '1.0.1' }); return instantiationService.get(IExtensionEnablementService).setEnablement(local, EnablementState.Disabled) .then(() => { @@ -1136,7 +1137,7 @@ suite('ExtensionsActions Test', () => { }); test('Test ReloadAction when extension is disabled when running', () => { - instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ id: 'pub.a', extensionLocation: URI.file('pub.a') }]); + instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new CanonicalExtensionIdentifier('pub.a'), extensionLocation: URI.file('pub.a') }]); const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); const local = aLocalExtension('a'); const workbenchService = instantiationService.get(IExtensionsWorkbenchService); @@ -1152,7 +1153,7 @@ suite('ExtensionsActions Test', () => { }); test('Test ReloadAction when extension enablement is toggled when running', () => { - instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ id: 'pub.a', version: '1.0.0', extensionLocation: URI.file('pub.a') }]); + instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new CanonicalExtensionIdentifier('pub.a'), version: '1.0.0', extensionLocation: URI.file('pub.a') }]); const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); const local = aLocalExtension('a'); const workbenchService = instantiationService.get(IExtensionsWorkbenchService); @@ -1167,7 +1168,7 @@ suite('ExtensionsActions Test', () => { }); test('Test ReloadAction when extension is enabled when not running', () => { - instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ id: 'pub.b', extensionLocation: URI.file('pub.b') }]); + instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new CanonicalExtensionIdentifier('pub.b'), extensionLocation: URI.file('pub.b') }]); const local = aLocalExtension('a'); return instantiationService.get(IExtensionEnablementService).setEnablement(local, EnablementState.Disabled) .then(() => { @@ -1187,7 +1188,7 @@ suite('ExtensionsActions Test', () => { }); test('Test ReloadAction when extension enablement is toggled when not running', () => { - instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ id: 'pub.b', extensionLocation: URI.file('pub.b') }]); + instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new CanonicalExtensionIdentifier('pub.b'), extensionLocation: URI.file('pub.b') }]); const local = aLocalExtension('a'); return instantiationService.get(IExtensionEnablementService).setEnablement(local, EnablementState.Disabled) .then(() => { @@ -1205,7 +1206,7 @@ suite('ExtensionsActions Test', () => { }); test('Test ReloadAction when extension is updated when not running and enabled', () => { - instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ id: 'pub.b', extensionLocation: URI.file('pub.b') }]); + instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new CanonicalExtensionIdentifier('pub.b'), extensionLocation: URI.file('pub.b') }]); const local = aLocalExtension('a', { version: '1.0.1' }); return instantiationService.get(IExtensionEnablementService).setEnablement(local, EnablementState.Disabled) .then(() => { @@ -1230,7 +1231,7 @@ suite('ExtensionsActions Test', () => { }); test('Test ReloadAction when a localization extension is newly installed', async () => { - instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ id: 'pub.b', extensionLocation: URI.file('pub.b') }]); + instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new CanonicalExtensionIdentifier('pub.b'), extensionLocation: URI.file('pub.b') }]); const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); const gallery = aGalleryExtension('a'); instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery)); @@ -1245,7 +1246,7 @@ suite('ExtensionsActions Test', () => { }); test('Test ReloadAction when a localization extension is updated while running', async () => { - instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ id: 'pub.a', version: '1.0.1', extensionLocation: URI.file('pub.a') }]); + instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new CanonicalExtensionIdentifier('pub.a'), version: '1.0.1', extensionLocation: URI.file('pub.a') }]); const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); const local = aLocalExtension('a', { version: '1.0.1', contributes: { localizations: [{ languageId: 'de', translations: [] }] } }); const workbenchService = instantiationService.get(IExtensionsWorkbenchService); diff --git a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsViews.test.ts b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsViews.test.ts index cec002787a7..79848955185 100644 --- a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsViews.test.ts +++ b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsViews.test.ts @@ -38,6 +38,7 @@ import { IExperimentService, ExperimentService, ExperimentState, ExperimentActio import { IRemoteAgentService } from 'vs/workbench/services/remote/node/remoteAgentService'; import { RemoteAgentService } from 'vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl'; import { ExtensionManagementServerService } from 'vs/workbench/services/extensions/node/extensionManagementServerService'; +import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; suite('ExtensionsListView Tests', () => { @@ -123,11 +124,11 @@ suite('ExtensionsListView Tests', () => { instantiationService.stub(IExtensionService, { getExtensions: () => { return Promise.resolve([ - { id: localEnabledTheme.galleryIdentifier.id }, - { id: localEnabledLanguage.galleryIdentifier.id }, - { id: localRandom.galleryIdentifier.id }, - { id: builtInTheme.galleryIdentifier.id }, - { id: builtInBasic.galleryIdentifier.id } + { identifier: new CanonicalExtensionIdentifier(localEnabledTheme.galleryIdentifier.id) }, + { identifier: new CanonicalExtensionIdentifier(localEnabledLanguage.galleryIdentifier.id) }, + { identifier: new CanonicalExtensionIdentifier(localRandom.galleryIdentifier.id) }, + { identifier: new CanonicalExtensionIdentifier(builtInTheme.galleryIdentifier.id) }, + { identifier: new CanonicalExtensionIdentifier(builtInBasic.galleryIdentifier.id) } ]); } }); @@ -327,7 +328,7 @@ suite('ExtensionsListView Tests', () => { assert.equal(result.length, workspaceRecommendedExtensions.length); for (let i = 0; i < workspaceRecommendedExtensions.length; i++) { assert.equal(options.names[i], workspaceRecommendedExtensions[i].identifier.id); - assert.equal(result.get(i).id, workspaceRecommendedExtensions[i].identifier.id); + assert.equal(result.get(i).identifier.id, workspaceRecommendedExtensions[i].identifier.id); } }); }); @@ -348,7 +349,7 @@ suite('ExtensionsListView Tests', () => { assert.equal(result.length, allRecommendedExtensions.length); for (let i = 0; i < allRecommendedExtensions.length; i++) { assert.equal(options.names[i], allRecommendedExtensions[i].identifier.id); - assert.equal(result.get(i).id, allRecommendedExtensions[i].identifier.id); + assert.equal(result.get(i).identifier.id, allRecommendedExtensions[i].identifier.id); } }); }); @@ -372,7 +373,7 @@ suite('ExtensionsListView Tests', () => { assert.equal(result.length, allRecommendedExtensions.length); for (let i = 0; i < allRecommendedExtensions.length; i++) { assert.equal(options.names[i], allRecommendedExtensions[i].identifier.id); - assert.equal(result.get(i).id, allRecommendedExtensions[i].identifier.id); + assert.equal(result.get(i).identifier.id, allRecommendedExtensions[i].identifier.id); } }); }); @@ -395,7 +396,7 @@ suite('ExtensionsListView Tests', () => { assert.equal(result.length, curatedList.length); for (let i = 0; i < curatedList.length; i++) { assert.equal(options.names[i], curatedList[i].identifier.id); - assert.equal(result.get(i).id, curatedList[i].identifier.id); + assert.equal(result.get(i).identifier.id, curatedList[i].identifier.id); } assert.equal(curatedKey, 'mykey'); }); @@ -417,7 +418,7 @@ suite('ExtensionsListView Tests', () => { assert.equal(options.text, searchText); assert.equal(result.length, results.length); for (let i = 0; i < results.length; i++) { - assert.equal(result.get(i).id, results[i].identifier.id); + assert.equal(result.get(i).identifier.id, results[i].identifier.id); } }); }); @@ -466,7 +467,7 @@ suite('ExtensionsListView Tests', () => { assert.equal(options.text, searchText); assert.equal(result.length, expected.length); for (let i = 0; i < expected.length; i++) { - assert.equal(result.get(i).id, expected[i].identifier.id); + assert.equal(result.get(i).identifier.id, expected[i].identifier.id); } }); }); @@ -492,7 +493,7 @@ suite('ExtensionsListView Tests', () => { assert.equal(options.text, searchText); assert.equal(result.length, realResults.length); for (let i = 0; i < realResults.length; i++) { - assert.equal(result.get(i).id, realResults[i].identifier.id); + assert.equal(result.get(i).identifier.id, realResults[i].identifier.id); } }); }); diff --git a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsWorkbenchService.test.ts b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsWorkbenchService.test.ts index 4fc2046a973..dac3ad9d2bd 100644 --- a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsWorkbenchService.test.ts +++ b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsWorkbenchService.test.ts @@ -131,7 +131,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { assert.equal(null, actual.type); assert.equal('expectedName', actual.name); assert.equal('expectedDisplayName', actual.displayName); - assert.equal('expectedpublisher.expectedname', actual.id); + assert.equal('expectedpublisher.expectedname', actual.identifier.id); assert.equal('expectedPublisher', actual.publisher); assert.equal('expectedPublisherDisplayName', actual.publisherDisplayName); assert.equal('1.5', actual.version); @@ -189,7 +189,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { assert.equal(LocalExtensionType.User, actual.type); assert.equal('local1', actual.name); assert.equal('localDisplayName1', actual.displayName); - assert.equal('localpublisher1.local1', actual.id); + assert.equal('localpublisher1.local1', actual.identifier.id); assert.equal('localPublisher1', actual.publisher); assert.equal('1.1.0', actual.version); assert.equal('1.1.0', actual.latestVersion); @@ -208,7 +208,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { assert.equal(LocalExtensionType.System, actual.type); assert.equal('local2', actual.name); assert.equal('localDisplayName2', actual.displayName); - assert.equal('localpublisher2.local2', actual.id); + assert.equal('localpublisher2.local2', actual.identifier.id); assert.equal('localPublisher2', actual.publisher); assert.equal('1.2.0', actual.version); assert.equal('1.2.0', actual.latestVersion); @@ -283,7 +283,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { assert.equal(LocalExtensionType.User, actual.type); assert.equal('local1', actual.name); assert.equal('expectedDisplayName', actual.displayName); - assert.equal('localpublisher1.local1', actual.id); + assert.equal('localpublisher1.local1', actual.identifier.id); assert.equal('localPublisher1', actual.publisher); assert.equal('1.1.0', actual.version); assert.equal('1.5.0', actual.latestVersion); @@ -302,7 +302,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { assert.equal(LocalExtensionType.System, actual.type); assert.equal('local2', actual.name); assert.equal('localDisplayName2', actual.displayName); - assert.equal('localpublisher2.local2', actual.id); + assert.equal('localpublisher2.local2', actual.identifier.id); assert.equal('localPublisher2', actual.publisher); assert.equal('1.2.0', actual.version); assert.equal('1.2.0', actual.latestVersion); @@ -335,7 +335,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { let local = testObject.local; assert.equal(1, local.length); const actual = local[0]; - assert.equal(`${gallery.publisher}.${gallery.name}`, actual.id); + assert.equal(`${gallery.publisher}.${gallery.name}`, actual.identifier.id); assert.equal(ExtensionState.Installing, actual.state); // Installed @@ -497,21 +497,21 @@ suite('ExtensionsWorkbenchServiceTest', () => { actual = dependent.dependencies[0]; assert.ok(!actual.hasDependencies); - assert.equal('pub.b', actual.extension.id); + assert.equal('pub.b', actual.extension.identifier.id); assert.equal('pub.b', actual.identifier); assert.equal(dependent, actual.dependent); assert.equal(0, actual.dependencies.length); actual = dependent.dependencies[1]; assert.ok(!actual.hasDependencies); - assert.equal('pub.c', actual.extension.id); + assert.equal('pub.c', actual.extension.identifier.id); assert.equal('pub.c', actual.identifier); assert.equal(dependent, actual.dependent); assert.equal(0, actual.dependencies.length); actual = dependent.dependencies[2]; assert.ok(!actual.hasDependencies); - assert.equal('pub.d', actual.extension.id); + assert.equal('pub.d', actual.extension.identifier.id); assert.equal('pub.d', actual.identifier); assert.equal(dependent, actual.dependent); assert.equal(0, actual.dependencies.length); @@ -536,14 +536,14 @@ suite('ExtensionsWorkbenchServiceTest', () => { actual = dependent.dependencies[0]; assert.ok(!actual.hasDependencies); - assert.equal('pub.b', actual.extension.id); + assert.equal('pub.b', actual.extension.identifier.id); assert.equal('pub.b', actual.identifier); assert.equal(dependent, actual.dependent); assert.equal(0, actual.dependencies.length); actual = dependent.dependencies[1]; assert.ok(!actual.hasDependencies); - assert.equal('pub.a', actual.extension.id); + assert.equal('pub.a', actual.extension.identifier.id); assert.equal('pub.a', actual.identifier); assert.equal(dependent, actual.dependent); assert.equal(0, actual.dependencies.length); @@ -575,7 +575,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { actual = dependent.dependencies[1]; assert.ok(!actual.hasDependencies); - assert.equal('pub.a', actual.extension.id); + assert.equal('pub.a', actual.extension.identifier.id); assert.equal('pub.a', actual.identifier); assert.equal(dependent, actual.dependent); assert.equal(0, actual.dependencies.length); @@ -602,7 +602,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { actual = dependent.dependencies[0]; assert.ok(!actual.hasDependencies); - assert.equal('pub.inbuilt', actual.extension.id); + assert.equal('pub.inbuilt', actual.extension.identifier.id); assert.equal('pub.inbuilt', actual.identifier); assert.equal(dependent, actual.dependent); assert.equal(0, actual.dependencies.length); @@ -610,7 +610,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { actual = dependent.dependencies[1]; assert.ok(!actual.hasDependencies); - assert.equal('pub.a', actual.extension.id); + assert.equal('pub.a', actual.extension.identifier.id); assert.equal('pub.a', actual.identifier); assert.equal(dependent, actual.dependent); assert.equal(0, actual.dependencies.length); @@ -639,28 +639,28 @@ suite('ExtensionsWorkbenchServiceTest', () => { let b = a.dependencies[0]; assert.ok(b.hasDependencies); - assert.equal('pub.b', b.extension.id); + assert.equal('pub.b', b.extension.identifier.id); assert.equal('pub.b', b.identifier); assert.equal(a, b.dependent); assert.equal(2, b.dependencies.length); let c = a.dependencies[1]; assert.ok(c.hasDependencies); - assert.equal('pub.c', c.extension.id); + assert.equal('pub.c', c.extension.identifier.id); assert.equal('pub.c', c.identifier); assert.equal(a, c.dependent); assert.equal(1, c.dependencies.length); let d = b.dependencies[0]; assert.ok(d.hasDependencies); - assert.equal('pub.d', d.extension.id); + assert.equal('pub.d', d.extension.identifier.id); assert.equal('pub.d', d.identifier); assert.equal(b, d.dependent); assert.equal(2, d.dependencies.length); let e = b.dependencies[1]; assert.ok(!e.hasDependencies); - assert.equal('pub.e', e.extension.id); + assert.equal('pub.e', e.extension.identifier.id); assert.equal('pub.e', e.identifier); assert.equal(b, e.dependent); assert.equal(0, e.dependencies.length); @@ -674,14 +674,14 @@ suite('ExtensionsWorkbenchServiceTest', () => { c = d.dependencies[1]; assert.ok(c.hasDependencies); - assert.equal('pub.c', c.extension.id); + assert.equal('pub.c', c.extension.identifier.id); assert.equal('pub.c', c.identifier); assert.equal(d, c.dependent); assert.equal(1, c.dependencies.length); d = c.dependencies[0]; assert.ok(!d.hasDependencies); - assert.equal('pub.d', d.extension.id); + assert.equal('pub.d', d.extension.identifier.id); assert.equal('pub.d', d.identifier); assert.equal(c, d.dependent); assert.equal(0, d.dependencies.length); @@ -689,7 +689,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { c = a.dependencies[1]; d = c.dependencies[0]; assert.ok(d.hasDependencies); - assert.equal('pub.d', d.extension.id); + assert.equal('pub.d', d.extension.identifier.id); assert.equal('pub.d', d.identifier); assert.equal(c, d.dependent); assert.equal(2, d.dependencies.length); @@ -703,7 +703,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { c = d.dependencies[1]; assert.ok(!c.hasDependencies); - assert.equal('pub.c', c.extension.id); + assert.equal('pub.c', c.extension.identifier.id); assert.equal('pub.c', c.identifier); assert.equal(d, c.dependent); assert.equal(0, c.dependencies.length); diff --git a/src/vs/workbench/parts/feedback/electron-browser/feedback.ts b/src/vs/workbench/parts/feedback/electron-browser/feedback.ts index a6f133c193d..1c0d70f0874 100644 --- a/src/vs/workbench/parts/feedback/electron-browser/feedback.ts +++ b/src/vs/workbench/parts/feedback/electron-browser/feedback.ts @@ -47,10 +47,10 @@ export class FeedbackDropdown extends Dropdown { private readonly feedbackDelegate: IFeedbackDelegate; - private feedbackForm: HTMLFormElement; - private feedbackDescriptionInput: HTMLTextAreaElement; - private smileyInput: HTMLElement; - private frownyInput: HTMLElement; + private feedbackForm: HTMLFormElement | null; + private feedbackDescriptionInput: HTMLTextAreaElement | null; + private smileyInput: HTMLElement | null; + private frownyInput: HTMLElement | null; private sendButton: Button; private hideButton: HTMLInputElement; private remainingCharacterCount: HTMLElement; @@ -108,7 +108,7 @@ export class FeedbackDropdown extends Dropdown { dom.addClass(container, 'monaco-menu-container'); // Form - this.feedbackForm = dom.append(container, dom.$('form.feedback-form')); + this.feedbackForm = dom.append(container, dom.$('form.feedback-form')); this.feedbackForm.setAttribute('action', 'javascript:void(0);'); // Title @@ -133,7 +133,13 @@ export class FeedbackDropdown extends Dropdown { } if (darkenFactor) { - closeBtn.style.backgroundColor = darken(theme.getColor(editorWidgetBackground), darkenFactor)(theme).toString(); + const backgroundBaseColor = theme.getColor(editorWidgetBackground); + if (backgroundBaseColor) { + const backgroundColor = darken(backgroundBaseColor, darkenFactor)(theme); + if (backgroundColor) { + closeBtn.style.backgroundColor = backgroundColor.toString(); + } + } } })); @@ -242,7 +248,7 @@ export class FeedbackDropdown extends Dropdown { this.remainingCharacterCount.textContent = this.getCharCountText(0); // Feedback Input Form - this.feedbackDescriptionInput = dom.append(this.feedbackForm, dom.$('textarea.feedback-description')); + this.feedbackDescriptionInput = dom.append(this.feedbackForm, dom.$('textarea.feedback-description')); this.feedbackDescriptionInput.rows = 3; this.feedbackDescriptionInput.maxLength = this.maxFeedbackCharacters; this.feedbackDescriptionInput.textContent = this.feedback; @@ -278,9 +284,10 @@ export class FeedbackDropdown extends Dropdown { this.sendButton.onDidClick(() => this.onSubmit()); disposables.push(attachStylerCallback(this.themeService, { widgetShadow, editorWidgetBackground, inputBackground, inputForeground, inputBorder, editorBackground, contrastBorder }, colors => { - this.feedbackForm.style.backgroundColor = colors.editorWidgetBackground ? colors.editorWidgetBackground.toString() : null; - this.feedbackForm.style.boxShadow = colors.widgetShadow ? `0 0 8px ${colors.widgetShadow}` : null; - + if (this.feedbackForm) { + this.feedbackForm.style.backgroundColor = colors.editorWidgetBackground ? colors.editorWidgetBackground.toString() : null; + this.feedbackForm.style.boxShadow = colors.widgetShadow ? `0 0 8px ${colors.widgetShadow}` : null; + } if (this.feedbackDescriptionInput) { this.feedbackDescriptionInput.style.backgroundColor = colors.inputBackground ? colors.inputBackground.toString() : null; this.feedbackDescriptionInput.style.color = colors.inputForeground ? colors.inputForeground.toString() : null; @@ -313,27 +320,39 @@ export class FeedbackDropdown extends Dropdown { } private updateCharCountText(): void { - this.remainingCharacterCount.innerText = this.getCharCountText(this.feedbackDescriptionInput.value.length); - this.sendButton.enabled = this.feedbackDescriptionInput.value.length > 0; + if (this.feedbackDescriptionInput) { + this.remainingCharacterCount.innerText = this.getCharCountText(this.feedbackDescriptionInput.value.length); + this.sendButton.enabled = this.feedbackDescriptionInput.value.length > 0; + } } private setSentiment(smile: boolean): void { if (smile) { - dom.addClass(this.smileyInput, 'checked'); - this.smileyInput.setAttribute('aria-checked', 'true'); - dom.removeClass(this.frownyInput, 'checked'); - this.frownyInput.setAttribute('aria-checked', 'false'); + if (this.smileyInput) { + dom.addClass(this.smileyInput, 'checked'); + this.smileyInput.setAttribute('aria-checked', 'true'); + } + if (this.frownyInput) { + dom.removeClass(this.frownyInput, 'checked'); + this.frownyInput.setAttribute('aria-checked', 'false'); + } } else { - dom.addClass(this.frownyInput, 'checked'); - this.frownyInput.setAttribute('aria-checked', 'true'); - dom.removeClass(this.smileyInput, 'checked'); - this.smileyInput.setAttribute('aria-checked', 'false'); + if (this.frownyInput) { + dom.addClass(this.frownyInput, 'checked'); + this.frownyInput.setAttribute('aria-checked', 'true'); + } + if (this.smileyInput) { + dom.removeClass(this.smileyInput, 'checked'); + this.smileyInput.setAttribute('aria-checked', 'false'); + } } this.sentiment = smile ? 1 : 0; this.maxFeedbackCharacters = this.feedbackDelegate.getCharacterLimit(this.sentiment); this.updateCharCountText(); - this.feedbackDescriptionInput.maxLength = this.maxFeedbackCharacters; + if (this.feedbackDescriptionInput) { + this.feedbackDescriptionInput.maxLength = this.maxFeedbackCharacters; + } } private invoke(element: HTMLElement, disposables: IDisposable[], callback: () => void): HTMLElement { @@ -392,7 +411,7 @@ export class FeedbackDropdown extends Dropdown { } private onSubmit(): void { - if ((this.feedbackForm.checkValidity && !this.feedbackForm.checkValidity())) { + if (!this.feedbackForm || !this.feedbackDescriptionInput || (this.feedbackForm.checkValidity && !this.feedbackForm.checkValidity())) { return; } diff --git a/src/vs/workbench/parts/feedback/electron-browser/feedbackStatusbarItem.ts b/src/vs/workbench/parts/feedback/electron-browser/feedbackStatusbarItem.ts index 67e4b743813..09be3a9f93f 100644 --- a/src/vs/workbench/parts/feedback/electron-browser/feedbackStatusbarItem.ts +++ b/src/vs/workbench/parts/feedback/electron-browser/feedbackStatusbarItem.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; import { IStatusbarItem } from 'vs/workbench/browser/parts/statusbar/statusbar'; import { FeedbackDropdown, IFeedback, IFeedbackDelegate, FEEDBACK_VISIBLE_CONFIG, IFeedbackDropdownOptions } from 'vs/workbench/parts/feedback/electron-browser/feedback'; import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView'; @@ -52,7 +52,7 @@ class TwitterFeedbackService implements IFeedbackDelegate { } export class FeedbackStatusbarItem extends Themable implements IStatusbarItem { - private dropdown: FeedbackDropdown; + private dropdown: FeedbackDropdown | undefined; private enabled: boolean; private container: HTMLElement; private hideAction: HideAction; @@ -89,7 +89,7 @@ export class FeedbackStatusbarItem extends Themable implements IStatusbarItem { protected updateStyles(): void { super.updateStyles(); - if (this.dropdown) { + if (this.dropdown && this.dropdown.label) { this.dropdown.label.style.backgroundColor = (this.getColor(this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? STATUS_BAR_FOREGROUND : STATUS_BAR_NO_FOLDER_FOREGROUND)); } } @@ -148,7 +148,7 @@ export class FeedbackStatusbarItem extends Themable implements IStatusbarItem { clearNode(this.container); } - return null; + return Disposable.None; } } diff --git a/src/vs/workbench/parts/files/electron-browser/fileActions.ts b/src/vs/workbench/parts/files/electron-browser/fileActions.ts index 1cece8af158..a020dda2799 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileActions.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileActions.ts @@ -1582,7 +1582,7 @@ function openExplorerAndRunAction(accessor: ServicesAccessor, constructor: ICons return explorerPromise.then((explorer: ExplorerViewlet) => { const explorerView = explorer.getExplorerView(); - if (explorerView && explorerView.isVisible() && explorerView.isExpanded()) { + if (explorerView && explorerView.isBodyVisible()) { explorerView.focus(); const explorerContext = getContext(listService.lastFocusedList, viewletService); const action = instantationService.createInstance(constructor, listService.lastFocusedList, explorerContext.stat); diff --git a/src/vs/workbench/parts/files/electron-browser/views/emptyView.ts b/src/vs/workbench/parts/files/electron-browser/views/emptyView.ts index 410c9c9a1fe..990332f091f 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/emptyView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/emptyView.ts @@ -9,7 +9,6 @@ import * as env from 'vs/base/common/platform'; import * as DOM from 'vs/base/browser/dom'; import { IAction } from 'vs/base/common/actions'; import { Button } from 'vs/base/browser/ui/button/button'; -import { IActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { OpenFolderAction, OpenFileFolderAction, AddRootFolderAction } from 'vs/workbench/browser/actions/workspaceActions'; @@ -26,8 +25,8 @@ import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; export class EmptyView extends ViewletPanel { - public static readonly ID: string = 'workbench.explorer.emptyView'; - public static readonly NAME = nls.localize('noWorkspace', "No Folder Opened"); + static readonly ID: string = 'workbench.explorer.emptyView'; + static readonly NAME = nls.localize('noWorkspace', "No Folder Opened"); private button: Button; private messageElement: HTMLElement; @@ -46,7 +45,7 @@ export class EmptyView extends ViewletPanel { this.contextService.onDidChangeWorkbenchState(() => this.setLabels()); } - public renderHeader(container: HTMLElement): void { + renderHeader(container: HTMLElement): void { const titleContainer = document.createElement('div'); DOM.addClass(titleContainer, 'title'); container.appendChild(titleContainer); @@ -123,29 +122,9 @@ export class EmptyView extends ViewletPanel { // no-op } - public setVisible(visible: boolean): Promise { - return Promise.resolve(void 0); - } - - public focusBody(): void { + focusBody(): void { if (this.button) { this.button.element.focus(); } } - - protected reveal(element: any, relativeTop?: number): Promise { - return Promise.resolve(void 0); - } - - public getActions(): IAction[] { - return []; - } - - public getSecondaryActions(): IAction[] { - return []; - } - - public getActionItem(action: IAction): IActionItem { - return null; - } } diff --git a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts index f2af83afd23..8cc11a0f705 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -42,6 +42,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IViewletPanelOptions } from 'vs/workbench/browser/parts/views/panelViewlet'; import { ILabelService } from 'vs/platform/label/common/label'; +import { ResourceLabels, IResourceLabelsContainer } from 'vs/workbench/browser/labels'; export interface IExplorerViewOptions extends IViewletViewOptions { fileViewletState: FileViewletState; @@ -59,6 +60,7 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView public readonly id: string = ExplorerView.ID; private explorerViewer: WorkbenchTree; + private explorerLabels: ResourceLabels; private filter: FileFilter; private fileViewletState: FileViewletState; @@ -240,7 +242,7 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView this.viewState[ExplorerView.MEMENTO_LAST_ACTIVE_FILE_RESOURCE] = activeFile.toString(); // Select file if input is inside workspace - if (this.isVisible() && !this.isDisposed && this.contextService.isInsideWorkspace(activeFile)) { + if (this.isBodyVisible() && !this.isDisposed && this.contextService.isInsideWorkspace(activeFile)) { const selection = this.hasSingleSelection(activeFile); if (!selection) { this.select(activeFile); @@ -406,7 +408,9 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView private createViewer(container: HTMLElement): WorkbenchTree { const dataSource = this.instantiationService.createInstance(FileDataSource); - const renderer = this.instantiationService.createInstance(FileRenderer, this.fileViewletState); + this.explorerLabels = this.instantiationService.createInstance(ResourceLabels, { onDidChangeVisibility: this.onDidChangeBodyVisibility } as IResourceLabelsContainer); + this.disposables.push(this.explorerLabels); + const renderer = this.instantiationService.createInstance(FileRenderer, this.fileViewletState, this.explorerLabels); const controller = this.instantiationService.createInstance(FileController); this.disposables.push(controller); const sorter = this.instantiationService.createInstance(FileSorter); @@ -710,7 +714,7 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView } private refreshFromEvent(newRoots: IWorkspaceFolder[] = []): void { - if (this.isVisible() && !this.isDisposed) { + if (this.isBodyVisible() && !this.isDisposed) { this.explorerRefreshDelayer.trigger(() => { if (!this.explorerViewer.getHighlight()) { return this.doRefresh(newRoots.map(r => r.uri)).then(() => { diff --git a/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts b/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts index d5b8d08fd31..4b24a42e874 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts @@ -17,7 +17,7 @@ import * as comparers from 'vs/base/common/comparers'; import { InputBox, MessageType } from 'vs/base/browser/ui/inputbox/inputBox'; import { isMacintosh, isLinux } from 'vs/base/common/platform'; import * as glob from 'vs/base/common/glob'; -import { FileLabel, IFileLabelOptions } from 'vs/workbench/browser/labels'; +import { ResourceLabels, IFileLabelOptions, IResourceLabel } from 'vs/workbench/browser/labels'; import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; import { IFilesConfiguration, SortOrder } from 'vs/workbench/parts/files/common/files'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; @@ -180,7 +180,7 @@ export class ActionRunner extends BaseActionRunner implements IActionRunner { export interface IFileTemplateData { elementDisposable: IDisposable; - label: FileLabel; + label: IResourceLabel; container: HTMLElement; } @@ -190,20 +190,17 @@ export class FileRenderer implements IRenderer { private static readonly ITEM_HEIGHT = 22; private static readonly FILE_TEMPLATE_ID = 'file'; - private state: FileViewletState; private config: IFilesConfiguration; private configListener: IDisposable; constructor( - state: FileViewletState, + private state: FileViewletState, + private labels: ResourceLabels, @IContextViewService private contextViewService: IContextViewService, - @IInstantiationService private instantiationService: IInstantiationService, @IThemeService private themeService: IThemeService, @IConfigurationService private configurationService: IConfigurationService, @IWorkspaceContextService private contextService: IWorkspaceContextService - ) { - this.state = state; this.config = this.configurationService.getValue(); this.configListener = this.configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration('explorer')) { @@ -231,7 +228,7 @@ export class FileRenderer implements IRenderer { public renderTemplate(tree: ITree, templateId: string, container: HTMLElement): IFileTemplateData { const elementDisposable = Disposable.None; - const label = this.instantiationService.createInstance(FileLabel, container, void 0); + const label = this.labels.create(container); return { elementDisposable, label, container }; } @@ -268,7 +265,7 @@ export class FileRenderer implements IRenderer { private renderInputBox(container: HTMLElement, tree: ITree, stat: ExplorerItem, editableData: IEditableData): void { // Use a file label only for the icon next to the input box - const label = this.instantiationService.createInstance(FileLabel, container, void 0); + const label = this.labels.create(container); const extraClasses = ['explorer-item', 'explorer-item-edited']; const fileKind = stat.isRoot ? FileKind.ROOT_FOLDER : (stat.isDirectory || (stat instanceof NewStatPlaceholder && stat.isDirectoryPlaceholder())) ? FileKind.FOLDER : FileKind.FILE; const labelOptions: IFileLabelOptions = { hidePath: true, hideLabel: true, fileKind, extraClasses }; diff --git a/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts b/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts index 400f005c7d4..23f164e9a71 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts @@ -26,7 +26,7 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { badgeBackground, badgeForeground, contrastBorder } from 'vs/platform/theme/common/colorRegistry'; import { WorkbenchList } from 'vs/platform/list/browser/listService'; import { IListVirtualDelegate, IListRenderer, IListContextMenuEvent } from 'vs/base/browser/ui/list/list'; -import { EditorLabel } from 'vs/workbench/browser/labels'; +import { ResourceLabels, IResourceLabel, IResourceLabelsContainer } from 'vs/workbench/browser/labels'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; @@ -51,6 +51,7 @@ export class OpenEditorsView extends ViewletPanel { private listRefreshScheduler: RunOnceScheduler; private structuralRefreshDelay: number; private list: WorkbenchList; + private listLabels: ResourceLabels; private contributedContextMenu: IMenu; private needsRefresh: boolean; private resourceContext: ResourceContextKey; @@ -103,7 +104,7 @@ export class OpenEditorsView extends ViewletPanel { private registerUpdateEvents(): void { const updateWholeList = () => { - if (!this.isVisible() || !this.list || !this.isExpanded()) { + if (!this.isBodyVisible() || !this.list) { this.needsRefresh = true; return; } @@ -117,7 +118,7 @@ export class OpenEditorsView extends ViewletPanel { if (this.listRefreshScheduler.isScheduled()) { return; } - if (!this.isVisible() || !this.list || !this.isExpanded()) { + if (!this.isBodyVisible() || !this.list) { this.needsRefresh = true; return; } @@ -213,14 +214,19 @@ export class OpenEditorsView extends ViewletPanel { if (this.list) { this.list.dispose(); } + if (this.listLabels) { + this.listLabels.clear(); + } + this.listLabels = this.instantiationService.createInstance(ResourceLabels, { onDidChangeVisibility: this.onDidChangeBodyVisibility } as IResourceLabelsContainer); this.list = this.instantiationService.createInstance(WorkbenchList, container, delegate, [ new EditorGroupRenderer(this.keybindingService, this.instantiationService, this.editorGroupService), - new OpenEditorRenderer(getSelectedElements, this.instantiationService, this.keybindingService, this.configurationService, this.editorGroupService) + new OpenEditorRenderer(this.listLabels, getSelectedElements, this.instantiationService, this.keybindingService, this.configurationService, this.editorGroupService) ], { identityProvider: { getId: (element: OpenEditor | IEditorGroup) => element instanceof OpenEditor ? element.getId() : element.id.toString() }, selectOnMouseDown: false /* disabled to better support DND */ }) as WorkbenchList; this.disposables.push(this.list); + this.disposables.push(this.listLabels); this.contributedContextMenu = this.menuService.createMenu(MenuId.OpenEditorsContext, this.list.contextKeyService); this.disposables.push(this.contributedContextMenu); @@ -278,6 +284,13 @@ export class OpenEditorsView extends ViewletPanel { })); this.listRefreshScheduler.schedule(0); + + this.disposables.push(this.onDidChangeBodyVisibility(visible => { + this.updateListVisibility(visible); + if (visible && this.needsRefresh) { + this.listRefreshScheduler.schedule(0); + } + })); } public getActions(): IAction[] { @@ -288,22 +301,6 @@ export class OpenEditorsView extends ViewletPanel { ]; } - public setExpanded(expanded: boolean): void { - super.setExpanded(expanded); - this.updateListVisibility(expanded); - if (expanded && this.needsRefresh) { - this.listRefreshScheduler.schedule(0); - } - } - - public setVisible(visible: boolean): void { - super.setVisible(visible); - this.updateListVisibility(visible && this.isExpanded()); - if (visible && this.needsRefresh) { - this.listRefreshScheduler.schedule(0); - } - } - public focus(): void { super.focus(); this.list.domFocus(); @@ -478,7 +475,7 @@ export class OpenEditorsView extends ViewletPanel { interface IOpenEditorTemplateData { container: HTMLElement; - root: EditorLabel; + root: IResourceLabel; actionBar: ActionBar; actionRunner: OpenEditorActionRunner; openEditor: OpenEditor; @@ -619,6 +616,7 @@ class OpenEditorRenderer implements IListRenderer(); constructor( + private labels: ResourceLabels, private getSelectedElements: () => Array, private instantiationService: IInstantiationService, private keybindingService: IKeybindingService, @@ -643,7 +641,7 @@ class OpenEditorRenderer implements IListRenderer void): IDisposable { let disposed = false; @@ -35,9 +34,9 @@ function animate(drawFn: () => void): IDisposable { }); } -function makeItSnow(canvas: HTMLCanvasElement, dark: boolean): IDisposable { - const ctx = canvas.getContext('2d'); - const flakes = []; +function makeItSnow(canvas: HTMLCanvasElement): IDisposable { + const ctx = canvas.getContext('2d')!; + const flakes: any[] = []; function update() { const spawnCount = Math.ceil(Math.max(200 - flakes.length, 10) * Math.random() * 0.0005); @@ -51,7 +50,7 @@ function makeItSnow(canvas: HTMLCanvasElement, dark: boolean): IDisposable { vx: (-(0.5 * distance)) * window.devicePixelRatio, vy: (0.2 + 1.5 * distance) * window.devicePixelRatio, size: (2 + 2 * distance) * window.devicePixelRatio, - color: dark ? (170 + distance * 50) : (200 - distance * 50) + color: 170 + distance * 50 }); } @@ -90,8 +89,7 @@ export class HappyHolidaysAction extends Action { constructor( id: string, - label: string, - @IThemeService private themeService: IThemeService + label: string ) { super(id, label, '', true); } @@ -111,7 +109,7 @@ export class HappyHolidaysAction extends Action { canvas.height = document.body.clientHeight * window.devicePixelRatio; canvas.style.width = `${document.body.clientWidth}px`; canvas.style.height = `${document.body.clientHeight}px`; - disposables.push(makeItSnow(canvas, this.themeService.getTheme().type !== 'light')); + disposables.push(makeItSnow(canvas)); const text = append(el, $('.happy-holidays-text')); text.innerText = `The VS Code team wishes you a great Holiday season!`; diff --git a/src/vs/workbench/parts/holidays/electron-browser/media/holidays.css b/src/vs/workbench/parts/holidays/electron-browser/media/holidays.css index 6cd3dd26570..669cb846ab5 100644 --- a/src/vs/workbench/parts/holidays/electron-browser/media/holidays.css +++ b/src/vs/workbench/parts/holidays/electron-browser/media/holidays.css @@ -21,6 +21,10 @@ } .monaco-workbench.blur { + filter: blur(5px) brightness(60%); +} + +.vs-dark .monaco-workbench.blur { filter: blur(5px); } @@ -49,6 +53,7 @@ transition-duration: 0.8s; transition-property: transform, opacity; transition-timing-function: ease-out; + color: #ddd; } .happy-holidays-text.animate { diff --git a/src/vs/workbench/parts/markers/electron-browser/markers.ts b/src/vs/workbench/parts/markers/electron-browser/markers.ts index 48c519e8cf9..59940b9b889 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markers.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markers.ts @@ -23,6 +23,7 @@ import { CodeAction } from 'vs/editor/common/modes'; import { Range } from 'vs/editor/common/core/range'; import { getCodeActions } from 'vs/editor/contrib/codeAction/codeAction'; import { CodeActionKind } from 'vs/editor/contrib/codeAction/codeActionTrigger'; +import { timeout } from 'vs/base/common/async'; export const IMarkersWorkbenchService = createDecorator('markersWorkbenchService'); @@ -44,6 +45,8 @@ export class MarkersWorkbenchService extends Disposable implements IMarkersWorkb readonly markersModel: MarkersModel; private readonly allFixesCache: Map> = new Map>(); + private readonly codeActionsPromises: Map>> = new Map>>(); + private readonly codeActions: Map> = new Map>(); constructor( @IMarkerService private markerService: IMarkerService, @@ -66,6 +69,8 @@ export class MarkersWorkbenchService extends Disposable implements IMarkersWorkb private onMarkerChanged(resources: URI[]): void { for (const resource of resources) { this.allFixesCache.delete(resource.toString()); + this.codeActionsPromises.delete(resource.toString()); + this.codeActions.delete(resource.toString()); this.markersModel.setResourceMarkers(resource, this.readMarkers(resource)); } } @@ -74,8 +79,33 @@ export class MarkersWorkbenchService extends Disposable implements IMarkersWorkb return this.markerService.read({ resource, severities: MarkerSeverity.Error | MarkerSeverity.Warning | MarkerSeverity.Info }); } - async getQuickFixActions(marker: Marker): Promise { - const codeActions = await this.getFixes(marker); + getQuickFixActions(marker: Marker): Promise { + const markerKey = IMarkerData.makeKey(marker.marker); + let codeActionsPerMarker = this.codeActions.get(marker.resource.toString()); + if (!codeActionsPerMarker) { + codeActionsPerMarker = new Map(); + this.codeActions.set(marker.resource.toString(), codeActionsPerMarker); + } + let codeActions = codeActionsPerMarker.get(markerKey); + if (codeActions) { + return Promise.resolve(this.toActions(codeActions, marker)); + } else { + let codeActionsPromisesPerMarker = this.codeActionsPromises.get(marker.resource.toString()); + if (!codeActionsPromisesPerMarker) { + codeActionsPromisesPerMarker = new Map>(); + this.codeActionsPromises.set(marker.resource.toString(), codeActionsPromisesPerMarker); + } + if (!codeActionsPromisesPerMarker.has(markerKey)) { + const codeActionsPromise = this.getFixes(marker); + codeActionsPromisesPerMarker.set(markerKey, codeActionsPromise); + codeActionsPromise.then(codeActions => codeActionsPerMarker.set(markerKey, codeActions)); + } + // Wait for 100ms for code actions fetching. + return timeout(100).then(() => this.toActions(codeActionsPerMarker.get(markerKey) || [], marker)); + } + } + + private toActions(codeActions: CodeAction[], marker: Marker): IAction[] { return codeActions.map(codeAction => new Action( codeAction.command ? codeAction.command.id : codeAction.title, codeAction.title, diff --git a/src/vs/workbench/parts/markers/electron-browser/markersModel.ts b/src/vs/workbench/parts/markers/electron-browser/markersModel.ts index 3dac059b2e8..61ad8a6c8d2 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markersModel.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markersModel.ts @@ -120,7 +120,7 @@ export class MarkersModel { private cachedSortedResources: ResourceMarkers[] | undefined = undefined; - private readonly _onDidChange: Emitter = new Emitter(); + private readonly _onDidChange = new Emitter(); readonly onDidChange: Event = this._onDidChange.event; get resourceMarkers(): ResourceMarkers[] { diff --git a/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts b/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts index 18eb2ab1d2b..00e58fa9e51 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts @@ -33,7 +33,7 @@ import { IExpression, getEmptyExpression } from 'vs/base/common/glob'; import { mixin, deepClone } from 'vs/base/common/objects'; import { IWorkspaceFolder, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { isAbsolute, join } from 'vs/base/common/paths'; -import { FilterData, FileResourceMarkersRenderer, Filter, VirtualDelegate, ResourceMarkersRenderer, MarkerRenderer, RelatedInformationRenderer, TreeElement, MarkersTreeAccessibilityProvider, MarkersViewState } from 'vs/workbench/parts/markers/electron-browser/markersTreeViewer'; +import { FilterData, Filter, VirtualDelegate, ResourceMarkersRenderer, MarkerRenderer, RelatedInformationRenderer, TreeElement, MarkersTreeAccessibilityProvider, MarkersViewState } from 'vs/workbench/parts/markers/electron-browser/markersTreeViewer'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { Separator, ActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; @@ -42,6 +42,7 @@ import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; import { domEvent } from 'vs/base/browser/event'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { ResourceLabels } from 'vs/workbench/browser/labels'; function createModelIterator(model: MarkersModel): Iterator> { const resourcesIt = Iterator.fromArray(model.resourceMarkers); @@ -66,6 +67,7 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { private currentActiveResource: URI | null = null; private tree: WorkbenchObjectTree; + private treeLabels: ResourceLabels; private rangeHighlightDecorations: RangeHighlightDecorations; private actions: IAction[]; @@ -128,8 +130,16 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { this.updateFilter(); - this.onDidFocus(() => this.panelFoucusContextKey.set(true)); - this.onDidBlur(() => this.panelFoucusContextKey.set(false)); + this._register(this.onDidFocus(() => this.panelFoucusContextKey.set(true))); + this._register(this.onDidBlur(() => this.panelFoucusContextKey.set(false))); + + this._register(this.onDidChangeVisibility(visible => { + if (visible) { + this.refreshPanel(); + } else { + this.rangeHighlightDecorations.removeHighlightRange(); + } + })); this.render(); } @@ -160,18 +170,6 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { } } - public setVisible(visible: boolean): void { - const wasVisible = this.isVisible(); - super.setVisible(visible); - if (this.isVisible()) { - if (!wasVisible) { - this.refreshPanel(); - } - } else { - this.rangeHighlightDecorations.removeHighlightRange(); - } - } - public getActions(): IAction[] { if (!this.actions) { this.createActions(); @@ -287,10 +285,11 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { const onDidChangeRenderNodeCount = new Relay>(); + this.treeLabels = this._register(this.instantiationService.createInstance(ResourceLabels, this)); + const virtualDelegate = new VirtualDelegate(this.markersViewState); const renderers = [ - this.instantiationService.createInstance(FileResourceMarkersRenderer, onDidChangeRenderNodeCount.event), - this.instantiationService.createInstance(ResourceMarkersRenderer, onDidChangeRenderNodeCount.event), + this.instantiationService.createInstance(ResourceMarkersRenderer, this.treeLabels, onDidChangeRenderNodeCount.event), this.instantiationService.createInstance(MarkerRenderer, this.markersViewState, a => this.getActionItem(a)), this.instantiationService.createInstance(RelatedInformationRenderer) ]; diff --git a/src/vs/workbench/parts/markers/electron-browser/markersPanelActions.ts b/src/vs/workbench/parts/markers/electron-browser/markersPanelActions.ts index f42b506f93f..cde33a3e401 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markersPanelActions.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markersPanelActions.ts @@ -220,6 +220,7 @@ export class MarkersFilterActionItem extends BaseActionItem { this._register(filesExcludeFilter.onChange(() => { filesExcludeFilter.domNode.title = filesExcludeFilter.checked ? Messages.MARKERS_PANEL_ACTION_TOOLTIP_DO_NOT_USE_FILES_EXCLUDE : Messages.MARKERS_PANEL_ACTION_TOOLTIP_USE_FILES_EXCLUDE; this.action.useFilesExclude = filesExcludeFilter.checked; + this.focus(); })); this._register(this.action.onDidChange((event: IMarkersFilterActionChangeEvent) => { if (event.useFilesExclude) { diff --git a/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts b/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts index 9dfc41aa0b6..0919da43160 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts @@ -7,7 +7,7 @@ import * as dom from 'vs/base/browser/dom'; import * as network from 'vs/base/common/network'; import * as paths from 'vs/base/common/paths'; import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge'; -import { FileLabel, ResourceLabel } from 'vs/workbench/browser/labels'; +import { ResourceLabels, IResourceLabel } from 'vs/workbench/browser/labels'; import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; import { IMarker, MarkerSeverity } from 'vs/platform/markers/common/markers'; import { ResourceMarkers, Marker, RelatedInformation } from 'vs/workbench/parts/markers/electron-browser/markersModel'; @@ -34,7 +34,7 @@ import { localize } from 'vs/nls'; export type TreeElement = ResourceMarkers | Marker | RelatedInformation; interface IResourceMarkersTemplateData { - resourceLabel: ResourceLabel; + resourceLabel: IResourceLabel; count: CountBadge; styler: IDisposable; } @@ -69,7 +69,6 @@ export class MarkersTreeAccessibilityProvider implements IAccessibilityProvider< } const enum TemplateId { - FileResourceMarkers = 'frm', ResourceMarkers = 'rm', Marker = 'm', RelatedInformation = 'ri' @@ -90,11 +89,7 @@ export class VirtualDelegate implements IListVirtualDelegate { getTemplateId(element: TreeElement): string { if (element instanceof ResourceMarkers) { - if ((element).resource.scheme === network.Schemas.file || (element).resource.scheme === network.Schemas.untitled) { - return TemplateId.FileResourceMarkers; - } else { - return TemplateId.ResourceMarkers; - } + return TemplateId.ResourceMarkers; } else if (element instanceof Marker) { return TemplateId.Marker; } else { @@ -135,8 +130,8 @@ export class ResourceMarkersRenderer implements ITreeRenderer>, - @IInstantiationService protected instantiationService: IInstantiationService, @IThemeService private themeService: IThemeService, @ILabelService private labelService: ILabelService ) { @@ -149,7 +144,7 @@ export class ResourceMarkersRenderer implements ITreeRendererObject.create(null); const resourceLabelContainer = dom.append(container, dom.$('.resource-label-container')); - data.resourceLabel = this.createResourceLabel(resourceLabelContainer); + data.resourceLabel = this.labels.create(resourceLabelContainer, { supportHighlights: true }); const badgeWrapper = dom.append(container, dom.$('.count-badge-wrapper')); data.count = new CountBadge(badgeWrapper); @@ -162,10 +157,10 @@ export class ResourceMarkersRenderer implements ITreeRenderer): void { const templateData = this.renderedNodes.get(node); @@ -205,12 +196,6 @@ export class ResourceMarkersRenderer implements ITreeRenderer { diff --git a/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts b/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts index 2b12b085d2c..59f43720db4 100644 --- a/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts +++ b/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts @@ -50,7 +50,7 @@ import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewl import { CollapseAction } from 'vs/workbench/browser/viewlet'; import { IViewsService } from 'vs/workbench/common/views'; import { ACTIVE_GROUP, IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; -import { OutlineController, OutlineDataSource, OutlineItemComparator, OutlineItemCompareType, OutlineItemFilter, OutlineRenderer, OutlineTreeState } from '../../../../editor/contrib/documentSymbols/outlineTree'; +import { OutlineController, OutlineDataSource, OutlineItemComparator, OutlineItemCompareType, OutlineItemFilter, OutlineRenderer, OutlineTreeState } from 'vs/editor/contrib/documentSymbols/outlineTree'; import { OutlineConfigKeys, OutlineViewFiltered, OutlineViewFocused, OutlineViewId } from './outline'; class RequestState { @@ -372,6 +372,16 @@ export class OutlinePanel extends ViewletPanel { dom.toggleClass(this._domNode, 'no-icons', !this._configurationService.getValue(OutlineConfigKeys.icons)); } })); + + this.disposables.push(this.onDidChangeBodyVisibility(visible => { + if (visible && !this._requestOracle) { + this._requestOracle = this._instantiationService.createInstance(RequestOracle, (editor, event) => this._doUpdate(editor, event), DocumentSymbolProviderRegistry); + } else if (!visible) { + dispose(this._requestOracle); + this._requestOracle = undefined; + this._doUpdate(undefined, undefined); + } + })); } protected layoutBody(height: number): void { @@ -382,25 +392,6 @@ export class OutlinePanel extends ViewletPanel { } } - setVisible(visible: boolean): void { - if (visible && this.isExpanded() && !this._requestOracle) { - // workaround for https://github.com/Microsoft/vscode/issues/60011 - this.setExpanded(true); - } - super.setVisible(visible); - } - - setExpanded(expanded: boolean): void { - if (expanded) { - this._requestOracle = this._requestOracle || this._instantiationService.createInstance(RequestOracle, (editor, event) => this._doUpdate(editor, event).then(undefined, onUnexpectedError), DocumentSymbolProviderRegistry); - } else { - dispose(this._requestOracle); - this._requestOracle = undefined; - this._doUpdate(undefined, undefined); - } - return super.setExpanded(expanded); - } - getActions(): IAction[] { return [ new Action('collapse', localize('collapse', "Collapse All"), 'explorer-action collapse-explorer', true, () => { diff --git a/src/vs/workbench/parts/output/common/output.ts b/src/vs/workbench/parts/output/common/output.ts index 61aaa78e5ff..4a96b390a3a 100644 --- a/src/vs/workbench/parts/output/common/output.ts +++ b/src/vs/workbench/parts/output/common/output.ts @@ -166,10 +166,10 @@ export interface IOutputChannelRegistry { class OutputChannelRegistry implements IOutputChannelRegistry { private channels = new Map(); - private readonly _onDidRegisterChannel: Emitter = new Emitter(); + private readonly _onDidRegisterChannel = new Emitter(); readonly onDidRegisterChannel: Event = this._onDidRegisterChannel.event; - private readonly _onDidRemoveChannel: Emitter = new Emitter(); + private readonly _onDidRemoveChannel = new Emitter(); readonly onDidRemoveChannel: Event = this._onDidRemoveChannel.event; public registerChannel(descriptor: IOutputChannelDescriptor): void { diff --git a/src/vs/workbench/parts/output/electron-browser/outputServices.ts b/src/vs/workbench/parts/output/electron-browser/outputServices.ts index 20e0a533388..18019f5d085 100644 --- a/src/vs/workbench/parts/output/electron-browser/outputServices.ts +++ b/src/vs/workbench/parts/output/electron-browser/outputServices.ts @@ -76,10 +76,10 @@ abstract class AbstractFileOutputChannel extends Disposable implements OutputCha scrollLock: boolean = false; - protected _onDidAppendedContent: Emitter = new Emitter(); + protected _onDidAppendedContent = new Emitter(); readonly onDidAppendedContent: Event = this._onDidAppendedContent.event; - protected _onDispose: Emitter = new Emitter(); + protected _onDispose = new Emitter(); readonly onDispose: Event = this._onDispose.event; private readonly mimeType: string; @@ -281,7 +281,7 @@ class OutputChannelBackedByFile extends AbstractFileOutputChannel implements Out class OutputFileListener extends Disposable { - private readonly _onDidContentChange: Emitter = new Emitter(); + private readonly _onDidContentChange = new Emitter(); readonly onDidContentChange: Event = this._onDidContentChange.event; private watching: boolean = false; @@ -427,7 +427,7 @@ export class OutputService extends Disposable implements IOutputService, ITextMo private activeChannel: IOutputChannel; private readonly outputDir: string; - private readonly _onActiveOutputChannel: Emitter = new Emitter(); + private readonly _onActiveOutputChannel = new Emitter(); readonly onActiveOutputChannel: Event = this._onActiveOutputChannel.event; private _outputPanel: OutputPanel; @@ -683,10 +683,10 @@ class BufferredOutputChannel extends Disposable implements OutputChannel { readonly file: URI | null = null; scrollLock: boolean = false; - protected _onDidAppendedContent: Emitter = new Emitter(); + protected _onDidAppendedContent = new Emitter(); readonly onDidAppendedContent: Event = this._onDidAppendedContent.event; - private readonly _onDispose: Emitter = new Emitter(); + private readonly _onDispose = new Emitter(); readonly onDispose: Event = this._onDispose.event; private modelUpdater: RunOnceScheduler; diff --git a/src/vs/workbench/parts/performance/electron-browser/performance.contribution.ts b/src/vs/workbench/parts/performance/electron-browser/performance.contribution.ts index 9fbd7234073..bdfb8746810 100644 --- a/src/vs/workbench/parts/performance/electron-browser/performance.contribution.ts +++ b/src/vs/workbench/parts/performance/electron-browser/performance.contribution.ts @@ -3,6 +3,39 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { localize } from 'vs/nls'; +import { MenuRegistry } from 'vs/platform/actions/common/actions'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { Extensions as Input, IEditorInputFactory, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor'; +import { PerfviewInput } from 'vs/workbench/parts/performance/electron-browser/perfviewEditor'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import './startupProfiler'; import './startupTimings'; import './stats'; + +Registry.as(Input.EditorInputFactories).registerEditorInputFactory( + PerfviewInput.Id, + class implements IEditorInputFactory { + serialize(): string { + return ''; + } + deserialize(instantiationService: IInstantiationService): PerfviewInput { + return instantiationService.createInstance(PerfviewInput); + } + } +); + +CommandsRegistry.registerCommand('perfview.show', accessor => { + + const editorService = accessor.get(IEditorService); + const instaService = accessor.get(IInstantiationService); + return editorService.openEditor(instaService.createInstance(PerfviewInput)); +}); + +MenuRegistry.addCommand({ + id: 'perfview.show', + category: localize('show.cat', "Developer"), + title: localize('show.label', "Startup Performance (2)") +}); diff --git a/src/vs/workbench/parts/performance/electron-browser/perfviewEditor.ts b/src/vs/workbench/parts/performance/electron-browser/perfviewEditor.ts new file mode 100644 index 00000000000..25823f7606d --- /dev/null +++ b/src/vs/workbench/parts/performance/electron-browser/perfviewEditor.ts @@ -0,0 +1,226 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { localize } from 'vs/nls'; +import { URI } from 'vs/base/common/uri'; +import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; +import { ITextModelService, ITextModelContentProvider } from 'vs/editor/common/services/resolverService'; +import { IHashService } from 'vs/workbench/services/hash/common/hashService'; +import { ITextModel } from 'vs/editor/common/model'; +import { ITextEditorModel } from 'vs/workbench/common/editor'; +import { ILifecycleService, LifecyclePhase, StartupKindToString } from 'vs/platform/lifecycle/common/lifecycle'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { ITimerService, IStartupMetrics } from 'vs/workbench/services/timer/electron-browser/timerService'; +import { repeat } from 'vs/base/common/strings'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import * as perf from 'vs/base/common/performance'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; +import { writeTransientState } from 'vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap'; + +export class PerfviewInput extends ResourceEditorInput { + + static readonly Id = 'PerfviewInput'; + static readonly Uri = URI.from({ scheme: 'perf', path: 'Startup Performance' }); + + constructor( + @IInstantiationService private readonly _instaService: IInstantiationService, + @ITextModelService private _textModelResolverService: ITextModelService, + @IHashService hashService: IHashService + ) { + super( + localize('name', "Startup Performance"), + undefined, + PerfviewInput.Uri, + _textModelResolverService, hashService + ); + } + + getTypeId(): string { + return PerfviewInput.Id; + } + + resolve(): Promise { + if (!this._textModelResolverService.hasTextModelContentProvider(PerfviewInput.Uri.scheme)) { + this._textModelResolverService.registerTextModelContentProvider(PerfviewInput.Uri.scheme, this._instaService.createInstance(PerfModelContentProvider)); + } + return super.resolve(); + } +} + +class PerfModelContentProvider implements ITextModelContentProvider { + + private _model: ITextModel | undefined; + private _modelDisposables: IDisposable[] = []; + + constructor( + @IModelService private readonly _modelService: IModelService, + @IModeService private readonly _modeService: IModeService, + @ICodeEditorService private readonly _editorService: ICodeEditorService, + @ILifecycleService private readonly _lifecycleService: ILifecycleService, + @ITimerService private readonly _timerService: ITimerService, + @IExtensionService private readonly _extensionService: IExtensionService, + ) { } + + provideTextContent(resource: URI): Promise { + + if (!this._model) { + dispose(this._modelDisposables); + const langId = this._modeService.create('markdown'); + this._model = this._modelService.getModel(resource) || this._modelService.createModel('Loading...', langId, resource); + + this._modelDisposables.push(langId.onDidChange(e => this._model.setMode(e))); + this._modelDisposables.push(langId); + this._modelDisposables.push(this._extensionService.onDidChangeExtensionsStatus(this._updateModel, this)); + + writeTransientState(this._model, { forceWordWrap: 'off', forceWordWrapMinified: false }, this._editorService); + } + this._updateModel(); + return Promise.resolve(this._model); + } + + private _updateModel(): void { + + + Promise.all([ + this._timerService.startupMetrics, + this._lifecycleService.when(LifecyclePhase.Eventually), + this._extensionService.whenInstalledExtensionsRegistered() + ]).then(([metrics]) => { + if (!this._model.isDisposed()) { + let md = new MarkdownBuilder(); + this._addSummary(md, metrics); + md.blank(); + this._addSummaryTable(md, metrics); + md.blank(); + this._addExtensionsTable(md); + md.blank(); + this._addRawPerfMarks(md); + this._model.setValue(md.value); + } + }); + + } + + private _addSummary(md: MarkdownBuilder, metrics: IStartupMetrics): void { + md.heading(2, 'System Info'); + md.li(`OS: ${metrics.platform}(${metrics.release})`); + md.li(`CPUs: ${metrics.cpus.model}(${metrics.cpus.count} x ${metrics.cpus.speed})`); + md.li(`Memory(System): ${(metrics.totalmem / (1024 * 1024 * 1024)).toFixed(2)} GB(${(metrics.freemem / (1024 * 1024 * 1024)).toFixed(2)}GB free)`); + md.li(`Memory(Process): ${(metrics.meminfo.workingSetSize / 1024).toFixed(2)} MB working set(${(metrics.meminfo.peakWorkingSetSize / 1024).toFixed(2)}MB peak, ${(metrics.meminfo.privateBytes / 1024).toFixed(2)}MB private, ${(metrics.meminfo.sharedBytes / 1024).toFixed(2)}MB shared)`); + md.li(`VM(likelyhood): ${metrics.isVMLikelyhood}%`); + md.li(`Initial Startup: ${metrics.initialStartup}`); + md.li(`Has ${metrics.windowCount - 1} other windows`); + md.li(`Screen Reader Active: ${metrics.hasAccessibilitySupport}`); + md.li(`Empty Workspace: ${metrics.emptyWorkbench}`); + } + + private _addSummaryTable(md: MarkdownBuilder, metrics: IStartupMetrics, nodeModuleLoadTime?: number): void { + + const table: (string | number)[][] = []; + table.push(['start => app.isReady', metrics.timers.ellapsedAppReady, '[main]', `initial startup: ${metrics.initialStartup}`]); + table.push(['nls:start => nls:end', metrics.timers.ellapsedNlsGeneration, '[main]', `initial startup: ${metrics.initialStartup}`]); + table.push(['app.isReady => window.loadUrl()', metrics.timers.ellapsedWindowLoad, '[main]', `initial startup: ${metrics.initialStartup}`]); + table.push(['require & init global storage', metrics.timers.ellapsedGlobalStorageInitMain, '[main]', `initial startup: ${metrics.initialStartup}`]); + table.push(['window.loadUrl() => begin to require(workbench.main.js)', metrics.timers.ellapsedWindowLoadToRequire, '[main->renderer]', StartupKindToString(metrics.windowKind)]); + table.push(['require(workbench.main.js)', metrics.timers.ellapsedRequire, '[renderer]', `cached data: ${(metrics.didUseCachedData ? 'YES' : 'NO')}${nodeModuleLoadTime ? `, node_modules took ${nodeModuleLoadTime}ms` : ''}`]); + table.push(['init global storage', metrics.timers.ellapsedGlobalStorageInitRenderer, '[renderer]', undefined]); + table.push(['require workspace storage', metrics.timers.ellapsedWorkspaceStorageRequire, '[renderer]', undefined]); + table.push(['require & init workspace storage', metrics.timers.ellapsedWorkspaceStorageInit, '[renderer]', undefined]); + table.push(['init workspace service', metrics.timers.ellapsedWorkspaceServiceInit, '[renderer]', undefined]); + table.push(['register extensions & spawn extension host', metrics.timers.ellapsedExtensions, '[renderer]', undefined]); + table.push(['restore viewlet', metrics.timers.ellapsedViewletRestore, '[renderer]', metrics.viewletId]); + table.push(['restore panel', metrics.timers.ellapsedPanelRestore, '[renderer]', metrics.panelId]); + table.push(['restore editors', metrics.timers.ellapsedEditorRestore, '[renderer]', `${metrics.editorIds.length}: ${metrics.editorIds.join(', ')}`]); + table.push(['overall workbench load', metrics.timers.ellapsedWorkbench, '[renderer]', undefined]); + table.push(['workbench ready', metrics.ellapsed, '[main->renderer]', undefined]); + table.push(['extensions registered', metrics.timers.ellapsedExtensionsReady, '[renderer]', undefined]); + + md.heading(2, 'Performance Marks'); + md.table(['What', 'Duration', 'Process', 'Info'], table); + } + + private _addExtensionsTable(md: MarkdownBuilder): void { + + const table: ({ toString(): string })[][] = []; + let extensionsStatus = this._extensionService.getExtensionsStatus(); + for (let id in extensionsStatus) { + const { activationTimes: times } = extensionsStatus[id]; + if (!times) { + continue; + } + table.push([id, times.startup, times.codeLoadingTime, times.activateCallTime, times.activateResolvedTime, times.activationEvent]); + } + if (table.length > 0) { + md.heading(2, 'Extension Activation Stats'); + md.table( + ['Extension', 'Eager', 'Load Code', 'Call Activate', 'Finish Activate', 'Event'], + table + ); + } + } + + private _addRawPerfMarks(md: MarkdownBuilder): void { + md.heading(2, 'Raw Perf Marks'); + md.value += '```\n'; + for (const { name, startTime } of perf.getEntries('mark')) { + md.value += `${name}\t${startTime}\n`; + } + md.value += '```\n'; + } +} + +class MarkdownBuilder { + + value: string = ''; + + heading(level: number, value: string): this { + this.value += `${repeat('#', level)} ${value}\n\n`; + return this; + } + + blank() { + this.value += '\n'; + return this; + } + + li(value: string) { + this.value += `* ${value}\n`; + return this; + } + + table(header: string[], rows: { toString() }[][]) { + let lengths: number[] = []; + header.forEach((cell, ci) => { + lengths[ci] = cell.length; + }); + rows.forEach(row => { + row.forEach((cell, ci) => { + if (!cell) { + cell = row[ci] = '-'; + } + const len = cell.toString().length; + lengths[ci] = Math.max(len, lengths[ci]); + }); + }); + + // header + header.forEach((cell, ci) => { this.value += `| ${cell + repeat(' ', lengths[ci] - cell.toString().length)} `; }); + this.value += '|\n'; + header.forEach((_cell, ci) => { this.value += `| ${repeat('-', lengths[ci])} `; }); + this.value += '|\n'; + + // cells + rows.forEach(row => { + row.forEach((cell, ci) => { + this.value += `| ${cell + repeat(' ', lengths[ci] - cell.toString().length)} `; + }); + this.value += '|\n'; + }); + + } +} diff --git a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts index 70c2887289f..4326e038e0d 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts @@ -773,10 +773,10 @@ class SideBySidePreferencesWidget extends Widget { private settingsTargetsWidget: SettingsTargetsWidget; - private readonly _onFocus: Emitter = new Emitter(); + private readonly _onFocus = new Emitter(); readonly onFocus: Event = this._onFocus.event; - private readonly _onDidSettingsTargetChange: Emitter = new Emitter(); + private readonly _onDidSettingsTargetChange = new Emitter(); readonly onDidSettingsTargetChange: Event = this._onDidSettingsTargetChange.event; private lastFocusedEditor: BaseEditor; diff --git a/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts b/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts index 620c08cf1aa..a723fd672ca 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts @@ -56,10 +56,10 @@ export class UserSettingsRenderer extends Disposable implements IPreferencesRend private modelChangeDelayer: Delayer = new Delayer(200); private associatedPreferencesModel: IPreferencesEditorModel; - private readonly _onFocusPreference: Emitter = new Emitter(); + private readonly _onFocusPreference = new Emitter(); public readonly onFocusPreference: Event = this._onFocusPreference.event; - private readonly _onClearFocusPreference: Emitter = new Emitter(); + private readonly _onClearFocusPreference = new Emitter(); public readonly onClearFocusPreference: Event = this._onClearFocusPreference.event; private readonly _onUpdatePreference: Emitter<{ key: string, value: any, source: IIndexedSetting }> = new Emitter<{ key: string, value: any, source: IIndexedSetting }>(); @@ -236,10 +236,10 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR private readonly _onUpdatePreference: Emitter<{ key: string, value: any, source: IIndexedSetting }> = new Emitter<{ key: string, value: any, source: IIndexedSetting }>(); public readonly onUpdatePreference: Event<{ key: string, value: any, source: IIndexedSetting }> = this._onUpdatePreference.event; - private readonly _onFocusPreference: Emitter = new Emitter(); + private readonly _onFocusPreference = new Emitter(); public readonly onFocusPreference: Event = this._onFocusPreference.event; - private readonly _onClearFocusPreference: Emitter = new Emitter(); + private readonly _onClearFocusPreference = new Emitter(); public readonly onClearFocusPreference: Event = this._onClearFocusPreference.event; constructor(protected editor: ICodeEditor, public readonly preferencesModel: DefaultSettingsEditorModel, @@ -436,7 +436,7 @@ class DefaultSettingsHeaderRenderer extends Disposable { export class SettingsGroupTitleRenderer extends Disposable implements HiddenAreasProvider { - private readonly _onHiddenAreasChanged: Emitter = new Emitter(); + private readonly _onHiddenAreasChanged = new Emitter(); get onHiddenAreasChanged(): Event { return this._onHiddenAreasChanged.event; } private settingsGroups: ISettingsGroup[]; diff --git a/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts b/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts index 218ec5d67f1..3baca020878 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts @@ -467,7 +467,7 @@ export class SettingsTargetsWidget extends Widget { private _settingsTarget: SettingsTarget; - private readonly _onDidTargetChange: Emitter = new Emitter(); + private readonly _onDidTargetChange = new Emitter(); public readonly onDidTargetChange: Event = this._onDidTargetChange.event; constructor( @@ -715,7 +715,7 @@ export class EditPreferenceWidget extends Disposable { private _editPreferenceDecoration: string[]; - private readonly _onClick: Emitter = new Emitter(); + private readonly _onClick = new Emitter(); public get onClick(): Event { return this._onClick.event; } constructor(private editor: ICodeEditor diff --git a/src/vs/workbench/parts/preferences/browser/settingsTree.ts b/src/vs/workbench/parts/preferences/browser/settingsTree.ts index 8c7a761cff1..de3a9807169 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsTree.ts @@ -350,19 +350,19 @@ export class SettingsRenderer implements ITreeRenderer { public static readonly SETTING_KEY_ATTR = 'data-key'; - private readonly _onDidClickOverrideElement: Emitter = new Emitter(); + private readonly _onDidClickOverrideElement = new Emitter(); public readonly onDidClickOverrideElement: Event = this._onDidClickOverrideElement.event; - private readonly _onDidChangeSetting: Emitter = new Emitter(); + private readonly _onDidChangeSetting = new Emitter(); public readonly onDidChangeSetting: Event = this._onDidChangeSetting.event; - private readonly _onDidOpenSettings: Emitter = new Emitter(); + private readonly _onDidOpenSettings = new Emitter(); public readonly onDidOpenSettings: Event = this._onDidOpenSettings.event; - private readonly _onDidClickSettingLink: Emitter = new Emitter(); + private readonly _onDidClickSettingLink = new Emitter(); public readonly onDidClickSettingLink: Event = this._onDidClickSettingLink.event; - private readonly _onDidFocusSetting: Emitter = new Emitter(); + private readonly _onDidFocusSetting = new Emitter(); public readonly onDidFocusSetting: Event = this._onDidFocusSetting.event; private descriptionMeasureContainer: HTMLElement; diff --git a/src/vs/workbench/parts/preferences/browser/settingsWidgets.ts b/src/vs/workbench/parts/preferences/browser/settingsWidgets.ts index c2478da6c80..819e6582f40 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsWidgets.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsWidgets.ts @@ -203,7 +203,7 @@ export class ExcludeSettingWidget extends Disposable { private model = new ExcludeSettingListModel(); - private readonly _onDidChangeExclude: Emitter = new Emitter(); + private readonly _onDidChangeExclude = new Emitter(); public readonly onDidChangeExclude: Event = this._onDidChangeExclude.event; get domNode(): HTMLElement { diff --git a/src/vs/workbench/parts/preferences/electron-browser/media/settingsEditor2.css b/src/vs/workbench/parts/preferences/electron-browser/media/settingsEditor2.css index 11772ecd961..45fec74561c 100644 --- a/src/vs/workbench/parts/preferences/electron-browser/media/settingsEditor2.css +++ b/src/vs/workbench/parts/preferences/electron-browser/media/settingsEditor2.css @@ -80,21 +80,6 @@ padding-top: 3px; } -.settings-editor > .settings-header > .settings-header-controls .settings-header-controls-right { - margin-left: auto; - padding-top: 4px; - display: flex; -} - -.settings-editor > .settings-header > .settings-header-controls .settings-header-controls-right .toolbar-toggle-more { - display: block; - width: 22px; - height: 22px; - background-position: center; - background-repeat: no-repeat; - background-size: 16px; -} - .settings-editor > .settings-header > .settings-header-controls .settings-tabs-widget > .monaco-action-bar .action-item { padding: 0px; /* padding must be on action-label because it has the bottom-border, because that's where the .checked class is */ diff --git a/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts b/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts index 6806ff55ae1..d37a3d0b875 100644 --- a/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts +++ b/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import * as DOM from 'vs/base/browser/dom'; -import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; import * as arrays from 'vs/base/common/arrays'; import { Delayer, ThrottledDelayer } from 'vs/base/common/async'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; @@ -18,7 +17,6 @@ import 'vs/css!./media/settingsEditor2'; import { localize } from 'vs/nls'; import { ConfigurationTarget, ConfigurationTargetToString, IConfigurationOverrides, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { WorkbenchTree } from 'vs/platform/list/browser/listService'; @@ -35,7 +33,7 @@ import { attachSuggestEnabledInputBoxStyler, SuggestEnabledInput } from 'vs/work import { SettingsTarget, SettingsTargetsWidget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets'; import { commonlyUsedData, tocData } from 'vs/workbench/parts/preferences/browser/settingsLayout'; import { ISettingLinkClickEvent, ISettingOverrideClickEvent, resolveExtensionsSettings, resolveSettingsTree, SettingsDataSource, SettingsRenderer, SettingsTree, SimplePagedDataSource } from 'vs/workbench/parts/preferences/browser/settingsTree'; -import { parseQuery, ISettingsEditorViewState, SearchResultIdx, SearchResultModel, SettingsTreeGroupElement, SettingsTreeModel, SettingsTreeSettingElement } from 'vs/workbench/parts/preferences/browser/settingsTreeModels'; +import { ISettingsEditorViewState, parseQuery, SearchResultIdx, SearchResultModel, SettingsTreeGroupElement, SettingsTreeModel, SettingsTreeSettingElement } from 'vs/workbench/parts/preferences/browser/settingsTreeModels'; import { settingsTextInputBorder } from 'vs/workbench/parts/preferences/browser/settingsWidgets'; import { TOCRenderer, TOCTree, TOCTreeModel } from 'vs/workbench/parts/preferences/browser/tocTree'; import { CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, IPreferencesSearchService, ISearchProvider, MODIFIED_SETTING_TAG, SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU } from 'vs/workbench/parts/preferences/common/preferences'; @@ -76,7 +74,6 @@ export class SettingsEditor2 extends BaseEditor { private searchWidget: SuggestEnabledInput; private countElement: HTMLElement; private settingsTargetsWidget: SettingsTargetsWidget; - private toolbar: ToolBar; private settingsTreeContainer: HTMLElement; private settingsTree: Tree; @@ -128,7 +125,6 @@ export class SettingsEditor2 extends BaseEditor { @IPreferencesSearchService private preferencesSearchService: IPreferencesSearchService, @ILogService private logService: ILogService, @IContextKeyService contextKeyService: IContextKeyService, - @IContextMenuService private contextMenuService: IContextMenuService, @IStorageService private storageService: IStorageService, @INotificationService private notificationService: INotificationService, @IEditorGroupsService protected editorGroupService: IEditorGroupsService, @@ -398,8 +394,6 @@ export class SettingsEditor2 extends BaseEditor { this.settingsTargetsWidget = this._register(this.instantiationService.createInstance(SettingsTargetsWidget, targetWidgetContainer)); this.settingsTargetsWidget.settingsTarget = ConfigurationTarget.USER; this.settingsTargetsWidget.onDidTargetChange(target => this.onDidSettingsTargetChange(target)); - - this.createHeaderControls(headerControlsContainer); } private onDidSettingsTargetChange(target: SettingsTarget): void { @@ -409,17 +403,6 @@ export class SettingsEditor2 extends BaseEditor { this.onConfigUpdate(undefined, true); } - private createHeaderControls(parent: HTMLElement): void { - const headerControlsContainerRight = DOM.append(parent, $('.settings-header-controls-right')); - - this.toolbar = this._register(new ToolBar(headerControlsContainerRight, this.contextMenuService, { - ariaLabel: localize('settingsToolbarLabel', "Settings Editor Actions"), - actionRunner: this.actionRunner - })); - - this.toolbar.context = { target: this.settingsTargetsWidget.settingsTarget }; - } - private onDidClickSetting(evt: ISettingLinkClickEvent, recursed?: boolean): void { const elements = this.currentSettingsModel.getElementsByName(evt.targetKey); if (elements && elements[0]) { @@ -1242,8 +1225,3 @@ interface ISettingsEditor2State { searchQuery: string; target: SettingsTarget; } - - -interface ISettingsToolbarContext { - target: SettingsTarget; -} diff --git a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts index 0c9e2165219..926949b2902 100644 --- a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts +++ b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts @@ -15,7 +15,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { List } from 'vs/base/browser/ui/list/listWidget'; import { IListVirtualDelegate, IListRenderer, IListContextMenuEvent, IListEvent, IKeyboardNavigationLabelProvider, IIdentityProvider } from 'vs/base/browser/ui/list/list'; import { VIEWLET_ID, VIEW_CONTAINER } from 'vs/workbench/parts/scm/common/scm'; -import { FileLabel } from 'vs/workbench/browser/labels'; +import { ResourceLabels, IResourceLabel, IResourceLabelsContainer } from 'vs/workbench/browser/labels'; import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge'; import { ISCMService, ISCMRepository, ISCMResourceGroup, ISCMResource, InputValidationType } from 'vs/workbench/services/scm/common/scm'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -444,7 +444,7 @@ class ResourceGroupRenderer implements IListRenderer get templateId(): string { return ResourceRenderer.TEMPLATE_ID; } constructor( + private labels: ResourceLabels, private actionItemProvider: IActionItemProvider, private getSelectedResources: () => ISCMResource[], private themeService: IThemeService, - private instantiationService: IInstantiationService, private menus: SCMMenus ) { } renderTemplate(container: HTMLElement): ResourceTemplate { const element = append(container, $('.resource')); const name = append(element, $('.name')); - const fileLabel = this.instantiationService.createInstance(FileLabel, name, void 0); + const fileLabel = this.labels.create(name); const actionsContainer = append(fileLabel.element, $('.actions')); const actionBar = new ActionBar(actionsContainer, { actionItemProvider: this.actionItemProvider, @@ -728,6 +728,7 @@ export class RepositoryPanel extends ViewletPanel { private inputBox: InputBox; private listContainer: HTMLElement; private list: List; + private listLabels: ResourceLabels; private menus: SCMMenus; private visibilityDisposables: IDisposable[] = []; protected contextKeyService: IContextKeyService; @@ -828,7 +829,7 @@ export class RepositoryPanel extends ViewletPanel { const triggerValidation = () => validationDelayer.trigger(validate); this.inputBox = new InputBox(this.inputBoxContainer, this.contextViewService, { flexibleHeight: true }); - this.inputBox.setEnabled(this.isVisible() && this.isExpanded()); + this.inputBox.setEnabled(this.isBodyVisible()); this.disposables.push(attachInputBoxStyler(this.inputBox, this.themeService)); this.disposables.push(this.inputBox); @@ -869,9 +870,12 @@ export class RepositoryPanel extends ViewletPanel { const actionItemProvider = (action: IAction) => this.getActionItem(action); + this.listLabels = this.instantiationService.createInstance(ResourceLabels, { onDidChangeVisibility: this.onDidChangeBodyVisibility } as IResourceLabelsContainer); + this.disposables.push(this.listLabels); + const renderers = [ new ResourceGroupRenderer(actionItemProvider, this.themeService, this.menus), - new ResourceRenderer(actionItemProvider, () => this.getSelectedResources(), this.themeService, this.instantiationService, this.menus) + new ResourceRenderer(this.listLabels, actionItemProvider, () => this.getSelectedResources(), this.themeService, this.menus) ]; this.list = this.instantiationService.createInstance(WorkbenchList, this.listContainer, delegate, renderers, { @@ -894,6 +898,7 @@ export class RepositoryPanel extends ViewletPanel { this.viewModel.onDidChangeVisibility(this.onDidChangeVisibility, this, this.disposables); this.onDidChangeVisibility(this.viewModel.isVisible()); + this.onDidChangeBodyVisibility(visible => this.inputBox.setEnabled(visible)); } private onDidChangeVisibility(visible: boolean): void { @@ -903,13 +908,6 @@ export class RepositoryPanel extends ViewletPanel { } else { this.visibilityDisposables = dispose(this.visibilityDisposables); } - - this.inputBox.setEnabled(this.isVisible() && this.isExpanded()); - } - - setExpanded(expanded: boolean): void { - super.setExpanded(expanded); - this.inputBox.setEnabled(this.isVisible() && this.isExpanded()); } layoutBody(height: number = this.cachedHeight): void { @@ -1055,9 +1053,6 @@ export class SCMViewlet extends PanelViewlet implements IViewModel, IViewsViewle private _onDidSplice = new Emitter>(); readonly onDidSplice: Event> = this._onDidSplice.event; - private _onDidChangeVisibility = new Emitter(); - readonly onDidChangeVisibility: Event = this._onDidChangeVisibility.event; - private _height: number | undefined = undefined; get height(): number | undefined { return this._height; } @@ -1205,14 +1200,14 @@ export class SCMViewlet extends PanelViewlet implements IViewModel, IViewsViewle this.cachedMainPanelHeight = this.getPanelSize(this.mainPanel); } - this._onDidChangeVisibility.fire(visible); - const start = this.getContributedViewsStartIndex(); for (let i = 0; i < this.contributedViews.visibleViewDescriptors.length; i++) { const panel = this.panels[start + i] as ViewletPanel; panel.setVisible(visible); } + + this.repositoryPanels.forEach(panel => panel.setVisible(visible)); } getOptimalWidth(): number { @@ -1296,6 +1291,7 @@ export class SCMViewlet extends PanelViewlet implements IViewModel, IViewsViewle .map((r, index) => { const panel = this.instantiationService.createInstance(RepositoryPanel, `scm.repository.${r.provider.label}.${index}`, r, this); panel.render(); + panel.setVisible(true); return panel; }); diff --git a/src/vs/workbench/parts/search/browser/media/searchview.css b/src/vs/workbench/parts/search/browser/media/searchview.css index c8c9203cac2..614c1dd30d5 100644 --- a/src/vs/workbench/parts/search/browser/media/searchview.css +++ b/src/vs/workbench/parts/search/browser/media/searchview.css @@ -240,10 +240,6 @@ font-style: italic; } -.search-view .query-clear { - background: url("action-query-clear.svg") center center no-repeat; -} - .search-view.wide .monaco-list .monaco-list-row .foldermatch .actionBarContainer, .search-view.wide .monaco-list .monaco-list-row .filematch .actionBarContainer, .search-view .monaco-list .monaco-list-row .linematch .actionBarContainer { diff --git a/src/vs/workbench/parts/search/browser/searchResultsView.ts b/src/vs/workbench/parts/search/browser/searchResultsView.ts index 56c778b4289..87f7ee2457c 100644 --- a/src/vs/workbench/parts/search/browser/searchResultsView.ts +++ b/src/vs/workbench/parts/search/browser/searchResultsView.ts @@ -23,7 +23,7 @@ import { ISearchConfigurationProperties } from 'vs/platform/search/common/search import { attachBadgeStyler } from 'vs/platform/theme/common/styler'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { FileLabel } from 'vs/workbench/browser/labels'; +import { IResourceLabel, ResourceLabels } from 'vs/workbench/browser/labels'; import { RemoveAction, ReplaceAction, ReplaceAllAction, ReplaceAllInFolderAction } from 'vs/workbench/parts/search/browser/searchActions'; import { SearchView } from 'vs/workbench/parts/search/browser/searchView'; import { FileMatch, FolderMatch, Match, RenderableMatch, searchMatchComparer, SearchModel } from 'vs/workbench/parts/search/common/searchModel'; @@ -35,14 +35,14 @@ export class SearchSorter implements ISorter { } interface IFolderMatchTemplate { - label: FileLabel; + label: IResourceLabel; badge: CountBadge; actions: ActionBar; } interface IFileMatchTemplate { el: HTMLElement; - label: FileLabel; + label: IResourceLabel; badge: CountBadge; actions: ActionBar; } @@ -84,6 +84,7 @@ export class FolderMatchRenderer extends Disposable implements ITreeRenderer; + private treeLabels: ResourceLabels; private viewletState: object; private globalMemento: object; private messagesElement: HTMLElement; @@ -335,6 +337,32 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { this._register(this.onDidFocus(() => this.viewletFocused.set(true))); this._register(this.onDidBlur(() => this.viewletFocused.set(false))); + + this._register(this.onDidChangeVisibility(visible => this.onVisibilityChanged(visible))); + } + + private onVisibilityChanged(visible: boolean): void { + this.viewletVisible.set(visible); + if (visible) { + if (this.changedWhileHidden) { + // Render if results changed while viewlet was hidden - #37818 + this.refreshAndUpdateCount(); + this.changedWhileHidden = false; + } + } + + // Enable highlights if there are searchresults + if (this.viewModel) { + this.viewModel.searchResult.toggleHighlights(visible); + } + + // Open focused element from results in case the editor area is otherwise empty + if (visible && !this.editorService.activeEditor) { + let focus = this.tree.getFocus(); + if (focus) { + this.onFocus(focus, true); + } + } } public get searchAndReplaceWidget(): SearchWidget { @@ -584,18 +612,19 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { } }; - this.tree = >this.instantiationService.createInstance(WorkbenchObjectTree, + this.treeLabels = this._register(this.instantiationService.createInstance(ResourceLabels, this)); + this.tree = this._register(>this.instantiationService.createInstance(WorkbenchObjectTree, this.resultsElement, delegate, [ - this._register(this.instantiationService.createInstance(FolderMatchRenderer, this.viewModel, this)), - this._register(this.instantiationService.createInstance(FileMatchRenderer, this.viewModel, this)), + this._register(this.instantiationService.createInstance(FolderMatchRenderer, this.viewModel, this, this.treeLabels)), + this._register(this.instantiationService.createInstance(FileMatchRenderer, this.viewModel, this, this.treeLabels)), this._register(this.instantiationService.createInstance(MatchRenderer, this.viewModel, this)), ], { identityProvider, accessibilityProvider: this.instantiationService.createInstance(SearchAccessibilityProvider, this.viewModel) - }); + })); this._register(this.tree.onContextMenu(e => this.onContextMenu(e))); const resourceNavigator = this._register(new TreeResourceNavigator2(this.tree, { openOnFocus: true })); @@ -742,32 +771,6 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { } } - public setVisible(visible: boolean): void { - this.viewletVisible.set(visible); - if (visible) { - if (this.changedWhileHidden) { - // Render if results changed while viewlet was hidden - #37818 - this.refreshAndUpdateCount(); - this.changedWhileHidden = false; - } - } - - super.setVisible(visible); - - // Enable highlights if there are searchresults - if (this.viewModel) { - this.viewModel.searchResult.toggleHighlights(visible); - } - - // Open focused element from results in case the editor area is otherwise empty - if (visible && !this.editorService.activeEditor) { - let focus = this.tree.getFocus(); - if (focus) { - this.onFocus(focus, true); - } - } - } - public moveFocusToResults(): void { this.tree.domFocus(); } diff --git a/src/vs/workbench/parts/tasks/common/problemMatcher.ts b/src/vs/workbench/parts/tasks/common/problemMatcher.ts index 343943893e7..6da519a9154 100644 --- a/src/vs/workbench/parts/tasks/common/problemMatcher.ts +++ b/src/vs/workbench/parts/tasks/common/problemMatcher.ts @@ -146,7 +146,7 @@ export interface NamedMultiLineProblemPattern { patterns: MultiLineProblemPattern; } -export function isNamedProblemMatcher(value: ProblemMatcher): value is NamedProblemMatcher { +export function isNamedProblemMatcher(value: ProblemMatcher | undefined): value is NamedProblemMatcher { return value && Types.isString((value).name) ? true : false; } @@ -1286,10 +1286,10 @@ export class ProblemMatcherParser extends Parser { super(logger); } - public parse(json: Config.ProblemMatcher): ProblemMatcher | null { + public parse(json: Config.ProblemMatcher): ProblemMatcher | undefined { let result = this.createProblemMatcher(json); if (!this.checkProblemMatcherValid(json, result)) { - return null; + return undefined; } this.addWatchingMatcher(json, result); diff --git a/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.ts b/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.ts index 09f5e13d159..daec033c515 100644 --- a/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.ts +++ b/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.ts @@ -12,6 +12,7 @@ import * as Objects from 'vs/base/common/objects'; import { ExtensionsRegistry, ExtensionMessageCollector } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import * as Tasks from 'vs/workbench/parts/tasks/common/tasks'; +import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; const taskDefinitionSchema: IJSONSchema = { @@ -45,7 +46,7 @@ namespace Configuration { properties?: IJSONSchemaMap; } - export function from(value: TaskDefinition, extensionId: string, messageCollector: ExtensionMessageCollector): Tasks.TaskDefinition | undefined { + export function from(value: TaskDefinition, extensionId: CanonicalExtensionIdentifier, messageCollector: ExtensionMessageCollector): Tasks.TaskDefinition | undefined { if (!value) { return undefined; } @@ -62,7 +63,7 @@ namespace Configuration { } } } - return { extensionId, taskType, required: required, properties: value.properties ? Objects.deepClone(value.properties) : {} }; + return { extensionId: extensionId.value, taskType, required: required, properties: value.properties ? Objects.deepClone(value.properties) : {} }; } } @@ -98,7 +99,7 @@ class TaskDefinitionRegistryImpl implements ITaskDefinitionRegistry { for (let extension of extensions) { let taskTypes = extension.value; for (let taskType of taskTypes) { - let type = Configuration.from(taskType, extension.description.id, extension.collector); + let type = Configuration.from(taskType, extension.description.identifier, extension.collector); if (type) { this.taskTypes[type.taskType] = type; } diff --git a/src/vs/workbench/parts/tasks/common/tasks.ts b/src/vs/workbench/parts/tasks/common/tasks.ts index 0848306c93a..5aed91f7678 100644 --- a/src/vs/workbench/parts/tasks/common/tasks.ts +++ b/src/vs/workbench/parts/tasks/common/tasks.ts @@ -218,7 +218,7 @@ export interface PresentationOptions { export namespace PresentationOptions { export const defaults: PresentationOptions = { - echo: true, reveal: RevealKind.Always, focus: false, panel: PanelKind.Shared, showReuseMessage: true, clear: false + echo: false, reveal: RevealKind.Always, focus: false, panel: PanelKind.Shared, showReuseMessage: true, clear: false }; } @@ -262,12 +262,12 @@ export interface CommandConfiguration { /** * The task type */ - runtime: RuntimeType; + runtime?: RuntimeType; /** * The command to execute */ - name: CommandString; + name?: CommandString; /** * Additional command options. @@ -293,7 +293,7 @@ export interface CommandConfiguration { /** * Describes how the task is presented in the UI. */ - presentation: PresentationOptions; + presentation?: PresentationOptions; } export namespace TaskGroup { @@ -372,7 +372,7 @@ export interface KeyedTaskIdentifier extends TaskIdentifier { export interface TaskDependency { workspaceFolder: IWorkspaceFolder; - task: string | KeyedTaskIdentifier; + task: string | KeyedTaskIdentifier | undefined; } export const enum GroupType { @@ -469,10 +469,12 @@ export abstract class CommonTask { private _taskLoadMessages: string[] | undefined; - protected constructor(id: string, label: string, type, runOptions: RunOptions, + protected constructor(id: string, label: string | undefined, type, runOptions: RunOptions, configurationProperties: ConfigurationProperties, source: BaseTaskSource) { this._id = id; - this._label = label; + if (label) { + this._label = label; + } if (type) { this.type = type; } @@ -494,9 +496,11 @@ export abstract class CommonTask { } public clone(): Task { - return Objects.assign({}, this); + return this.fromObject(Objects.assign({}, this)); } + protected abstract fromObject(object: any): Task; + public getWorkspaceFolder(): IWorkspaceFolder | undefined { return undefined; } @@ -563,12 +567,14 @@ export class CustomTask extends CommonTask { */ command: CommandConfiguration; - public constructor(id: string, source: WorkspaceTaskSource, label: string, type, command: CommandConfiguration, + public constructor(id: string, source: WorkspaceTaskSource, label: string, type, command: CommandConfiguration | undefined, hasDefinedMatchers: boolean, runOptions: RunOptions, configurationProperties: ConfigurationProperties) { super(id, label, undefined, runOptions, configurationProperties, source); this._source = source; this.hasDefinedMatchers = hasDefinedMatchers; - this.command = command; + if (command) { + this.command = command; + } } public customizes(): KeyedTaskIdentifier | undefined { @@ -631,6 +637,10 @@ export class CustomTask extends CommonTask { return 'workspace'; } } + + protected fromObject(object: CustomTask): CustomTask { + return new CustomTask(object._id, object._source, object._label, object.type, object.command, object.hasDefinedMatchers, object.runOptions, object.configurationProperties); + } } export class ConfiguringTask extends CommonTask { @@ -642,7 +652,7 @@ export class ConfiguringTask extends CommonTask { configures: KeyedTaskIdentifier; - public constructor(id: string, source: WorkspaceTaskSource, label: string, type, + public constructor(id: string, source: WorkspaceTaskSource, label: string | undefined, type, configures: KeyedTaskIdentifier, runOptions: RunOptions, configurationProperties: ConfigurationProperties) { super(id, label, type, runOptions, configurationProperties, source); this._source = source; @@ -652,6 +662,11 @@ export class ConfiguringTask extends CommonTask { public static is(value: any): value is ConfiguringTask { return value instanceof ConfiguringTask; } + + protected fromObject(object: any): Task { + return object; + } + } export class ContributedTask extends CommonTask { @@ -716,6 +731,10 @@ export class ContributedTask extends CommonTask { public getTelemetryKind(): string { return 'extension'; } + + protected fromObject(object: ContributedTask): ContributedTask { + return new ContributedTask(object._id, object._source, object._label, object.type, object.defines, object.command, object.hasDefinedMatchers, object.runOptions, object.configurationProperties); + } } export class InMemoryTask extends CommonTask { @@ -739,6 +758,10 @@ export class InMemoryTask extends CommonTask { public getTelemetryKind(): string { return 'composite'; } + + protected fromObject(object: InMemoryTask): InMemoryTask { + return new InMemoryTask(object._id, object._source, object._label, object.type, object.runOptions, object.configurationProperties); + } } export type Task = CustomTask | ContributedTask | InMemoryTask; @@ -842,8 +865,7 @@ export const enum TaskRunSource { } export namespace TaskEvent { - export function create(kind: TaskEventKind.ProcessStarted, task: Task, processId: number): TaskEvent; - export function create(kind: TaskEventKind.ProcessEnded, task: Task, exitCode: number): TaskEvent; + export function create(kind: TaskEventKind.ProcessStarted | TaskEventKind.ProcessEnded, task: Task, processIdOrExitCode: number): TaskEvent; export function create(kind: TaskEventKind.Start | TaskEventKind.Active | TaskEventKind.Inactive | TaskEventKind.Terminated | TaskEventKind.End, task: Task): TaskEvent; export function create(kind: TaskEventKind.Changed): TaskEvent; export function create(kind: TaskEventKind, task?: Task, processIdOrExitCode?: number): TaskEvent { diff --git a/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.ts b/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.ts index ad4de0f0058..2c4b0fbc2d6 100644 --- a/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.ts +++ b/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v1.ts @@ -63,10 +63,10 @@ const shellCommand: IJSONSchema = { }; schema.definitions = Objects.deepClone(commonSchema.definitions); -let definitions = schema.definitions; -definitions['commandConfiguration']['properties']['isShellCommand'] = Objects.deepClone(shellCommand); -definitions['taskDescription']['properties']['isShellCommand'] = Objects.deepClone(shellCommand); -definitions['taskRunnerConfiguration']['properties']['isShellCommand'] = Objects.deepClone(shellCommand); +let definitions = schema.definitions!; +definitions['commandConfiguration']['properties']!['isShellCommand'] = Objects.deepClone(shellCommand); +definitions['taskDescription']['properties']!['isShellCommand'] = Objects.deepClone(shellCommand); +definitions['taskRunnerConfiguration']['properties']!['isShellCommand'] = Objects.deepClone(shellCommand); Object.getOwnPropertyNames(definitions).forEach(key => { let newKey = key + '1'; @@ -94,8 +94,8 @@ fixReferences(schema); ProblemMatcherRegistry.onReady().then(() => { try { let matcherIds = ProblemMatcherRegistry.keys().map(key => '$' + key); - definitions.problemMatcherType1.oneOf[0].enum = matcherIds; - (definitions.problemMatcherType1.oneOf[2].items as IJSONSchema).anyOf[1].enum = matcherIds; + definitions.problemMatcherType1.oneOf![0].enum = matcherIds; + (definitions.problemMatcherType1.oneOf![2].items as IJSONSchema).anyOf![1].enum = matcherIds; } catch (err) { console.log('Installing problem matcher ids failed'); } diff --git a/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.ts b/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.ts index ad79e9809b9..fdc154f2ddb 100644 --- a/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.ts +++ b/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.ts @@ -300,8 +300,10 @@ const runOptions: IJSONSchema = { description: nls.localize('JsonSchema.tasks.runOptions', 'The task\'s run related options') }; -const options: IJSONSchema = Objects.deepClone(commonSchema.definitions.options); -options.properties.shell = Objects.deepClone(commonSchema.definitions.shellConfiguration); +const commonSchemaDefinitions = commonSchema.definitions!; +const options: IJSONSchema = Objects.deepClone(commonSchemaDefinitions.options); +const optionsProperties = options.properties!; +optionsProperties.shell = Objects.deepClone(commonSchemaDefinitions.shellConfiguration); let taskConfiguration: IJSONSchema = { type: 'object', @@ -342,8 +344,9 @@ let taskDefinitions: IJSONSchema[] = []; TaskDefinitionRegistry.onReady().then(() => { for (let taskType of TaskDefinitionRegistry.all()) { let schema: IJSONSchema = Objects.deepClone(taskConfiguration); + const schemaProperties = schema.properties!; // Since we do this after the schema is assigned we need to patch the refs. - schema.properties.type = { + schemaProperties.type = { type: 'string', description: nls.localize('JsonSchema.customizations.customizes.type', 'The task type to customize'), enum: [taskType.taskType] @@ -354,7 +357,7 @@ TaskDefinitionRegistry.onReady().then(() => { if (taskType.properties) { for (let key of Object.keys(taskType.properties)) { let property = taskType.properties[key]; - schema.properties[key] = Objects.deepClone(property); + schemaProperties[key] = Objects.deepClone(property); } } fixReferences(schema); @@ -363,27 +366,28 @@ TaskDefinitionRegistry.onReady().then(() => { }); let customize = Objects.deepClone(taskConfiguration); -customize.properties.customize = { +customize.properties!.customize = { type: 'string', deprecationMessage: nls.localize('JsonSchema.tasks.customize.deprecated', 'The customize property is deprecated. See the 1.14 release notes on how to migrate to the new task customization approach') }; taskDefinitions.push(customize); -let definitions = Objects.deepClone(commonSchema.definitions); +let definitions = Objects.deepClone(commonSchemaDefinitions); let taskDescription: IJSONSchema = definitions.taskDescription; taskDescription.required = ['label']; -taskDescription.properties.label = Objects.deepClone(label); -taskDescription.properties.command = Objects.deepClone(command); -taskDescription.properties.args = Objects.deepClone(args); -taskDescription.properties.isShellCommand = Objects.deepClone(shellCommand); -taskDescription.properties.dependsOn = dependsOn; -taskDescription.properties.identifier = Objects.deepClone(identifier); -taskDescription.properties.type = Objects.deepClone(taskType); -taskDescription.properties.presentation = Objects.deepClone(presentation); -taskDescription.properties.terminal = terminal; -taskDescription.properties.group = Objects.deepClone(group); -taskDescription.properties.runOptions = Objects.deepClone(runOptions); -taskDescription.properties.taskName.deprecationMessage = nls.localize( +const taskDescriptionProperties = taskDescription.properties!; +taskDescriptionProperties.label = Objects.deepClone(label); +taskDescriptionProperties.command = Objects.deepClone(command); +taskDescriptionProperties.args = Objects.deepClone(args); +taskDescriptionProperties.isShellCommand = Objects.deepClone(shellCommand); +taskDescriptionProperties.dependsOn = dependsOn; +taskDescriptionProperties.identifier = Objects.deepClone(identifier); +taskDescriptionProperties.type = Objects.deepClone(taskType); +taskDescriptionProperties.presentation = Objects.deepClone(presentation); +taskDescriptionProperties.terminal = terminal; +taskDescriptionProperties.group = Objects.deepClone(group); +taskDescriptionProperties.runOptions = Objects.deepClone(runOptions); +taskDescriptionProperties.taskName.deprecationMessage = nls.localize( 'JsonSchema.tasks.taskName.deprecated', 'The task\'s name property is deprecated. Use the label property instead.' ); @@ -397,19 +401,19 @@ definitions.showOutputType.deprecationMessage = nls.localize( 'JsonSchema.tasks.showOputput.deprecated', 'The property showOutput is deprecated. Use the reveal property inside the presentation property instead. See also the 1.14 release notes.' ); -definitions.taskDescription.properties.echoCommand.deprecationMessage = nls.localize( +taskDescriptionProperties.echoCommand.deprecationMessage = nls.localize( 'JsonSchema.tasks.echoCommand.deprecated', 'The property echoCommand is deprecated. Use the echo property inside the presentation property instead. See also the 1.14 release notes.' ); -definitions.taskDescription.properties.suppressTaskName.deprecationMessage = nls.localize( +taskDescriptionProperties.suppressTaskName.deprecationMessage = nls.localize( 'JsonSchema.tasks.suppressTaskName.deprecated', 'The property suppressTaskName is deprecated. Inline the command with its arguments into the task instead. See also the 1.14 release notes.' ); -definitions.taskDescription.properties.isBuildCommand.deprecationMessage = nls.localize( +taskDescriptionProperties.isBuildCommand.deprecationMessage = nls.localize( 'JsonSchema.tasks.isBuildCommand.deprecated', 'The property isBuildCommand is deprecated. Use the group property instead. See also the 1.14 release notes.' ); -definitions.taskDescription.properties.isTestCommand.deprecationMessage = nls.localize( +taskDescriptionProperties.isTestCommand.deprecationMessage = nls.localize( 'JsonSchema.tasks.isTestCommand.deprecated', 'The property isTestCommand is deprecated. Use the group property instead. See also the 1.14 release notes.' ); @@ -419,37 +423,38 @@ taskDefinitions.push({ $ref: '#/definitions/taskDescription' } as IJSONSchema); -let tasks = definitions.taskRunnerConfiguration.properties.tasks; +const definitionsTaskRunnerConfigurationProperties = definitions.taskRunnerConfiguration.properties!; +let tasks = definitionsTaskRunnerConfigurationProperties.tasks; tasks.items = { oneOf: taskDefinitions }; -definitions.taskRunnerConfiguration.properties.inputs = inputsSchema.definitions.inputs; +definitionsTaskRunnerConfigurationProperties.inputs = inputsSchema.definitions!.inputs; -definitions.commandConfiguration.properties.isShellCommand = Objects.deepClone(shellCommand); -definitions.options.properties.shell = { +definitions.commandConfiguration.properties!.isShellCommand = Objects.deepClone(shellCommand); +definitions.options.properties!.shell = { $ref: '#/definitions/shellConfiguration' }; -definitions.taskRunnerConfiguration.properties.isShellCommand = Objects.deepClone(shellCommand); -definitions.taskRunnerConfiguration.properties.type = Objects.deepClone(taskType); -definitions.taskRunnerConfiguration.properties.group = Objects.deepClone(group); -definitions.taskRunnerConfiguration.properties.presentation = Objects.deepClone(presentation); -definitions.taskRunnerConfiguration.properties.suppressTaskName.deprecationMessage = nls.localize( +definitionsTaskRunnerConfigurationProperties.isShellCommand = Objects.deepClone(shellCommand); +definitionsTaskRunnerConfigurationProperties.type = Objects.deepClone(taskType); +definitionsTaskRunnerConfigurationProperties.group = Objects.deepClone(group); +definitionsTaskRunnerConfigurationProperties.presentation = Objects.deepClone(presentation); +definitionsTaskRunnerConfigurationProperties.suppressTaskName.deprecationMessage = nls.localize( 'JsonSchema.tasks.suppressTaskName.deprecated', 'The property suppressTaskName is deprecated. Inline the command with its arguments into the task instead. See also the 1.14 release notes.' ); -definitions.taskRunnerConfiguration.properties.taskSelector.deprecationMessage = nls.localize( +definitionsTaskRunnerConfigurationProperties.taskSelector.deprecationMessage = nls.localize( 'JsonSchema.tasks.taskSelector.deprecated', 'The property taskSelector is deprecated. Inline the command with its arguments into the task instead. See also the 1.14 release notes.' ); let osSpecificTaskRunnerConfiguration = Objects.deepClone(definitions.taskRunnerConfiguration); -delete osSpecificTaskRunnerConfiguration.properties.tasks; +delete osSpecificTaskRunnerConfiguration.properties!.tasks; osSpecificTaskRunnerConfiguration.additionalProperties = false; definitions.osSpecificTaskRunnerConfiguration = osSpecificTaskRunnerConfiguration; -definitions.taskRunnerConfiguration.properties.version = Objects.deepClone(version); +definitionsTaskRunnerConfigurationProperties.version = Objects.deepClone(version); const schema: IJSONSchema = { oneOf: [ @@ -485,9 +490,10 @@ const schema: IJSONSchema = { schema.definitions = definitions; function deprecatedVariableMessage(schemaMap: IJSONSchemaMap, property: string) { - if (schemaMap[property].properties) { - Object.keys(schemaMap[property].properties).forEach(name => { - deprecatedVariableMessage(schemaMap[property].properties, name); + const mapAtProperty = schemaMap[property].properties!; + if (mapAtProperty) { + Object.keys(mapAtProperty).forEach(name => { + deprecatedVariableMessage(mapAtProperty, name); }); } else { ConfigurationResolverUtils.applyDeprecatedVariableMessage(schemaMap[property]); @@ -505,8 +511,8 @@ fixReferences(schema); ProblemMatcherRegistry.onReady().then(() => { try { let matcherIds = ProblemMatcherRegistry.keys().map(key => '$' + key); - definitions.problemMatcherType2.oneOf[0].enum = matcherIds; - (definitions.problemMatcherType2.oneOf[2].items as IJSONSchema).anyOf[1].enum = matcherIds; + definitions.problemMatcherType2.oneOf![0].enum = matcherIds; + (definitions.problemMatcherType2.oneOf![2].items as IJSONSchema).anyOf![1].enum = matcherIds; } catch (err) { console.log('Installing problem matcher ids failed'); } diff --git a/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts b/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts index 288bd831094..28201789f17 100644 --- a/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts +++ b/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts @@ -1043,8 +1043,7 @@ class TaskService extends Disposable implements ITaskService { }; let content = [ '{', - '\t// See https://go.microsoft.com/fwlink/?LinkId=733558', - '\t// for the documentation about the tasks.json format', + nls.localize('tasksJsonComment', '\t// See https://go.microsoft.com/fwlink/?LinkId=733558 \n\t// for the documentation about the tasks.json format'), ].join('\n') + JSON.stringify(value, null, '\t').substr(1); let editorConfig = this.configurationService.getValue(); if (editorConfig.editor.insertSpaces) { diff --git a/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts b/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts index 468d130372c..749cf499a93 100644 --- a/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts +++ b/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts @@ -85,12 +85,16 @@ export class VerifiedTask { } public verify(): boolean { - return this.trigger && this.resolvedVariables && this.workspaceFolder && (this.shellLaunchConfig !== undefined); + let verified = false; + if (this.trigger && this.resolvedVariables && this.workspaceFolder && (this.shellLaunchConfig !== undefined)) { + verified = true; + } + return verified; } public getVerifiedTask(): { task: Task, resolver: ITaskResolver, trigger: string, resolvedVariables: ResolvedVariables, systemInfo: TaskSystemInfo, workspaceFolder: IWorkspaceFolder, shellLaunchConfig: IShellLaunchConfig } { if (this.verify()) { - return { task: this.task, resolver: this.resolver, trigger: this.trigger, resolvedVariables: this.resolvedVariables, systemInfo: this.systemInfo, workspaceFolder: this.workspaceFolder, shellLaunchConfig: this.shellLaunchConfig }; + return { task: this.task, resolver: this.resolver, trigger: this.trigger, resolvedVariables: this.resolvedVariables!, systemInfo: this.systemInfo!, workspaceFolder: this.workspaceFolder!, shellLaunchConfig: this.shellLaunchConfig! }; } else { throw new Error('VerifiedTask was not checked. verify must be checked before getVerifiedTask.'); } @@ -188,15 +192,15 @@ export class TerminalTaskSystem implements ITaskSystem { let reveal = RevealKind.Always; let focus = false; if (CustomTask.is(task) || ContributedTask.is(task)) { - reveal = task.command.presentation.reveal; - focus = task.command.presentation.focus; + reveal = task.command.presentation!.reveal; + focus = task.command.presentation!.focus; } if (reveal === RevealKind.Always || focus) { this.terminalService.setActiveInstance(terminalData.terminal); this.terminalService.showPanel(focus); } this.lastTask = this.currentTask; - return { kind: TaskExecuteKind.Active, task, active: { same: true, background: task.configurationProperties.isBackground }, promise: terminalData.promise }; + return { kind: TaskExecuteKind.Active, task, active: { same: true, background: task.configurationProperties.isBackground! }, promise: terminalData.promise }; } try { @@ -240,7 +244,7 @@ export class TerminalTaskSystem implements ITaskSystem { } this.terminalService.setActiveInstance(terminalData.terminal); if (CustomTask.is(task) || ContributedTask.is(task)) { - this.terminalService.showPanel(task.command.presentation.focus); + this.terminalService.showPanel(task.command.presentation!.focus); } return true; } @@ -309,7 +313,7 @@ export class TerminalTaskSystem implements ITaskSystem { let promises: Promise[] = []; if (task.configurationProperties.dependsOn) { task.configurationProperties.dependsOn.forEach((dependency) => { - let task = resolver.resolve(dependency.workspaceFolder, dependency.task); + let task = resolver.resolve(dependency.workspaceFolder, dependency.task!); if (task) { let key = task.getMapKey(); let promise = this.activeTasks[key] ? this.activeTasks[key].promise : undefined; @@ -353,7 +357,7 @@ export class TerminalTaskSystem implements ITaskSystem { } } - private resolveVariablesFromSet(taskSystemInfo: TaskSystemInfo, workspaceFolder: IWorkspaceFolder, task: CustomTask | ContributedTask, variables: Set): Promise { + private resolveVariablesFromSet(taskSystemInfo: TaskSystemInfo | undefined, workspaceFolder: IWorkspaceFolder, task: CustomTask | ContributedTask, variables: Set): Promise { let isProcess = task.command && task.command.runtime === RuntimeType.Process; let options = task.command && task.command.options ? task.command.options : undefined; let cwd = options ? options.cwd : undefined; @@ -376,7 +380,7 @@ export class TerminalTaskSystem implements ITaskSystem { }; if (taskSystemInfo.platform === Platform.Platform.Windows && isProcess) { - resolveSet.process = { name: CommandString.value(task.command.name) }; + resolveSet.process = { name: CommandString.value(task.command.name!) }; if (cwd) { resolveSet.process.cwd = cwd; } @@ -386,7 +390,7 @@ export class TerminalTaskSystem implements ITaskSystem { } resolvedVariables = taskSystemInfo.resolveVariables(workspaceFolder, resolveSet).then(resolved => { if ((taskSystemInfo.platform !== Platform.Platform.Windows) && isProcess) { - resolved.variables.set(TerminalTaskSystem.ProcessVarName, CommandString.value(task.command.name)); + resolved.variables.set(TerminalTaskSystem.ProcessVarName, CommandString.value(task.command.name!)); } return Promise.resolve(resolved); }); @@ -402,12 +406,12 @@ export class TerminalTaskSystem implements ITaskSystem { let processVarValue: string; if (Platform.isWindows) { processVarValue = win32.findExecutable( - this.configurationResolverService.resolve(workspaceFolder, CommandString.value(task.command.name)), + this.configurationResolverService.resolve(workspaceFolder, CommandString.value(task.command.name!)), cwd ? this.configurationResolverService.resolve(workspaceFolder, cwd) : undefined, envPath ? envPath.split(path.delimiter).map(p => this.configurationResolverService.resolve(workspaceFolder, p)) : undefined ); } else { - processVarValue = this.configurationResolverService.resolve(workspaceFolder, CommandString.value(task.command.name)); + processVarValue = this.configurationResolverService.resolve(workspaceFolder, CommandString.value(task.command.name!)); } resolvedVariablesMap.set(TerminalTaskSystem.ProcessVarName, processVarValue); } @@ -433,12 +437,12 @@ export class TerminalTaskSystem implements ITaskSystem { let variables = new Set(); this.collectTaskVariables(variables, task); - const resolvedVariables = this.resolveVariablesFromSet(this.currentTask.systemInfo, this.currentTask.workspaceFolder, task, variables); + const resolvedVariables = this.resolveVariablesFromSet(this.currentTask.systemInfo, this.currentTask.workspaceFolder!, task, variables); return resolvedVariables.then((resolvedVariables) => { if (resolvedVariables) { this.currentTask.resolvedVariables = resolvedVariables; - return this.executeInTerminal(task, trigger, new VariableResolver(this.currentTask.workspaceFolder, this.currentTask.systemInfo, resolvedVariables.variables, this.configurationResolverService)); + return this.executeInTerminal(task, trigger, new VariableResolver(this.currentTask.workspaceFolder!, this.currentTask.systemInfo, resolvedVariables.variables, this.configurationResolverService)); } else { return Promise.resolve({ exitCode: 0 }); } @@ -482,7 +486,7 @@ export class TerminalTaskSystem implements ITaskSystem { promise = new Promise((resolve, reject) => { const problemMatchers = this.resolveMatchers(resolver, task.configurationProperties.problemMatchers); let watchingProblemMatcher = new WatchingProblemCollector(problemMatchers, this.markerService, this.modelService); - let toDispose: IDisposable[] = []; + let toDispose: IDisposable[] | undefined = []; let eventCounter: number = 0; toDispose.push(watchingProblemMatcher.onDidStateChange((event) => { if (event.kind === ProblemCollectorEventKind.BackgroundProcessingBegins) { @@ -492,16 +496,17 @@ export class TerminalTaskSystem implements ITaskSystem { eventCounter--; this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Inactive, task)); if (eventCounter === 0) { - let reveal = task.command.presentation.reveal; - if ((reveal === RevealKind.Silent) && (watchingProblemMatcher.numberOfMatches > 0) && (watchingProblemMatcher.maxMarkerSeverity >= MarkerSeverity.Error)) { - this.terminalService.setActiveInstance(terminal); + let reveal = task.command.presentation!.reveal; + if ((reveal === RevealKind.Silent) && (watchingProblemMatcher.numberOfMatches > 0) && watchingProblemMatcher.maxMarkerSeverity && + (watchingProblemMatcher.maxMarkerSeverity >= MarkerSeverity.Error)) { + this.terminalService.setActiveInstance(terminal!); this.terminalService.showPanel(false); } } } })); watchingProblemMatcher.aboutToStart(); - let delayer: Async.Delayer = undefined; + let delayer: Async.Delayer | undefined = undefined; [terminal, executedCommand, error] = this.createTerminal(task, resolver); if (error || !terminal) { return; @@ -509,7 +514,7 @@ export class TerminalTaskSystem implements ITaskSystem { let processStartedSignaled = false; terminal.processReady.then(() => { if (!processStartedSignaled) { - this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.ProcessStarted, task, terminal.processId)); + this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.ProcessStarted, task, terminal!.processId!)); processStartedSignaled = true; } }, (_error) => { @@ -533,24 +538,25 @@ export class TerminalTaskSystem implements ITaskSystem { let key = task.getMapKey(); delete this.activeTasks[key]; this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Changed)); - switch (task.command.presentation.panel) { + switch (task.command.presentation!.panel) { case PanelKind.Dedicated: - this.sameTaskTerminals[key] = terminal.id.toString(); + this.sameTaskTerminals[key] = terminal!.id.toString(); break; case PanelKind.Shared: - this.idleTaskTerminals.set(key, terminal.id.toString(), Touch.AsOld); + this.idleTaskTerminals.set(key, terminal!.id.toString(), Touch.AsOld); break; } - let reveal = task.command.presentation.reveal; - if ((reveal === RevealKind.Silent) && ((exitCode !== 0) || (watchingProblemMatcher.numberOfMatches > 0) && (watchingProblemMatcher.maxMarkerSeverity >= MarkerSeverity.Error))) { - this.terminalService.setActiveInstance(terminal); + let reveal = task.command.presentation!.reveal; + if ((reveal === RevealKind.Silent) && ((exitCode !== 0) || (watchingProblemMatcher.numberOfMatches > 0) && watchingProblemMatcher.maxMarkerSeverity && + (watchingProblemMatcher.maxMarkerSeverity >= MarkerSeverity.Error))) { + this.terminalService.setActiveInstance(terminal!); this.terminalService.showPanel(false); } watchingProblemMatcher.done(); watchingProblemMatcher.dispose(); - registeredLinkMatchers.forEach(handle => terminal.deregisterLinkMatcher(handle)); + registeredLinkMatchers.forEach(handle => terminal!.deregisterLinkMatcher(handle)); if (!processStartedSignaled) { - this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.ProcessStarted, task, terminal.processId)); + this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.ProcessStarted, task, terminal!.processId!)); processStartedSignaled = true; } this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.ProcessEnded, task, exitCode)); @@ -560,22 +566,22 @@ export class TerminalTaskSystem implements ITaskSystem { } eventCounter = 0; this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.End, task)); - toDispose = dispose(toDispose); - toDispose = null; + toDispose = dispose(toDispose!); + toDispose = undefined; resolve({ exitCode }); }); }); } else { promise = new Promise((resolve, reject) => { [terminal, executedCommand, error] = this.createTerminal(task, resolver); - if (error || !terminal) { + if (!terminal || error) { return; } let processStartedSignaled = false; terminal.processReady.then(() => { if (!processStartedSignaled) { - this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.ProcessStarted, task, terminal.processId)); + this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.ProcessStarted, task, terminal!.processId!)); processStartedSignaled = true; } }, (_error) => { @@ -595,24 +601,29 @@ export class TerminalTaskSystem implements ITaskSystem { let key = task.getMapKey(); delete this.activeTasks[key]; this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Changed)); - switch (task.command.presentation.panel) { + switch (task.command.presentation!.panel) { case PanelKind.Dedicated: - this.sameTaskTerminals[key] = terminal.id.toString(); + this.sameTaskTerminals[key] = terminal!.id.toString(); break; case PanelKind.Shared: - this.idleTaskTerminals.set(key, terminal.id.toString(), Touch.AsOld); + this.idleTaskTerminals.set(key, terminal!.id.toString(), Touch.AsOld); break; } - let reveal = task.command.presentation.reveal; - if ((reveal === RevealKind.Silent) && ((exitCode !== 0) || (startStopProblemMatcher.numberOfMatches > 0) && (startStopProblemMatcher.maxMarkerSeverity >= MarkerSeverity.Error))) { + let reveal = task.command.presentation!.reveal; + if (terminal && (reveal === RevealKind.Silent) && ((exitCode !== 0) || (startStopProblemMatcher.numberOfMatches > 0) && startStopProblemMatcher.maxMarkerSeverity && + (startStopProblemMatcher.maxMarkerSeverity >= MarkerSeverity.Error))) { this.terminalService.setActiveInstance(terminal); this.terminalService.showPanel(false); } startStopProblemMatcher.done(); startStopProblemMatcher.dispose(); - registeredLinkMatchers.forEach(handle => terminal.deregisterLinkMatcher(handle)); - if (!processStartedSignaled) { - this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.ProcessStarted, task, terminal.processId)); + registeredLinkMatchers.forEach(handle => { + if (terminal) { + terminal.deregisterLinkMatcher(handle); + } + }); + if (!processStartedSignaled && terminal) { + this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.ProcessStarted, task, terminal.processId!)); processStartedSignaled = true; } this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.ProcessEnded, task, exitCode)); @@ -623,12 +634,12 @@ export class TerminalTaskSystem implements ITaskSystem { }); } if (error) { - return Promise.reject(new Error(error.message)); + return Promise.reject(new Error((error).message)); } if (!terminal) { return Promise.reject(new Error(`Failed to create terminal for task ${task._label}`)); } - if (task.command.presentation.reveal === RevealKind.Always) { + if (task.command.presentation && (task.command.presentation.reveal === RevealKind.Always)) { this.terminalService.setActiveInstance(terminal); this.terminalService.showPanel(task.command.presentation.focus); } @@ -640,7 +651,7 @@ export class TerminalTaskSystem implements ITaskSystem { trigger: trigger, runner: 'terminal', taskKind: task.getTelemetryKind(), - command: this.getSanitizedCommand(executedCommand), + command: this.getSanitizedCommand(executedCommand!), success: true, exitCode: summary.exitCode }; @@ -661,7 +672,7 @@ export class TerminalTaskSystem implements ITaskSystem { trigger: trigger, runner: 'terminal', taskKind: task.getTelemetryKind(), - command: this.getSanitizedCommand(executedCommand), + command: this.getSanitizedCommand(executedCommand!), success: false }; /* __GDPR__ @@ -679,16 +690,16 @@ export class TerminalTaskSystem implements ITaskSystem { } private createShellLaunchConfig(task: CustomTask | ContributedTask, variableResolver: VariableResolver, platform: Platform.Platform, options: CommandOptions, command: CommandString, args: CommandString[], waitOnExit: boolean | string): IShellLaunchConfig | undefined { - let shellLaunchConfig: IShellLaunchConfig | undefined = undefined; + let shellLaunchConfig: IShellLaunchConfig; let isShellCommand = task.command.runtime === RuntimeType.Shell; let needsFolderQualification = this.currentTask.workspaceFolder && this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE; let terminalName = nls.localize('TerminalTaskSystem.terminalName', 'Task - {0}', needsFolderQualification ? task.getQualifiedLabel() : task.configurationProperties.name); let originalCommand = task.command.name; if (isShellCommand) { - shellLaunchConfig = { name: terminalName, executable: null, args: null, waitOnExit }; + shellLaunchConfig = { name: terminalName, executable: undefined, args: undefined, waitOnExit }; this.terminalService.configHelper.mergeDefaultShellPathAndArgs(shellLaunchConfig, platform); let shellSpecified: boolean = false; - let shellOptions: ShellConfiguration = task.command.options && task.command.options.shell; + let shellOptions: ShellConfiguration | undefined = task.command.options && task.command.options.shell; if (shellOptions) { if (shellOptions.executable) { shellLaunchConfig.executable = this.resolveVariable(variableResolver, shellOptions.executable); @@ -700,22 +711,22 @@ export class TerminalTaskSystem implements ITaskSystem { shellLaunchConfig.args = []; } } - let shellArgs = shellLaunchConfig.args.slice(0); + let shellArgs = shellLaunchConfig.args!.slice(0); let toAdd: string[] = []; - let commandLine = this.buildShellCommandLine(platform, shellLaunchConfig.executable, shellOptions, command, originalCommand, args); + let commandLine = this.buildShellCommandLine(platform, shellLaunchConfig.executable!, shellOptions, command, originalCommand, args); let windowsShellArgs: boolean = false; if (platform === Platform.Platform.Windows) { // Change Sysnative to System32 if the OS is Windows but NOT WoW64. It's // safe to assume that this was used by accident as Sysnative does not // exist and will break the terminal in non-WoW64 environments. if (!process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432')) { - const sysnativePath = path.join(process.env.windir, 'Sysnative').toLowerCase(); - if (shellLaunchConfig.executable.toLowerCase().indexOf(sysnativePath) === 0) { - shellLaunchConfig.executable = path.join(process.env.windir, 'System32', shellLaunchConfig.executable.substr(sysnativePath.length)); + const sysnativePath = path.join(process.env.windir!, 'Sysnative').toLowerCase(); + if (shellLaunchConfig.executable!.toLowerCase().indexOf(sysnativePath) === 0) { + shellLaunchConfig.executable = path.join(process.env.windir!, 'System32', shellLaunchConfig.executable!.substr(sysnativePath.length)); } } windowsShellArgs = true; - let basename = path.basename(shellLaunchConfig.executable).toLowerCase(); + let basename = path.basename(shellLaunchConfig.executable!).toLowerCase(); if (basename === 'cmd.exe' && ((options.cwd && TPath.isUNC(options.cwd)) || (!options.cwd && TPath.isUNC(process.cwd())))) { return undefined; } @@ -756,9 +767,9 @@ export class TerminalTaskSystem implements ITaskSystem { }); shellArgs.push(commandLine); shellLaunchConfig.args = windowsShellArgs ? shellArgs.join(' ') : shellArgs; - if (task.command.presentation.echo) { + if (task.command.presentation && task.command.presentation.echo) { if (needsFolderQualification) { - shellLaunchConfig.initialText = `\x1b[1m> Executing task in folder ${this.currentTask.workspaceFolder.name}: ${commandLine} <\x1b[0m\n`; + shellLaunchConfig.initialText = `\x1b[1m> Executing task in folder ${this.currentTask.workspaceFolder!.name}: ${commandLine} <\x1b[0m\n`; } else { shellLaunchConfig.initialText = `\x1b[1m> Executing task: ${commandLine} <\x1b[0m\n`; } @@ -776,8 +787,8 @@ export class TerminalTaskSystem implements ITaskSystem { args: args.map(a => Types.isString(a) ? a : a.value), waitOnExit }; - if (task.command.presentation.echo) { - let getArgsToEcho = (args: string | string[]): string => { + if (task.command.presentation && task.command.presentation.echo) { + let getArgsToEcho = (args: string | string[] | undefined): string => { if (!args || args.length === 0) { return ''; } @@ -787,7 +798,7 @@ export class TerminalTaskSystem implements ITaskSystem { return args.join(' '); }; if (needsFolderQualification) { - shellLaunchConfig.initialText = `\x1b[1m> Executing task in folder ${this.currentTask.workspaceFolder.name}: ${shellLaunchConfig.executable} ${getArgsToEcho(shellLaunchConfig.args)} <\x1b[0m\n`; + shellLaunchConfig.initialText = `\x1b[1m> Executing task in folder ${this.currentTask.workspaceFolder!.name}: ${shellLaunchConfig.executable} ${getArgsToEcho(shellLaunchConfig.args)} <\x1b[0m\n`; } else { shellLaunchConfig.initialText = `\x1b[1m> Executing task: ${shellLaunchConfig.executable} ${getArgsToEcho(shellLaunchConfig.args)} <\x1b[0m\n`; } @@ -807,7 +818,7 @@ export class TerminalTaskSystem implements ITaskSystem { } if (!p.isAbsolute(cwd)) { let workspaceFolder = task.getWorkspaceFolder(); - if (workspaceFolder.uri.scheme === 'file') { + if (workspaceFolder && (workspaceFolder.uri.scheme === 'file')) { cwd = p.join(workspaceFolder.uri.fsPath, cwd); } } @@ -820,17 +831,21 @@ export class TerminalTaskSystem implements ITaskSystem { return shellLaunchConfig; } - private createTerminal(task: CustomTask | ContributedTask, resolver: VariableResolver): [ITerminalInstance, string, TaskError | undefined] { + private createTerminal(task: CustomTask | ContributedTask, resolver: VariableResolver): [ITerminalInstance | undefined, string | undefined, TaskError | undefined] { let platform = resolver.taskSystemInfo ? resolver.taskSystemInfo.platform : Platform.platform; let options = this.resolveOptions(resolver, task.command.options); let waitOnExit: boolean | string = false; let { command, args } = this.resolveCommandAndArgs(resolver, task.command); let commandExecutable = CommandString.value(command); + const presentationOptions = task.command.presentation; + if (!presentationOptions) { + throw new Error('Task presentation options should not be undefined here.'); + } - if (task.command.presentation.reveal !== RevealKind.Never || !task.configurationProperties.isBackground) { - if (task.command.presentation.panel === PanelKind.New) { + if (presentationOptions.reveal !== RevealKind.Never || !task.configurationProperties.isBackground) { + if (presentationOptions.panel === PanelKind.New) { waitOnExit = nls.localize('closeTerminal', 'Press any key to close the terminal.'); - } else if (task.command.presentation.showReuseMessage) { + } else if (presentationOptions.showReuseMessage) { waitOnExit = nls.localize('reuseTerminal', 'Terminal will be reused by tasks, press any key to close it.'); } else { waitOnExit = true; @@ -840,11 +855,11 @@ export class TerminalTaskSystem implements ITaskSystem { if (this.currentTask.shellLaunchConfig === undefined) { return [undefined, undefined, new TaskError(Severity.Error, nls.localize('TerminalTaskSystem', 'Can\'t execute a shell command on an UNC drive using cmd.exe.'), TaskErrors.UnknownError)]; } - let prefersSameTerminal = task.command.presentation.panel === PanelKind.Dedicated; - let allowsSharedTerminal = task.command.presentation.panel === PanelKind.Shared; + let prefersSameTerminal = presentationOptions.panel === PanelKind.Dedicated; + let allowsSharedTerminal = presentationOptions.panel === PanelKind.Shared; let taskKey = task.getMapKey(); - let terminalToReuse: TerminalData; + let terminalToReuse: TerminalData | undefined; if (prefersSameTerminal) { let terminalId = this.sameTaskTerminals[taskKey]; if (terminalId) { @@ -859,7 +874,7 @@ export class TerminalTaskSystem implements ITaskSystem { } if (terminalToReuse) { terminalToReuse.terminal.reuseTerminal(this.currentTask.shellLaunchConfig); - if (task.command.presentation.clear) { + if (task.command.presentation && task.command.presentation.clear) { terminalToReuse.terminal.clear(); } return [terminalToReuse.terminal, commandExecutable, undefined]; @@ -884,7 +899,7 @@ export class TerminalTaskSystem implements ITaskSystem { return [result, commandExecutable, undefined]; } - private buildShellCommandLine(platform: Platform.Platform, shellExecutable: string, shellOptions: ShellConfiguration, command: CommandString, originalCommand: CommandString, args: CommandString[]): string { + private buildShellCommandLine(platform: Platform.Platform, shellExecutable: string, shellOptions: ShellConfiguration | undefined, command: CommandString, originalCommand: CommandString | undefined, args: CommandString[]): string { let basename = path.parse(shellExecutable).name.toLowerCase(); let shellQuoteOptions = this.getQuotingOptions(basename, shellOptions); @@ -895,7 +910,7 @@ export class TerminalTaskSystem implements ITaskSystem { return false; } } - let quote: string; + let quote: string | undefined; for (let i = 0; i < value.length; i++) { // We found the end quote. let ch = value[i]; @@ -986,7 +1001,7 @@ export class TerminalTaskSystem implements ITaskSystem { return commandLine; } - private getQuotingOptions(shellBasename: string, shellOptions: ShellConfiguration): ShellQuotingOptions { + private getQuotingOptions(shellBasename: string, shellOptions: ShellConfiguration | undefined): ShellQuotingOptions { if (shellOptions && shellOptions.quoting) { return shellOptions.quoting; } @@ -1001,6 +1016,9 @@ export class TerminalTaskSystem implements ITaskSystem { } private collectCommandVariables(variables: Set, command: CommandConfiguration, task: CustomTask | ContributedTask): void { + if (command.name === void 0) { + throw new Error('Command name should never be undefined here.'); + } this.collectVariables(variables, command.name); if (command.args) { command.args.forEach(arg => this.collectVariables(variables, arg)); @@ -1015,9 +1033,10 @@ export class TerminalTaskSystem implements ITaskSystem { if (options.cwd) { this.collectVariables(variables, options.cwd); } - if (options.env) { - Object.keys(options.env).forEach((key) => { - let value: any = options.env[key]; + const optionsEnv = options.env; + if (optionsEnv) { + Object.keys(optionsEnv).forEach((key) => { + let value: any = optionsEnv[key]; if (Types.isString(value)) { this.collectVariables(variables, value); } @@ -1034,7 +1053,7 @@ export class TerminalTaskSystem implements ITaskSystem { } } - private collectMatcherVariables(variables: Set, values: Array): void { + private collectMatcherVariables(variables: Set, values: Array | undefined): void { if (values === void 0 || values === null || values.length === 0) { return; } @@ -1081,7 +1100,7 @@ export class TerminalTaskSystem implements ITaskSystem { return value.map(s => this.resolveVariable(resolver, s)); } - private resolveMatchers(resolver: VariableResolver, values: Array): ProblemMatcher[] { + private resolveMatchers(resolver: VariableResolver, values: Array | undefined): ProblemMatcher[] { if (values === void 0 || values === null || values.length === 0) { return []; } @@ -1101,14 +1120,14 @@ export class TerminalTaskSystem implements ITaskSystem { this.outputChannel.append(nls.localize('unkownProblemMatcher', 'Problem matcher {0} can\'t be resolved. The matcher will be ignored')); return; } - let taskSystemInfo: TaskSystemInfo = resolver.taskSystemInfo; + let taskSystemInfo: TaskSystemInfo | undefined = resolver.taskSystemInfo; let hasFilePrefix = matcher.filePrefix !== void 0; let hasUriProvider = taskSystemInfo !== void 0 && taskSystemInfo.uriProvider !== void 0; if (!hasFilePrefix && !hasUriProvider) { result.push(matcher); } else { let copy = Objects.deepClone(matcher); - if (hasUriProvider) { + if (hasUriProvider && (taskSystemInfo !== void 0)) { copy.uriProvider = taskSystemInfo.uriProvider; } if (hasFilePrefix) { @@ -1120,21 +1139,23 @@ export class TerminalTaskSystem implements ITaskSystem { return result; } - private resolveVariable(resolver: VariableResolver, value: string): string; - private resolveVariable(resolver: VariableResolver, value: CommandString): CommandString; - private resolveVariable(resolver: VariableResolver, value: CommandString): CommandString { + private resolveVariable(resolver: VariableResolver, value: string | undefined): string; + private resolveVariable(resolver: VariableResolver, value: CommandString | undefined): CommandString; + private resolveVariable(resolver: VariableResolver, value: CommandString | undefined): CommandString { // TODO@Dirk Task.getWorkspaceFolder should return a WorkspaceFolder that is defined in workspace.ts if (Types.isString(value)) { return resolver.resolve(value); - } else { + } else if (value !== void 0) { return { value: resolver.resolve(value.value), quoting: value.quoting }; + } else { // This should never happen + throw new Error('Should never try to resolve undefined.'); } } - private resolveOptions(resolver: VariableResolver, options: CommandOptions): CommandOptions { + private resolveOptions(resolver: VariableResolver, options: CommandOptions | undefined): CommandOptions { if (options === void 0 || options === null) { return { cwd: this.resolveVariable(resolver, '${workspaceFolder}') }; } @@ -1144,11 +1165,11 @@ export class TerminalTaskSystem implements ITaskSystem { if (options.env) { result.env = Object.create(null); Object.keys(options.env).forEach((key) => { - let value: any = options.env[key]; + let value: any = options.env![key]; if (Types.isString(value)) { - result.env[key] = this.resolveVariable(resolver, value); + result.env![key] = this.resolveVariable(resolver, value); } else { - result.env[key] = value.toString(); + result.env![key] = value.toString(); } }); } diff --git a/src/vs/workbench/parts/tasks/node/processTaskSystem.ts b/src/vs/workbench/parts/tasks/node/processTaskSystem.ts index acb29cb1c0e..13b6482057d 100644 --- a/src/vs/workbench/parts/tasks/node/processTaskSystem.ts +++ b/src/vs/workbench/parts/tasks/node/processTaskSystem.ts @@ -35,6 +35,9 @@ import { import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +/** + * Since ProcessTaskSystem is not receiving new feature updates all strict null check fixing has been done with !. + */ export class ProcessTaskSystem implements ITaskSystem { public static TelemetryEventName: string = 'taskService'; @@ -48,9 +51,9 @@ export class ProcessTaskSystem implements ITaskSystem { private outputChannel: IOutputChannel; private errorsShown: boolean; - private childProcess: LineProcess; - private activeTask: CustomTask; - private activeTaskPromise: Promise; + private childProcess: LineProcess | null; + private activeTask: CustomTask | null; + private activeTaskPromise: Promise | null; private readonly _onDidStateChange: Emitter; @@ -92,7 +95,7 @@ export class ProcessTaskSystem implements ITaskSystem { public run(task: Task): ITaskExecuteResult { if (this.activeTask) { - return { kind: TaskExecuteKind.Active, task, active: { same: this.activeTask._id === task._id, background: this.activeTask.configurationProperties.isBackground }, promise: this.activeTaskPromise }; + return { kind: TaskExecuteKind.Active, task, active: { same: this.activeTask._id === task._id, background: this.activeTask.configurationProperties.isBackground! }, promise: this.activeTaskPromise! }; } return this.executeTask(task); } @@ -127,8 +130,8 @@ export class ProcessTaskSystem implements ITaskSystem { if (this.childProcess) { let task = this.activeTask; return this.childProcess.terminate().then((response) => { - let result: TaskTerminateResponse = Objects.assign({ task: task }, response); - this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Terminated, task)); + let result: TaskTerminateResponse = Objects.assign({ task: task! }, response); + this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Terminated, task!)); return [result]; }); } @@ -220,22 +223,22 @@ export class ProcessTaskSystem implements ITaskSystem { } } args = this.resolveVariables(task, args); - let command: string = this.resolveVariable(task, Types.isString(commandConfig.name) ? commandConfig.name : commandConfig.name.value); - this.childProcess = new LineProcess(command, args, commandConfig.runtime === RuntimeType.Shell, this.resolveOptions(task, commandConfig.options)); + let command: string = this.resolveVariable(task, Types.isString(commandConfig.name) ? commandConfig.name : commandConfig.name!.value); + this.childProcess = new LineProcess(command, args, commandConfig.runtime === RuntimeType.Shell, this.resolveOptions(task, commandConfig.options!)); telemetryEvent.command = this.childProcess.getSanitizedCommand(); // we have no problem matchers defined. So show the output log - let reveal = task.command.presentation.reveal; - if (reveal === RevealKind.Always || (reveal === RevealKind.Silent && task.configurationProperties.problemMatchers.length === 0)) { + let reveal = task.command.presentation!.reveal; + if (reveal === RevealKind.Always || (reveal === RevealKind.Silent && task.configurationProperties.problemMatchers!.length === 0)) { this.showOutput(); } - if (commandConfig.presentation.echo) { + if (commandConfig.presentation!.echo) { let prompt: string = Platform.isWindows ? '>' : '$'; this.log(`running command${prompt} ${command} ${args.join(' ')}`); } if (task.configurationProperties.isBackground) { - let watchingProblemMatcher = new WatchingProblemCollector(this.resolveMatchers(task, task.configurationProperties.problemMatchers), this.markerService, this.modelService); - let toDispose: IDisposable[] = []; + let watchingProblemMatcher = new WatchingProblemCollector(this.resolveMatchers(task, task.configurationProperties.problemMatchers!), this.markerService, this.modelService); + let toDispose: IDisposable[] | null = []; let eventCounter: number = 0; toDispose.push(watchingProblemMatcher.onDidStateChange((event) => { if (event.kind === ProblemCollectorEventKind.BackgroundProcessingBegins) { @@ -247,7 +250,7 @@ export class ProcessTaskSystem implements ITaskSystem { } })); watchingProblemMatcher.aboutToStart(); - let delayer: Async.Delayer = null; + let delayer: Async.Delayer | null = null; this.activeTask = task; const inactiveEvent = TaskEvent.create(TaskEventKind.Inactive, task); let processStartedSignaled: boolean = false; @@ -277,9 +280,9 @@ export class ProcessTaskSystem implements ITaskSystem { watchingProblemMatcher.done(); watchingProblemMatcher.dispose(); if (processStartedSignaled) { - this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.ProcessEnded, task, success.cmdCode)); + this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.ProcessEnded, task, success.cmdCode!)); } - toDispose = dispose(toDispose); + toDispose = dispose(toDispose!); toDispose = null; for (let i = 0; i < eventCounter; i++) { this._onDidStateChange.fire(inactiveEvent); @@ -296,7 +299,7 @@ export class ProcessTaskSystem implements ITaskSystem { }, (error: ErrorData) => { this.childProcessEnded(); watchingProblemMatcher.dispose(); - toDispose = dispose(toDispose); + toDispose = dispose(toDispose!); toDispose = null; for (let i = 0; i < eventCounter; i++) { this._onDidStateChange.fire(inactiveEvent); @@ -311,7 +314,7 @@ export class ProcessTaskSystem implements ITaskSystem { } else { this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Start, task)); this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Active, task)); - let startStopProblemMatcher = new StartStopProblemCollector(this.resolveMatchers(task, task.configurationProperties.problemMatchers), this.markerService, this.modelService); + let startStopProblemMatcher = new StartStopProblemCollector(this.resolveMatchers(task, task.configurationProperties.problemMatchers!), this.markerService, this.modelService); this.activeTask = task; const inactiveEvent = TaskEvent.create(TaskEventKind.Inactive, task); let processStartedSignaled: boolean = false; @@ -333,7 +336,7 @@ export class ProcessTaskSystem implements ITaskSystem { startStopProblemMatcher.dispose(); this.checkTerminated(task, success); if (processStartedSignaled) { - this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.ProcessEnded, task, success.cmdCode)); + this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.ProcessEnded, task, success.cmdCode!)); } this._onDidStateChange.fire(inactiveEvent); this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.End, task)); @@ -397,15 +400,15 @@ export class ProcessTaskSystem implements ITaskSystem { } private resolveOptions(task: CustomTask, options: CommandOptions): CommandOptions { - let result: CommandOptions = { cwd: this.resolveVariable(task, options.cwd) }; + let result: CommandOptions = { cwd: this.resolveVariable(task, options.cwd!) }; if (options.env) { result.env = Object.create(null); Object.keys(options.env).forEach((key) => { - let value: any = options.env[key]; + let value: any = options.env![key]; if (Types.isString(value)) { - result.env[key] = this.resolveVariable(task, value); + result.env![key] = this.resolveVariable(task, value); } else { - result.env[key] = value.toString(); + result.env![key] = value.toString(); } }); } @@ -440,7 +443,7 @@ export class ProcessTaskSystem implements ITaskSystem { result.push(matcher); } else { let copy = Objects.deepClone(matcher); - copy.filePrefix = this.resolveVariable(task, copy.filePrefix); + copy.filePrefix = this.resolveVariable(task, copy.filePrefix!); result.push(copy); } }); @@ -448,7 +451,7 @@ export class ProcessTaskSystem implements ITaskSystem { } private resolveVariable(task: CustomTask, value: string): string { - return this.configurationResolverService.resolve(task.getWorkspaceFolder(), value); + return this.configurationResolverService.resolve(task.getWorkspaceFolder()!, value); } public log(value: string): void { diff --git a/src/vs/workbench/parts/tasks/node/taskConfiguration.ts b/src/vs/workbench/parts/tasks/node/taskConfiguration.ts index 876b1b57dfc..806964a0ced 100644 --- a/src/vs/workbench/parts/tasks/node/taskConfiguration.ts +++ b/src/vs/workbench/parts/tasks/node/taskConfiguration.ts @@ -504,24 +504,26 @@ const EMPTY_ARRAY: any[] = []; Object.freeze(EMPTY_ARRAY); function assignProperty(target: T, source: Partial, key: K) { - if (source[key] !== void 0) { - target[key] = source[key]; + const sourceAtKey = source[key]; + if (sourceAtKey !== void 0) { + target[key] = sourceAtKey!; } } function fillProperty(target: T, source: Partial, key: K) { - if (target[key] === void 0 && source[key] !== void 0) { - target[key] = source[key]; + const sourceAtKey = source[key]; + if (target[key] === void 0 && sourceAtKey !== void 0) { + target[key] = sourceAtKey!; } } interface ParserType { - isEmpty(value: T): boolean; - assignProperties(target: T, source: T): T; - fillProperties(target: T, source: T): T; - fillDefaults(value: T, context: ParseContext): T; - freeze(value: T): Readonly; + isEmpty(value: T | undefined): boolean; + assignProperties(target: T | undefined, source: T | undefined): T | undefined; + fillProperties(target: T | undefined, source: T | undefined): T | undefined; + fillDefaults(value: T | undefined, context: ParseContext): T | undefined; + freeze(value: T): Readonly | undefined; } interface MetaData { @@ -530,8 +532,8 @@ interface MetaData { } -function _isEmpty(this: void, value: T, properties: MetaData[]): boolean { - if (value === void 0 || value === null) { +function _isEmpty(this: void, value: T, properties: MetaData[] | undefined): boolean { + if (value === void 0 || value === null || properties === void 0) { return true; } for (let meta of properties) { @@ -569,14 +571,14 @@ function _assignProperties(this: void, target: T, source: T, properties: Meta return target; } -function _fillProperties(this: void, target: T, source: T, properties: MetaData[]): T { +function _fillProperties(this: void, target: T, source: T, properties: MetaData[] | undefined): T { if (_isEmpty(source, properties)) { return target; } if (_isEmpty(target, properties)) { return source; } - for (let meta of properties) { + for (let meta of properties!) { let property = meta.property; let value: any; if (meta.type) { @@ -591,7 +593,7 @@ function _fillProperties(this: void, target: T, source: T, properties: MetaDa return target; } -function _fillDefaults(this: void, target: T, defaults: T, properties: MetaData[], context: ParseContext): T { +function _fillDefaults(this: void, target: T, defaults: T, properties: MetaData[], context: ParseContext): T | undefined { if (target && Object.isFrozen(target)) { return target; } @@ -621,7 +623,7 @@ function _fillDefaults(this: void, target: T, defaults: T, properties: MetaDa return target; } -function _freeze(this: void, target: T, properties: MetaData[]): Readonly { +function _freeze(this: void, target: T, properties: MetaData[]): Readonly | undefined { if (target === void 0 || target === null) { return undefined; } @@ -685,7 +687,7 @@ namespace ShellConfiguration { return candidate && (Types.isString(candidate.executable) || Types.isStringArray(candidate.args)); } - export function from(this: void, config: ShellConfiguration, context: ParseContext): Tasks.ShellConfiguration { + export function from(this: void, config: ShellConfiguration | undefined, context: ParseContext): Tasks.ShellConfiguration | undefined { if (!is(config)) { return undefined; } @@ -707,7 +709,7 @@ namespace ShellConfiguration { return _isEmpty(value, properties); } - export function assignProperties(this: void, target: Tasks.ShellConfiguration, source: Tasks.ShellConfiguration): Tasks.ShellConfiguration { + export function assignProperties(this: void, target: Tasks.ShellConfiguration | undefined, source: Tasks.ShellConfiguration | undefined): Tasks.ShellConfiguration | undefined { return _assignProperties(target, source, properties); } @@ -719,7 +721,7 @@ namespace ShellConfiguration { return value; } - export function freeze(this: void, value: Tasks.ShellConfiguration): Readonly { + export function freeze(this: void, value: Tasks.ShellConfiguration): Readonly | undefined { if (!value) { return undefined; } @@ -732,7 +734,7 @@ namespace CommandOptions { const properties: MetaData[] = [{ property: 'cwd' }, { property: 'env' }, { property: 'shell', type: ShellConfiguration }]; const defaults: CommandOptionsConfig = { cwd: '${workspaceFolder}' }; - export function from(this: void, options: CommandOptionsConfig, context: ParseContext): Tasks.CommandOptions { + export function from(this: void, options: CommandOptionsConfig, context: ParseContext): Tasks.CommandOptions | undefined { let result: Tasks.CommandOptions = {}; if (options.cwd !== void 0) { if (Types.isString(options.cwd)) { @@ -748,15 +750,15 @@ namespace CommandOptions { return isEmpty(result) ? undefined : result; } - export function isEmpty(value: Tasks.CommandOptions): boolean { + export function isEmpty(value: Tasks.CommandOptions | undefined): boolean { return _isEmpty(value, properties); } - export function assignProperties(target: Tasks.CommandOptions, source: Tasks.CommandOptions): Tasks.CommandOptions { - if (isEmpty(source)) { + export function assignProperties(target: Tasks.CommandOptions | undefined, source: Tasks.CommandOptions | undefined): Tasks.CommandOptions | undefined { + if ((source === void 0) || isEmpty(source)) { return target; } - if (isEmpty(target)) { + if ((target === void 0) || isEmpty(target)) { return source; } assignProperty(target, source, 'cwd'); @@ -764,23 +766,27 @@ namespace CommandOptions { target.env = source.env; } else if (source.env !== void 0) { let env: { [key: string]: string; } = Object.create(null); - Object.keys(target.env).forEach(key => env[key] = target.env[key]); - Object.keys(source.env).forEach(key => env[key] = source.env[key]); + if (target.env !== void 0) { + Object.keys(target.env).forEach(key => env[key] = target.env![key]); + } + if (source.env !== void 0) { + Object.keys(source.env).forEach(key => env[key] = source.env![key]); + } target.env = env; } target.shell = ShellConfiguration.assignProperties(target.shell, source.shell); return target; } - export function fillProperties(target: Tasks.CommandOptions, source: Tasks.CommandOptions): Tasks.CommandOptions { + export function fillProperties(target: Tasks.CommandOptions | undefined, source: Tasks.CommandOptions | undefined): Tasks.CommandOptions | undefined { return _fillProperties(target, source, properties); } - export function fillDefaults(value: Tasks.CommandOptions, context: ParseContext): Tasks.CommandOptions { + export function fillDefaults(value: Tasks.CommandOptions | undefined, context: ParseContext): Tasks.CommandOptions | undefined { return _fillDefaults(value, defaults, properties, context); } - export function freeze(value: Tasks.CommandOptions): Readonly { + export function freeze(value: Tasks.CommandOptions): Readonly | undefined { return _freeze(value, properties); } } @@ -794,18 +800,21 @@ namespace CommandConfiguration { presentation?: PresentationOptionsConfig; } - export function from(this: void, config: PresentationOptionsShape, context: ParseContext): Tasks.PresentationOptions { + export function from(this: void, config: PresentationOptionsShape, context: ParseContext): Tasks.PresentationOptions | undefined { let echo: boolean; let reveal: Tasks.RevealKind; let focus: boolean; let panel: Tasks.PanelKind; let showReuseMessage: boolean; let clear: boolean; + let hasProps = false; if (Types.isBoolean(config.echoCommand)) { echo = config.echoCommand; + hasProps = true; } if (Types.isString(config.showOutput)) { reveal = Tasks.RevealKind.fromString(config.showOutput); + hasProps = true; } let presentation = config.presentation || config.terminal; if (presentation) { @@ -827,27 +836,28 @@ namespace CommandConfiguration { if (Types.isBoolean(presentation.clear)) { clear = presentation.clear; } + hasProps = true; } - if (echo === void 0 && reveal === void 0 && focus === void 0 && panel === void 0 && showReuseMessage === void 0 && clear === void 0) { + if (!hasProps) { return undefined; } - return { echo, reveal, focus, panel, showReuseMessage, clear }; + return { echo: echo!, reveal: reveal!, focus: focus!, panel: panel!, showReuseMessage: showReuseMessage!, clear: clear! }; } - export function assignProperties(target: Tasks.PresentationOptions, source: Tasks.PresentationOptions): Tasks.PresentationOptions { + export function assignProperties(target: Tasks.PresentationOptions, source: Tasks.PresentationOptions | undefined): Tasks.PresentationOptions | undefined { return _assignProperties(target, source, properties); } - export function fillProperties(target: Tasks.PresentationOptions, source: Tasks.PresentationOptions): Tasks.PresentationOptions { + export function fillProperties(target: Tasks.PresentationOptions, source: Tasks.PresentationOptions | undefined): Tasks.PresentationOptions | undefined { return _fillProperties(target, source, properties); } - export function fillDefaults(value: Tasks.PresentationOptions, context: ParseContext): Tasks.PresentationOptions { + export function fillDefaults(value: Tasks.PresentationOptions, context: ParseContext): Tasks.PresentationOptions | undefined { let defaultEcho = context.engine === Tasks.ExecutionEngine.Terminal ? true : false; return _fillDefaults(value, { echo: defaultEcho, reveal: Tasks.RevealKind.Always, focus: false, panel: Tasks.PanelKind.Shared, showReuseMessage: true, clear: false }, properties, context); } - export function freeze(value: Tasks.PresentationOptions): Readonly { + export function freeze(value: Tasks.PresentationOptions): Readonly | undefined { return _freeze(value, properties); } @@ -857,7 +867,7 @@ namespace CommandConfiguration { } namespace ShellString { - export function from(this: void, value: CommandString): Tasks.CommandString { + export function from(this: void, value: CommandString | undefined): Tasks.CommandString | undefined { if (value === void 0 || value === null) { return undefined; } @@ -895,10 +905,10 @@ namespace CommandConfiguration { { property: 'presentation', type: PresentationOptions } ]; - export function from(this: void, config: CommandConfiguationShape, context: ParseContext): Tasks.CommandConfiguration { - let result: Tasks.CommandConfiguration = fromBase(config, context); + export function from(this: void, config: CommandConfiguationShape, context: ParseContext): Tasks.CommandConfiguration | undefined { + let result: Tasks.CommandConfiguration = fromBase(config, context)!; - let osConfig: Tasks.CommandConfiguration = undefined; + let osConfig: Tasks.CommandConfiguration | undefined = undefined; if (config.windows && context.platform === Platform.Windows) { osConfig = fromBase(config.windows, context); } else if (config.osx && context.platform === Platform.Mac) { @@ -912,26 +922,27 @@ namespace CommandConfiguration { return isEmpty(result) ? undefined : result; } - function fromBase(this: void, config: BaseCommandConfiguationShape, context: ParseContext): Tasks.CommandConfiguration { - let result: Tasks.CommandConfiguration = { - name: undefined, - runtime: undefined, - presentation: undefined - }; - - result.name = ShellString.from(config.command); + function fromBase(this: void, config: BaseCommandConfiguationShape, context: ParseContext): Tasks.CommandConfiguration | undefined { + let name: Tasks.CommandString = ShellString.from(config.command)!; + let runtime: Tasks.RuntimeType; if (Types.isString(config.type)) { if (config.type === 'shell' || config.type === 'process') { - result.runtime = Tasks.RuntimeType.fromString(config.type); + runtime = Tasks.RuntimeType.fromString(config.type); } } let isShellConfiguration = ShellConfiguration.is(config.isShellCommand); if (Types.isBoolean(config.isShellCommand) || isShellConfiguration) { - result.runtime = Tasks.RuntimeType.Shell; + runtime = Tasks.RuntimeType.Shell; } else if (config.isShellCommand !== void 0) { - result.runtime = !!config.isShellCommand ? Tasks.RuntimeType.Shell : Tasks.RuntimeType.Process; + runtime = !!config.isShellCommand ? Tasks.RuntimeType.Shell : Tasks.RuntimeType.Process; } + let result: Tasks.CommandConfiguration = { + name: name!, + runtime: runtime!, + presentation: PresentationOptions.from(config, context)! + }; + if (config.args !== void 0) { result.args = []; for (let arg of config.args) { @@ -957,16 +968,14 @@ namespace CommandConfiguration { } } } - let panel = PresentationOptions.from(config, context); - if (panel) { - result.presentation = panel; - } + if (Types.isString(config.taskSelector)) { result.taskSelector = config.taskSelector; } if (Types.isBoolean(config.suppressTaskName)) { result.suppressTaskName = config.suppressTaskName; } + return isEmpty(result) ? undefined : result; } @@ -974,7 +983,7 @@ namespace CommandConfiguration { return value && !!value.name; } - export function isEmpty(value: Tasks.CommandConfiguration): boolean { + export function isEmpty(value: Tasks.CommandConfiguration | undefined): boolean { return _isEmpty(value, properties); } @@ -996,7 +1005,7 @@ namespace CommandConfiguration { target.args = target.args.concat(source.args); } } - target.presentation = PresentationOptions.assignProperties(target.presentation, source.presentation); + target.presentation = PresentationOptions.assignProperties(target.presentation!, source.presentation)!; target.options = CommandOptions.assignProperties(target.options, source.options); return target; } @@ -1005,8 +1014,8 @@ namespace CommandConfiguration { return _fillProperties(target, source, properties); } - export function fillGlobals(target: Tasks.CommandConfiguration, source: Tasks.CommandConfiguration, taskName: string): Tasks.CommandConfiguration { - if (isEmpty(source)) { + export function fillGlobals(target: Tasks.CommandConfiguration, source: Tasks.CommandConfiguration | undefined, taskName: string | undefined): Tasks.CommandConfiguration { + if ((source === undefined) || isEmpty(source)) { return target; } target = target || { @@ -1019,7 +1028,7 @@ namespace CommandConfiguration { fillProperty(target, source, 'taskSelector'); fillProperty(target, source, 'suppressTaskName'); let args: Tasks.CommandString[] = source.args ? source.args.slice() : []; - if (!target.suppressTaskName) { + if (!target.suppressTaskName && taskName) { if (target.taskSelector !== void 0) { args.push(target.taskSelector + taskName); } else { @@ -1033,20 +1042,20 @@ namespace CommandConfiguration { } fillProperty(target, source, 'runtime'); - target.presentation = PresentationOptions.fillProperties(target.presentation, source.presentation); + target.presentation = PresentationOptions.fillProperties(target.presentation!, source.presentation)!; target.options = CommandOptions.fillProperties(target.options, source.options); return target; } - export function fillDefaults(value: Tasks.CommandConfiguration, context: ParseContext): void { + export function fillDefaults(value: Tasks.CommandConfiguration | undefined, context: ParseContext): void { if (!value || Object.isFrozen(value)) { return; } if (value.name !== void 0 && value.runtime === void 0) { value.runtime = Tasks.RuntimeType.Process; } - value.presentation = PresentationOptions.fillDefaults(value.presentation, context); + value.presentation = PresentationOptions.fillDefaults(value.presentation!, context)!; if (!isEmpty(value)) { value.options = CommandOptions.fillDefaults(value.options, context); } @@ -1058,14 +1067,14 @@ namespace CommandConfiguration { } } - export function freeze(value: Tasks.CommandConfiguration): Readonly { + export function freeze(value: Tasks.CommandConfiguration): Readonly | undefined { return _freeze(value, properties); } } namespace ProblemMatcherConverter { - export function namedFrom(this: void, declares: ProblemMatcherConfig.NamedProblemMatcher[], context: ParseContext): IStringDictionary { + export function namedFrom(this: void, declares: ProblemMatcherConfig.NamedProblemMatcher[] | undefined, context: ParseContext): IStringDictionary { let result: IStringDictionary = Object.create(null); if (!Types.isArray(declares)) { @@ -1082,7 +1091,7 @@ namespace ProblemMatcherConverter { return result; } - export function from(this: void, config: ProblemMatcherConfig.ProblemMatcherType, context: ParseContext): ProblemMatcher[] { + export function from(this: void, config: ProblemMatcherConfig.ProblemMatcherType | undefined, context: ParseContext): ProblemMatcher[] { let result: ProblemMatcher[] = []; if (config === void 0) { return result; @@ -1123,7 +1132,7 @@ namespace ProblemMatcherConverter { } } - function resolveProblemMatcher(this: void, value: string | ProblemMatcherConfig.ProblemMatcher, context: ParseContext): ProblemMatcher { + function resolveProblemMatcher(this: void, value: string | ProblemMatcherConfig.ProblemMatcher, context: ParseContext): ProblemMatcher | undefined { if (Types.isString(value)) { let variableName = value; if (variableName.length > 1 && variableName[0] === '$') { @@ -1149,14 +1158,14 @@ namespace ProblemMatcherConverter { } } -const source: Tasks.TaskSource = { +const source: Partial = { kind: Tasks.TaskSourceKind.Workspace, label: 'Workspace', config: undefined }; namespace GroupKind { - export function from(this: void, external: string | GroupKind): [string, Tasks.GroupType] { + export function from(this: void, external: string | GroupKind | undefined): [string, Tasks.GroupType] | undefined { if (external === void 0) { return undefined; } @@ -1178,7 +1187,7 @@ namespace GroupKind { } namespace TaskDependency { - export function from(this: void, external: string | TaskIdentifier, context: ParseContext): Tasks.TaskDependency { + export function from(this: void, external: string | TaskIdentifier, context: ParseContext): Tasks.TaskDependency | undefined { if (Types.isString(external)) { return { workspaceFolder: context.workspaceFolder, task: external }; } else if (TaskIdentifier.is(external)) { @@ -1198,7 +1207,7 @@ namespace ConfigurationProperties { { property: 'presentation', type: CommandConfiguration.PresentationOptions }, { property: 'problemMatchers' } ]; - export function from(this: void, external: ConfigurationProperties, context: ParseContext, includeCommandOptions: boolean): Tasks.ConfigurationProperties { + export function from(this: void, external: ConfigurationProperties, context: ParseContext, includeCommandOptions: boolean): Tasks.ConfigurationProperties | undefined { if (!external) { return undefined; } @@ -1232,9 +1241,16 @@ namespace ConfigurationProperties { } if (external.dependsOn !== void 0) { if (Types.isArray(external.dependsOn)) { - result.dependsOn = external.dependsOn.map(item => TaskDependency.from(item, context)); + result.dependsOn = external.dependsOn.reduce((dependencies: Tasks.TaskDependency[], item): Tasks.TaskDependency[] => { + const dependency = TaskDependency.from(item, context); + if (dependency) { + dependencies.push(dependency); + } + return dependencies; + }, []); } else { - result.dependsOn = [TaskDependency.from(external.dependsOn, context)]; + const dependsOnValue = TaskDependency.from(external.dependsOn, context); + result.dependsOn = dependsOnValue ? [dependsOnValue] : undefined; } } if (includeCommandOptions && (external.presentation !== void 0 || (external as LegacyCommandProperties).terminal !== void 0)) { @@ -1266,7 +1282,7 @@ namespace ConfiguringTask { customize: string; } - export function from(this: void, external: ConfiguringTask, context: ParseContext, index: number): Tasks.ConfiguringTask { + export function from(this: void, external: ConfiguringTask, context: ParseContext, index: number): Tasks.ConfiguringTask | undefined { if (!external) { return undefined; } @@ -1276,13 +1292,13 @@ namespace ConfiguringTask { context.problemReporter.error(nls.localize('ConfigurationParser.noTaskType', 'Error: tasks configuration must have a type property. The configuration will be ignored.\n{0}\n', JSON.stringify(external, null, 4))); return undefined; } - let typeDeclaration = TaskDefinitionRegistry.get(type); + let typeDeclaration = type ? TaskDefinitionRegistry.get(type) : undefined; if (!typeDeclaration) { let message = nls.localize('ConfigurationParser.noTypeDefinition', 'Error: there is no registered task type \'{0}\'. Did you miss to install an extension that provides a corresponding task provider?', type); context.problemReporter.error(message); return undefined; } - let identifier: Tasks.TaskIdentifier; + let identifier: Tasks.TaskIdentifier | undefined; if (Types.isString(customize)) { if (customize.indexOf(grunt) === 0) { identifier = { type: 'grunt', task: customize.substring(grunt.length) }; @@ -1307,7 +1323,7 @@ namespace ConfiguringTask { )); return undefined; } - let taskIdentifier: Tasks.KeyedTaskIdentifier = TaskDefinition.createTaskIdentifier(identifier, context.problemReporter); + let taskIdentifier: Tasks.KeyedTaskIdentifier | undefined = TaskDefinition.createTaskIdentifier(identifier, context.problemReporter); if (taskIdentifier === void 0) { context.problemReporter.error(nls.localize( 'ConfigurationParser.incorrectType', @@ -1358,7 +1374,7 @@ namespace ConfiguringTask { namespace CustomTask { - export function from(this: void, external: CustomTask, context: ParseContext, index: number): Tasks.CustomTask { + export function from(this: void, external: CustomTask, context: ParseContext, index: number): Tasks.CustomTask | undefined { if (!external) { return undefined; } @@ -1410,7 +1426,7 @@ namespace CustomTask { } } } - let command: Tasks.CommandConfiguration = CommandConfiguration.from(external, context); + let command: Tasks.CommandConfiguration = CommandConfiguration.from(external, context)!; if (command) { result.command = command; } @@ -1454,32 +1470,32 @@ namespace CustomTask { } } - export function createCustomTask(contributedTask: Tasks.ContributedTask, configuredProps: Tasks.ConfigurationProperties & { _id: string, _source: Tasks.WorkspaceTaskSource, taskLoadMessages: string[] | undefined }): Tasks.CustomTask { + export function createCustomTask(contributedTask: Tasks.ContributedTask, configuredProps: Tasks.ConfiguringTask | Tasks.CustomTask): Tasks.CustomTask { let result: Tasks.CustomTask = new Tasks.CustomTask( configuredProps._id, Objects.assign({}, configuredProps._source, { customizes: contributedTask.defines }), - configuredProps.name || contributedTask._label, + configuredProps.configurationProperties.name || contributedTask._label, Tasks.CUSTOMIZED_TASK_TYPE, contributedTask.command, false, contributedTask.runOptions, { - name: configuredProps.name || contributedTask.configurationProperties.name, - identifier: configuredProps.identifier || contributedTask.configurationProperties.identifier, + name: configuredProps.configurationProperties.name || contributedTask.configurationProperties.name, + identifier: configuredProps.configurationProperties.identifier || contributedTask.configurationProperties.identifier, } ); result.addTaskLoadMessages(configuredProps.taskLoadMessages); let resultConfigProps: Tasks.ConfigurationProperties = result.configurationProperties; - assignProperty(resultConfigProps, configuredProps, 'group'); - assignProperty(resultConfigProps, configuredProps, 'groupType'); - assignProperty(resultConfigProps, configuredProps, 'isBackground'); - assignProperty(resultConfigProps, configuredProps, 'dependsOn'); - assignProperty(resultConfigProps, configuredProps, 'problemMatchers'); - assignProperty(resultConfigProps, configuredProps, 'promptOnClose'); + assignProperty(resultConfigProps, configuredProps.configurationProperties, 'group'); + assignProperty(resultConfigProps, configuredProps.configurationProperties, 'groupType'); + assignProperty(resultConfigProps, configuredProps.configurationProperties, 'isBackground'); + assignProperty(resultConfigProps, configuredProps.configurationProperties, 'dependsOn'); + assignProperty(resultConfigProps, configuredProps.configurationProperties, 'problemMatchers'); + assignProperty(resultConfigProps, configuredProps.configurationProperties, 'promptOnClose'); result.command.presentation = CommandConfiguration.PresentationOptions.assignProperties( - result.command.presentation, configuredProps.presentation); - result.command.options = CommandOptions.assignProperties(result.command.options, configuredProps.options); + result.command.presentation!, configuredProps.configurationProperties.presentation)!; + result.command.options = CommandOptions.assignProperties(result.command.options, configuredProps.configurationProperties.options); let contributedConfigProps: Tasks.ConfigurationProperties = contributedTask.configurationProperties; fillProperty(resultConfigProps, contributedConfigProps, 'group'); @@ -1489,7 +1505,7 @@ namespace CustomTask { fillProperty(resultConfigProps, contributedConfigProps, 'problemMatchers'); fillProperty(resultConfigProps, contributedConfigProps, 'promptOnClose'); result.command.presentation = CommandConfiguration.PresentationOptions.fillProperties( - result.command.presentation, contributedConfigProps.presentation); + result.command.presentation!, contributedConfigProps.presentation)!; result.command.options = CommandOptions.fillProperties(result.command.options, contributedConfigProps.options); if (contributedTask.hasDefinedMatchers === true) { @@ -1513,13 +1529,13 @@ namespace TaskParser { return customize === void 0 && (type === void 0 || type === null || type === Tasks.CUSTOMIZED_TASK_TYPE || type === 'shell' || type === 'process'); } - export function from(this: void, externals: Array, globals: Globals, context: ParseContext): TaskParseResult { + export function from(this: void, externals: Array | undefined, globals: Globals, context: ParseContext): TaskParseResult { let result: TaskParseResult = { custom: [], configured: [] }; if (!externals) { return result; } - let defaultBuildTask: { task: Tasks.Task; rank: number; } = { task: undefined, rank: -1 }; - let defaultTestTask: { task: Tasks.Task; rank: number; } = { task: undefined, rank: -1 }; + let defaultBuildTask: { task: Tasks.Task | undefined; rank: number; } = { task: undefined, rank: -1 }; + let defaultTestTask: { task: Tasks.Task | undefined; rank: number; } = { task: undefined, rank: -1 }; let schema2_0_0: boolean = context.schemaVersion === Tasks.JsonSchemaVersion.V2_0_0; const baseLoadIssues = Objects.deepClone(context.taskLoadIssues); for (let index = 0; index < externals.length; index++) { @@ -1571,10 +1587,10 @@ namespace TaskParser { } context.taskLoadIssues = Objects.deepClone(baseLoadIssues); } - if (defaultBuildTask.rank > -1 && defaultBuildTask.rank < 2) { + if ((defaultBuildTask.rank > -1) && (defaultBuildTask.rank < 2) && defaultBuildTask.task) { defaultBuildTask.task.configurationProperties.group = Tasks.TaskGroup.Build; defaultBuildTask.task.configurationProperties.groupType = Tasks.GroupType.user; - } else if (defaultTestTask.rank > -1 && defaultTestTask.rank < 2) { + } else if ((defaultTestTask.rank > -1) && (defaultTestTask.rank < 2) && defaultTestTask.task) { defaultTestTask.task.configurationProperties.group = Tasks.TaskGroup.Test; defaultTestTask.task.configurationProperties.groupType = Tasks.GroupType.user; } @@ -1594,16 +1610,16 @@ namespace TaskParser { // Tasks are keyed by ID but we need to merge by name let map: IStringDictionary = Object.create(null); target.forEach((task) => { - map[task.configurationProperties.name] = task; + map[task.configurationProperties.name!] = task; }); source.forEach((task) => { - map[task.configurationProperties.name] = task; + map[task.configurationProperties.name!] = task; }); let newTarget: Tasks.CustomTask[] = []; target.forEach(task => { - newTarget.push(map[task.configurationProperties.name]); - delete map[task.configurationProperties.name]; + newTarget.push(map[task.configurationProperties.name!]); + delete map[task.configurationProperties.name!]; }); Object.keys(map).forEach(key => newTarget.push(map[key])); target = newTarget; @@ -1698,7 +1714,7 @@ export namespace ExecutionEngine { export function from(config: ExternalTaskRunnerConfiguration): Tasks.ExecutionEngine { let runner = config.runner || config._runner; - let result: Tasks.ExecutionEngine; + let result: Tasks.ExecutionEngine | undefined; if (runner) { switch (runner) { case 'terminal': @@ -1752,7 +1768,7 @@ export interface IProblemReporter extends IProblemReporterBase { class UUIDMap { - private last: IStringDictionary; + private last: IStringDictionary | undefined; private current: IStringDictionary; constructor(other?: UUIDMap) { @@ -1775,17 +1791,17 @@ class UUIDMap { } public getUUID(identifier: string): string { - let lastValue = this.last[identifier]; - let result: string; + let lastValue = this.last ? this.last[identifier] : undefined; + let result: string | undefined = undefined; if (lastValue !== void 0) { if (Array.isArray(lastValue)) { result = lastValue.shift(); if (lastValue.length === 0) { - delete this.last[identifier]; + delete this.last![identifier]; } } else { result = lastValue; - delete this.last[identifier]; + delete this.last![identifier]; } } if (result === void 0) { @@ -1832,7 +1848,7 @@ class ConfigurationParser { workspaceFolder: this.workspaceFolder, problemReporter: this.problemReporter, uuidMap: this.uuidMap, - namedProblemMatchers: undefined, + namedProblemMatchers: {}, engine, schemaVersion, platform: this.platform, @@ -1853,8 +1869,8 @@ class ConfigurationParser { return { custom: [], configured: [] }; } context.namedProblemMatchers = ProblemMatcherConverter.namedFrom(fileConfig.declares, context); - let globalTasks: Tasks.CustomTask[]; - let externalGlobalTasks: Array; + let globalTasks: Tasks.CustomTask[] | undefined = undefined; + let externalGlobalTasks: Array | undefined = undefined; if (fileConfig.windows && context.platform === Platform.Windows) { globalTasks = TaskParser.from(fileConfig.windows.tasks, globals, context).custom; externalGlobalTasks = fileConfig.windows.tasks; @@ -1877,7 +1893,7 @@ class ConfigurationParser { ); } - let result: TaskParseResult = { custom: undefined, configured: undefined }; + let result: TaskParseResult = { custom: [], configured: [] }; if (fileConfig.tasks) { result = TaskParser.from(fileConfig.tasks, globals, context); } @@ -1942,7 +1958,7 @@ export function parse(workspaceFolder: IWorkspaceFolder, platform: Platform, con } } -export function createCustomTask(contributedTask: Tasks.ContributedTask, configuredProps: Tasks.ConfigurationProperties & { _id: string; _source: Tasks.WorkspaceTaskSource, taskLoadMessages: string[] | undefined }): Tasks.CustomTask { +export function createCustomTask(contributedTask: Tasks.ContributedTask, configuredProps: Tasks.ConfiguringTask | Tasks.CustomTask): Tasks.CustomTask { return CustomTask.createCustomTask(contributedTask, configuredProps); } diff --git a/src/vs/workbench/parts/terminal/common/terminalService.ts b/src/vs/workbench/parts/terminal/common/terminalService.ts index 1ea70cdf23f..88a5fc320ab 100644 --- a/src/vs/workbench/parts/terminal/common/terminalService.ts +++ b/src/vs/workbench/parts/terminal/common/terminalService.ts @@ -30,25 +30,25 @@ export abstract class TerminalService implements ITerminalService { public get terminalInstances(): ITerminalInstance[] { return this._terminalInstances; } public get terminalTabs(): ITerminalTab[] { return this._terminalTabs; } - private readonly _onActiveTabChanged: Emitter = new Emitter(); + private readonly _onActiveTabChanged = new Emitter(); public get onActiveTabChanged(): Event { return this._onActiveTabChanged.event; } - protected readonly _onInstanceCreated: Emitter = new Emitter(); + protected readonly _onInstanceCreated = new Emitter(); public get onInstanceCreated(): Event { return this._onInstanceCreated.event; } - protected readonly _onInstanceDisposed: Emitter = new Emitter(); + protected readonly _onInstanceDisposed = new Emitter(); public get onInstanceDisposed(): Event { return this._onInstanceDisposed.event; } - protected readonly _onInstanceProcessIdReady: Emitter = new Emitter(); + protected readonly _onInstanceProcessIdReady = new Emitter(); public get onInstanceProcessIdReady(): Event { return this._onInstanceProcessIdReady.event; } - protected readonly _onInstanceRequestExtHostProcess: Emitter = new Emitter(); + protected readonly _onInstanceRequestExtHostProcess = new Emitter(); public get onInstanceRequestExtHostProcess(): Event { return this._onInstanceRequestExtHostProcess.event; } - protected readonly _onInstanceDimensionsChanged: Emitter = new Emitter(); + protected readonly _onInstanceDimensionsChanged = new Emitter(); public get onInstanceDimensionsChanged(): Event { return this._onInstanceDimensionsChanged.event; } - protected readonly _onInstancesChanged: Emitter = new Emitter(); + protected readonly _onInstancesChanged = new Emitter(); public get onInstancesChanged(): Event { return this._onInstancesChanged.event; } - protected readonly _onInstanceTitleChanged: Emitter = new Emitter(); + protected readonly _onInstanceTitleChanged = new Emitter(); public get onInstanceTitleChanged(): Event { return this._onInstanceTitleChanged.event; } - protected readonly _onActiveInstanceChanged: Emitter = new Emitter(); + protected readonly _onActiveInstanceChanged = new Emitter(); public get onActiveInstanceChanged(): Event { return this._onActiveInstanceChanged.event; } - protected readonly _onTabDisposed: Emitter = new Emitter(); + protected readonly _onTabDisposed = new Emitter(); public get onTabDisposed(): Event { return this._onTabDisposed.event; } public abstract get configHelper(): ITerminalConfigHelper; diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts index 2a655fa9dfc..814e8aaf1d5 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts @@ -94,27 +94,27 @@ export class TerminalInstance implements ITerminalInstance { public get shellLaunchConfig(): IShellLaunchConfig { return this._shellLaunchConfig; } public get commandTracker(): TerminalCommandTracker { return this._commandTracker; } - private readonly _onExit: Emitter = new Emitter(); + private readonly _onExit = new Emitter(); public get onExit(): Event { return this._onExit.event; } - private readonly _onDisposed: Emitter = new Emitter(); + private readonly _onDisposed = new Emitter(); public get onDisposed(): Event { return this._onDisposed.event; } - private readonly _onFocused: Emitter = new Emitter(); + private readonly _onFocused = new Emitter(); public get onFocused(): Event { return this._onFocused.event; } - private readonly _onProcessIdReady: Emitter = new Emitter(); + private readonly _onProcessIdReady = new Emitter(); public get onProcessIdReady(): Event { return this._onProcessIdReady.event; } - private readonly _onTitleChanged: Emitter = new Emitter(); + private readonly _onTitleChanged = new Emitter(); public get onTitleChanged(): Event { return this._onTitleChanged.event; } - private readonly _onData: Emitter = new Emitter(); + private readonly _onData = new Emitter(); public get onData(): Event { return this._onData.event; } - private readonly _onLineData: Emitter = new Emitter(); + private readonly _onLineData = new Emitter(); public get onLineData(): Event { return this._onLineData.event; } - private readonly _onRendererInput: Emitter = new Emitter(); + private readonly _onRendererInput = new Emitter(); public get onRendererInput(): Event { return this._onRendererInput.event; } - private readonly _onRequestExtHostProcess: Emitter = new Emitter(); + private readonly _onRequestExtHostProcess = new Emitter(); public get onRequestExtHostProcess(): Event { return this._onRequestExtHostProcess.event; } - private readonly _onDimensionsChanged: Emitter = new Emitter(); + private readonly _onDimensionsChanged = new Emitter(); public get onDimensionsChanged(): Event { return this._onDimensionsChanged.event; } - private readonly _onFocus: Emitter = new Emitter(); + private readonly _onFocus = new Emitter(); public get onFocus(): Event { return this._onFocus.event; } public constructor( diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts index 89eca9e2f91..bbbd8e6fbc6 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts @@ -96,6 +96,24 @@ export class TerminalPanel extends Panel { this._updateFont(); this._updateTheme(); + this._register(this.onDidChangeVisibility(visible => { + if (visible) { + if (this._terminalService.terminalInstances.length > 0) { + this._updateFont(); + this._updateTheme(); + } else { + // Check if instances were already restored as part of workbench restore + if (this._terminalService.terminalInstances.length === 0) { + this._terminalService.createTerminal(); + } + if (this._terminalService.terminalInstances.length > 0) { + this._updateFont(); + this._updateTheme(); + } + } + } + })); + // Force another layout (first is setContainers) since config has changed this.layout(new dom.Dimension(this._terminalContainer.offsetWidth, this._terminalContainer.offsetHeight)); } @@ -107,28 +125,6 @@ export class TerminalPanel extends Panel { this._terminalService.terminalTabs.forEach(t => t.layout(dimension.width, dimension.height)); } - public setVisible(visible: boolean): void { - if (visible) { - if (this._terminalService.terminalInstances.length > 0) { - this._updateFont(); - this._updateTheme(); - } else { - super.setVisible(visible); - // Check if instances were already restored as part of workbench restore - if (this._terminalService.terminalInstances.length === 0) { - this._terminalService.createTerminal(); - } - if (this._terminalService.terminalInstances.length > 0) { - this._updateFont(); - this._updateTheme(); - } - return; - } - } - super.setVisible(visible); - - } - public getActions(): IAction[] { if (!this._actions) { this._actions = [ diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts index 5eb78018793..ab36c8a10f1 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts @@ -41,13 +41,13 @@ export class TerminalProcessManager implements ITerminalProcessManager { private _preLaunchInputQueue: string[] = []; private _disposables: IDisposable[] = []; - private readonly _onProcessReady: Emitter = new Emitter(); + private readonly _onProcessReady = new Emitter(); public get onProcessReady(): Event { return this._onProcessReady.event; } - private readonly _onProcessData: Emitter = new Emitter(); + private readonly _onProcessData = new Emitter(); public get onProcessData(): Event { return this._onProcessData.event; } - private readonly _onProcessTitle: Emitter = new Emitter(); + private readonly _onProcessTitle = new Emitter(); public get onProcessTitle(): Event { return this._onProcessTitle.event; } - private readonly _onProcessExit: Emitter = new Emitter(); + private readonly _onProcessExit = new Emitter(); public get onProcessExit(): Event { return this._onProcessExit.event; } constructor( diff --git a/src/vs/workbench/parts/terminal/node/terminalProcess.ts b/src/vs/workbench/parts/terminal/node/terminalProcess.ts index 01d82d9f0e2..ae95fcf87f9 100644 --- a/src/vs/workbench/parts/terminal/node/terminalProcess.ts +++ b/src/vs/workbench/parts/terminal/node/terminalProcess.ts @@ -21,13 +21,13 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable { private _isDisposed: boolean = false; private _titleInterval: number = -1; - private readonly _onProcessData: Emitter = new Emitter(); + private readonly _onProcessData = new Emitter(); public get onProcessData(): Event { return this._onProcessData.event; } - private readonly _onProcessExit: Emitter = new Emitter(); + private readonly _onProcessExit = new Emitter(); public get onProcessExit(): Event { return this._onProcessExit.event; } - private readonly _onProcessIdReady: Emitter = new Emitter(); + private readonly _onProcessIdReady = new Emitter(); public get onProcessIdReady(): Event { return this._onProcessIdReady.event; } - private readonly _onProcessTitleChanged: Emitter = new Emitter(); + private readonly _onProcessTitleChanged = new Emitter(); public get onProcessTitleChanged(): Event { return this._onProcessTitleChanged.event; } constructor( diff --git a/src/vs/workbench/parts/terminal/node/terminalProcessExtHostProxy.ts b/src/vs/workbench/parts/terminal/node/terminalProcessExtHostProxy.ts index d3e9b3be184..831f8bdc353 100644 --- a/src/vs/workbench/parts/terminal/node/terminalProcessExtHostProxy.ts +++ b/src/vs/workbench/parts/terminal/node/terminalProcessExtHostProxy.ts @@ -13,20 +13,20 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten export class TerminalProcessExtHostProxy implements ITerminalChildProcess, ITerminalProcessExtHostProxy { private _disposables: IDisposable[] = []; - private readonly _onProcessData: Emitter = new Emitter(); + private readonly _onProcessData = new Emitter(); public get onProcessData(): Event { return this._onProcessData.event; } - private readonly _onProcessExit: Emitter = new Emitter(); + private readonly _onProcessExit = new Emitter(); public get onProcessExit(): Event { return this._onProcessExit.event; } - private readonly _onProcessIdReady: Emitter = new Emitter(); + private readonly _onProcessIdReady = new Emitter(); public get onProcessIdReady(): Event { return this._onProcessIdReady.event; } - private readonly _onProcessTitleChanged: Emitter = new Emitter(); + private readonly _onProcessTitleChanged = new Emitter(); public get onProcessTitleChanged(): Event { return this._onProcessTitleChanged.event; } - private readonly _onInput: Emitter = new Emitter(); + private readonly _onInput = new Emitter(); public get onInput(): Event { return this._onInput.event; } private readonly _onResize: Emitter<{ cols: number, rows: number }> = new Emitter<{ cols: number, rows: number }>(); public get onResize(): Event<{ cols: number, rows: number }> { return this._onResize.event; } - private readonly _onShutdown: Emitter = new Emitter(); + private readonly _onShutdown = new Emitter(); public get onShutdown(): Event { return this._onShutdown.event; } constructor( diff --git a/src/vs/workbench/parts/webview/electron-browser/webviewElement.ts b/src/vs/workbench/parts/webview/electron-browser/webviewElement.ts index 4b9f3eeb4b5..4460e9f461b 100644 --- a/src/vs/workbench/parts/webview/electron-browser/webviewElement.ts +++ b/src/vs/workbench/parts/webview/electron-browser/webviewElement.ts @@ -18,6 +18,7 @@ import { WebviewFindWidget } from './webviewFindWidget'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { endsWith } from 'vs/base/common/strings'; +import { isMacintosh } from 'vs/base/common/platform'; export interface WebviewOptions { readonly allowScripts?: boolean; @@ -40,7 +41,7 @@ interface IKeydownEvent { repeat: boolean; } -class WebviewProtocolRegister extends Disposable { +class WebviewProtocolProvider extends Disposable { constructor( webview: Electron.WebviewTag, private readonly _extensionLocation: URI, @@ -76,11 +77,152 @@ class WebviewProtocolRegister extends Disposable { ]); registerFileProtocol(contents, WebviewProtocol.VsCodeResource, this._fileService, this._extensionLocation, () => - (this._getLocalResourceRoots()) + this._getLocalResourceRoots() ); } } +class SvgBlocker extends Disposable { + + private readonly _onDidBlockSvg = this._register(new Emitter()); + public readonly onDidBlockSvg = this._onDidBlockSvg.event; + + constructor( + webview: Electron.WebviewTag, + private readonly _options: WebviewOptions, + ) { + super(); + + if (this._options.allowSvgs) { + return; + } + + let loaded = false; + this._register(addDisposableListener(webview, 'did-start-loading', () => { + if (loaded) { + return; + } + loaded = true; + + const contents = webview.getWebContents(); + if (!contents) { + return; + } + + contents.session.webRequest.onBeforeRequest((details, callback) => { + if (details.url.indexOf('.svg') > 0) { + const uri = URI.parse(details.url); + if (uri && !uri.scheme.match(/file/i) && endsWith(uri.path, '.svg') && !this.isAllowedSvg(uri)) { + this._onDidBlockSvg.fire(); + return callback({ cancel: true }); + } + } + return callback({}); + }); + + contents.session.webRequest.onHeadersReceived((details, callback) => { + const contentType: string[] = details.responseHeaders['content-type'] || details.responseHeaders['Content-Type']; + if (contentType && Array.isArray(contentType) && contentType.some(x => x.toLowerCase().indexOf('image/svg') >= 0)) { + const uri = URI.parse(details.url); + if (uri && !this.isAllowedSvg(uri)) { + this._onDidBlockSvg.fire(); + return callback({ cancel: true }); + } + } + return callback({ cancel: false, responseHeaders: details.responseHeaders }); + }); + })); + } + + private isAllowedSvg(uri: URI): boolean { + if (this._options.allowSvgs) { + return true; + } + if (this._options.svgWhiteList) { + return this._options.svgWhiteList.indexOf(uri.authority.toLowerCase()) >= 0; + } + return false; + } +} + +class WebviewKeyboardHandler extends Disposable { + constructor( + private readonly _webview: Electron.WebviewTag, + private readonly _keybindingService: IKeybindingService + ) { + super(); + + if (this.shouldToggleMenuShortcutsEnablement) { + this._register(addDisposableListener(this._webview, 'did-start-loading', () => { + const contents = this.getWebContents(); + if (contents) { + contents.on('before-input-event', (_event, input) => { + this.setIgnoreMenuShortcuts(input.control || input.meta); + }); + } + })); + } + + this._register(addDisposableListener(this._webview, 'ipc-message', (event) => { + switch (event.channel) { + case 'did-keydown': + // Electron: workaround for https://github.com/electron/electron/issues/14258 + // We have to detect keyboard events in the and dispatch them to our + // keybinding service because these events do not bubble to the parent window anymore. + this.handleKeydown(event.args[0]); + return; + + case 'did-blur': + this.setIgnoreMenuShortcuts(false); + return; + } + })); + } + + private get shouldToggleMenuShortcutsEnablement() { + return isMacintosh; + } + + private setIgnoreMenuShortcuts(value: boolean) { + if (!this.shouldToggleMenuShortcutsEnablement) { + return; + } + const contents = this.getWebContents(); + if (contents) { + contents.setIgnoreMenuShortcuts(value); + } + } + + private getWebContents(): Electron.WebContents | undefined { + const contents = this._webview.getWebContents(); + if (contents && !contents.isDestroyed()) { + return contents; + } + return undefined; + } + + private handleKeydown(event: IKeydownEvent): void { + // return; + // Create a fake KeyboardEvent from the data provided + const emulatedKeyboardEvent = new KeyboardEvent('keydown', { + code: event.code, + key: event.key, + keyCode: event.keyCode, + shiftKey: event.shiftKey, + altKey: event.altKey, + ctrlKey: event.ctrlKey, + metaKey: event.metaKey, + repeat: event.repeat + } as KeyboardEvent); + + // Dispatch through our keybinding service + // Note: we set the as target of the event so that scoped context key + // services function properly to enable commands like select all and find. + this._keybindingService.dispatchEvent(new StandardKeyboardEvent(emulatedKeyboardEvent), this._webview); + } +} + + export class WebviewElement extends Disposable { private _webview: Electron.WebviewTag; private _ready: Promise; @@ -128,50 +270,18 @@ export class WebviewElement extends Disposable { })); }); - this._register(new WebviewProtocolRegister( - this._webview, - this._options.extensionLocation, - () => this._options.localResourceRoots || [], - environmentService, - fileService)); + this._register( + new WebviewProtocolProvider( + this._webview, + this._options.extensionLocation, + () => (this._options.localResourceRoots || []), + environmentService, + fileService)); - if (!this._options.allowSvgs) { - let loaded = false; - this._register(addDisposableListener(this._webview, 'did-start-loading', () => { - if (loaded) { - return; - } - loaded = true; + const svgBlocker = this._register(new SvgBlocker(this._webview, this._options)); + svgBlocker.onDidBlockSvg(() => this.onDidBlockSvg()); - const contents = this._webview.getWebContents(); - if (!contents) { - return; - } - - contents.session.webRequest.onBeforeRequest((details, callback) => { - if (details.url.indexOf('.svg') > 0) { - const uri = URI.parse(details.url); - if (uri && !uri.scheme.match(/file/i) && endsWith(uri.path, '.svg') && !this.isAllowedSvg(uri)) { - this.onDidBlockSvg(); - return callback({ cancel: true }); - } - } - return callback({}); - }); - - contents.session.webRequest.onHeadersReceived((details, callback) => { - const contentType: string[] = details.responseHeaders['content-type'] || details.responseHeaders['Content-Type']; - if (contentType && Array.isArray(contentType) && contentType.some(x => x.toLowerCase().indexOf('image/svg') >= 0)) { - const uri = URI.parse(details.url); - if (uri && !this.isAllowedSvg(uri)) { - this.onDidBlockSvg(); - return callback({ cancel: true }); - } - } - return callback({ cancel: false, responseHeaders: details.responseHeaders }); - }); - })); - } + this._register(new WebviewKeyboardHandler(this._webview, this._keybindingService)); this._register(addDisposableListener(this._webview, 'console-message', function (e: { level: number; message: string; line: number; sourceId: string; }) { console.log(`[Embedded Page] ${e.message}`); @@ -224,13 +334,6 @@ export class WebviewElement extends Disposable { case 'did-blur': this.handleFocusChange(false); return; - - case 'did-keydown': - // Electron: workaround for https://github.com/electron/electron/issues/14258 - // We have to detect keyboard events in the and dispatch them to our - // keybinding service because these events do not bubble to the parent window anymore. - this.handleKeydown(event.args[0]); - return; } })); this._register(addDisposableListener(this._webview, 'devtools-opened', () => { @@ -339,26 +442,6 @@ export class WebviewElement extends Disposable { } } - private handleKeydown(event: IKeydownEvent): void { - - // Create a fake KeyboardEvent from the data provided - const emulatedKeyboardEvent = new KeyboardEvent('keydown', { - code: event.code, - key: event.key, - keyCode: event.keyCode, - shiftKey: event.shiftKey, - altKey: event.altKey, - ctrlKey: event.ctrlKey, - metaKey: event.metaKey, - repeat: event.repeat - } as KeyboardEvent); - - // Dispatch through our keybinding service - // Note: we set the as target of the event so that scoped context key - // services function properly to enable commands like select all and find. - this._keybindingService.dispatchEvent(new StandardKeyboardEvent(emulatedKeyboardEvent), this._webview); - } - public sendMessage(data: any): void { this._send('message', data); } @@ -422,16 +505,6 @@ export class WebviewElement extends Disposable { }); } - private isAllowedSvg(uri: URI): boolean { - if (this._options.allowSvgs) { - return true; - } - if (this._options.svgWhiteList) { - return this._options.svgWhiteList.indexOf(uri.authority.toLowerCase()) >= 0; - } - return false; - } - public startFind(value: string, options?: Electron.FindInPageOptions) { if (!value) { return; diff --git a/src/vs/workbench/services/configuration/common/configurationExtensionPoint.ts b/src/vs/workbench/services/configuration/common/configurationExtensionPoint.ts index e44874aa4fe..c3acf23eeed 100644 --- a/src/vs/workbench/services/configuration/common/configurationExtensionPoint.ts +++ b/src/vs/workbench/services/configuration/common/configurationExtensionPoint.ts @@ -97,7 +97,7 @@ const defaultConfigurationExtPoint = ExtensionsRegistry.registerExtensionPoint { registeredDefaultConfigurations = extensions.map(extension => { - const id = extension.description.id; + const id = extension.description.identifier; const name = extension.description.name; const defaults = objects.deepClone(extension.value); return { @@ -135,9 +135,9 @@ configurationExtPoint.setHandler(extensions => { validateProperties(configuration, extension); - configuration.id = node.id || extension.description.id || extension.description.uuid; + configuration.id = node.id || extension.description.identifier.value; configuration.contributedByExtension = true; - configuration.title = configuration.title || extension.description.displayName || extension.description.id; + configuration.title = configuration.title || extension.description.displayName || extension.description.identifier.value; configurations.push(configuration); } diff --git a/src/vs/workbench/services/configuration/node/configurationService.ts b/src/vs/workbench/services/configuration/node/configurationService.ts index e879455a601..6a4e63bc7b0 100644 --- a/src/vs/workbench/services/configuration/node/configurationService.ts +++ b/src/vs/workbench/services/configuration/node/configurationService.ts @@ -52,7 +52,7 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat private workspaceEditingQueue: Queue; - protected readonly _onDidChangeConfiguration: Emitter = this._register(new Emitter({ leakWarningThreshold: 500 })); + protected readonly _onDidChangeConfiguration: Emitter = this._register(new Emitter()); public readonly onDidChangeConfiguration: Event = this._onDidChangeConfiguration.event; protected readonly _onDidChangeWorkspaceFolders: Emitter = this._register(new Emitter()); diff --git a/src/vs/workbench/services/decorations/browser/decorationsService.ts b/src/vs/workbench/services/decorations/browser/decorationsService.ts index fbe224d33d3..2e3b151afc9 100644 --- a/src/vs/workbench/services/decorations/browser/decorationsService.ts +++ b/src/vs/workbench/services/decorations/browser/decorationsService.ts @@ -339,7 +339,7 @@ export class FileDecorationsService implements IDecorationsService { private readonly _data = new LinkedList(); private readonly _onDidChangeDecorationsDelayed = new Emitter(); - private readonly _onDidChangeDecorations = new Emitter({ leakWarningThreshold: 500 }); + private readonly _onDidChangeDecorations = new Emitter(); private readonly _decorationStyles: DecorationStyles; private readonly _disposables: IDisposable[]; diff --git a/src/vs/workbench/services/extensions/common/extensions.ts b/src/vs/workbench/services/extensions/common/extensions.ts index ab067f5c283..e96de026549 100644 --- a/src/vs/workbench/services/extensions/common/extensions.ts +++ b/src/vs/workbench/services/extensions/common/extensions.ts @@ -8,9 +8,10 @@ import Severity from 'vs/base/common/severity'; import { URI } from 'vs/base/common/uri'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IExtensionPoint } from 'vs/workbench/services/extensions/common/extensionsRegistry'; +import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; export interface IExtensionDescription { - readonly id: string; + readonly identifier: CanonicalExtensionIdentifier; readonly name: string; readonly uuid?: string; readonly displayName?: string; @@ -34,7 +35,7 @@ export interface IExtensionDescription { } export const nullExtensionDescription = Object.freeze({ - id: 'nullExtensionDescription', + identifier: new CanonicalExtensionIdentifier('nullExtensionDescription'), name: 'Null Extension Description', version: '0.0.0', publisher: 'vscode', @@ -49,7 +50,7 @@ export const IExtensionService = createDecorator('extensionSe export interface IMessage { type: Severity; message: string; - extensionId: string; + extensionId: CanonicalExtensionIdentifier; extensionPointId: string; } @@ -154,7 +155,7 @@ export interface IExtensionService extends ICpuProfilerTarget { * Fired when extensions status changes. * The event contains the ids of the extensions that have changed. */ - onDidChangeExtensionsStatus: Event; + onDidChangeExtensionsStatus: Event; /** * An event that is fired when activation happens. @@ -244,5 +245,5 @@ export function checkProposedApiEnabled(extension: IExtensionDescription): void } export function throwProposedApiError(extension: IExtensionDescription): never { - throw new Error(`[${extension.id}]: Proposed API is only available when running out of dev or with the following command line switch: --enable-proposed-api ${extension.id}`); + throw new Error(`[${extension.identifier.value}]: Proposed API is only available when running out of dev or with the following command line switch: --enable-proposed-api ${extension.identifier.value}`); } diff --git a/src/vs/workbench/services/extensions/common/extensionsRegistry.ts b/src/vs/workbench/services/extensions/common/extensionsRegistry.ts index d0d8adac639..e9187014ccb 100644 --- a/src/vs/workbench/services/extensions/common/extensionsRegistry.ts +++ b/src/vs/workbench/services/extensions/common/extensionsRegistry.ts @@ -35,7 +35,7 @@ export class ExtensionMessageCollector { this._messageHandler({ type: type, message: message, - extensionId: this._extension.id, + extensionId: this._extension.identifier, extensionPointId: this._extensionPointId }); } diff --git a/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts b/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts index ec7179e5bca..8a749895af5 100644 --- a/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts +++ b/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts @@ -16,7 +16,7 @@ import { URI } from 'vs/base/common/uri'; import * as pfs from 'vs/base/node/pfs'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { BUILTIN_MANIFEST_CACHE_FILE, MANIFEST_CACHE_FOLDER, USER_MANIFEST_CACHE_FILE } from 'vs/platform/extensions/common/extensions'; +import { BUILTIN_MANIFEST_CACHE_FILE, MANIFEST_CACHE_FOLDER, USER_MANIFEST_CACHE_FILE, CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import pkg from 'vs/platform/node/package'; import product from 'vs/platform/node/product'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; @@ -66,24 +66,32 @@ export class CachedExtensionScanner { public startScanningExtensions(log: ILog): void { CachedExtensionScanner._scanInstalledExtensions(this._windowService, this._notificationService, this._environmentService, this._extensionEnablementService, log) .then(({ system, user, development }) => { - let result: { [extensionId: string]: IExtensionDescription; } = {}; + let result = new Map(); system.forEach((systemExtension) => { - result[systemExtension.id] = systemExtension; + const extensionKey = CanonicalExtensionIdentifier.toKey(systemExtension.identifier); + if (result.has(extensionKey)) { + log.warn(systemExtension.extensionLocation.fsPath, nls.localize('overwritingExtension', "Overwriting extension {0} with {1}.", result.get(extensionKey).extensionLocation.fsPath, systemExtension.extensionLocation.fsPath)); + } + result.set(extensionKey, systemExtension); }); user.forEach((userExtension) => { - if (result.hasOwnProperty(userExtension.id)) { - log.warn(userExtension.extensionLocation.fsPath, nls.localize('overwritingExtension', "Overwriting extension {0} with {1}.", result[userExtension.id].extensionLocation.fsPath, userExtension.extensionLocation.fsPath)); + const extensionKey = CanonicalExtensionIdentifier.toKey(userExtension.identifier); + if (result.has(extensionKey)) { + log.warn(userExtension.extensionLocation.fsPath, nls.localize('overwritingExtension', "Overwriting extension {0} with {1}.", result.get(extensionKey).extensionLocation.fsPath, userExtension.extensionLocation.fsPath)); } - result[userExtension.id] = userExtension; + result.set(extensionKey, userExtension); }); development.forEach(developedExtension => { log.info('', nls.localize('extensionUnderDevelopment', "Loading development extension at {0}", developedExtension.extensionLocation.fsPath)); - if (result.hasOwnProperty(developedExtension.id)) { - log.warn(developedExtension.extensionLocation.fsPath, nls.localize('overwritingExtension', "Overwriting extension {0} with {1}.", result[developedExtension.id].extensionLocation.fsPath, developedExtension.extensionLocation.fsPath)); + const extensionKey = CanonicalExtensionIdentifier.toKey(developedExtension.identifier); + if (result.has(extensionKey)) { + log.warn(developedExtension.extensionLocation.fsPath, nls.localize('overwritingExtension', "Overwriting extension {0} with {1}.", result.get(extensionKey).extensionLocation.fsPath, developedExtension.extensionLocation.fsPath)); } - result[developedExtension.id] = developedExtension; + result.set(extensionKey, developedExtension); }); - return Object.keys(result).map(name => result[name]); + let r: IExtensionDescription[] = []; + result.forEach((value) => r.push(value)); + return r; }) .then(this._scannedExtensionsResolve, this._scannedExtensionsReject); } diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts index 11ddcb6af1f..53a6b9389e5 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts @@ -10,7 +10,6 @@ import { Server, Socket, createServer } from 'net'; import { getPathFromAmdModule } from 'vs/base/common/amd'; import { timeout } from 'vs/base/common/async'; import { toErrorMessage } from 'vs/base/common/errorMessage'; -import { onUnexpectedError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; @@ -21,7 +20,7 @@ import { URI } from 'vs/base/common/uri'; import { IRemoteConsoleLog, log, parse } from 'vs/base/node/console'; import { findFreePort, randomPort } from 'vs/base/node/ports'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/node/ipc'; -import { Protocol, generateRandomPipeName } from 'vs/base/parts/ipc/node/ipc.net'; +import { Protocol, generateRandomPipeName, BufferedProtocol } from 'vs/base/parts/ipc/node/ipc.net'; import { IBroadcast, IBroadcastService } from 'vs/platform/broadcast/electron-browser/broadcastService'; import { getScopes } from 'vs/platform/configuration/common/configurationRegistry'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; @@ -351,7 +350,11 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { this._namedPipeServer.close(); this._namedPipeServer = null; this._extensionHostConnection = socket; - resolve(new Protocol(this._extensionHostConnection)); + + // using a buffered message protocol here because between now + // and the first time a `then` executes some messages might be lost + // unless we immediately register a listener for `onMessage`. + resolve(new BufferedProtocol(new Protocol(this._extensionHostConnection))); }); }).then((protocol) => { @@ -397,10 +400,7 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { disposable.dispose(); // release this promise - // using a buffered message protocol here because between now - // and the first time a `then` executes some messages might be lost - // unless we immediately register a listener for `onMessage`. - resolve(new BufferedMessagePassingProtocol(protocol)); + resolve(protocol); return; } @@ -569,57 +569,3 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { } } } - -/** - * Will ensure no messages are lost from creation time until the first user of onMessage comes in. - */ -class BufferedMessagePassingProtocol implements IMessagePassingProtocol { - - private readonly _actual: IMessagePassingProtocol; - private _bufferedMessagesListener: IDisposable; - private _bufferedMessages: Buffer[]; - - constructor(actual: IMessagePassingProtocol) { - this._actual = actual; - this._bufferedMessages = []; - this._bufferedMessagesListener = this._actual.onMessage((buff) => this._bufferedMessages.push(buff)); - } - - public send(buffer: Buffer): void { - this._actual.send(buffer); - } - - public onMessage(listener: (e: Buffer) => any, thisArgs?: any, disposables?: IDisposable[]): IDisposable { - if (!this._bufferedMessages) { - // second caller gets nothing - return this._actual.onMessage(listener, thisArgs, disposables); - } - - // prepare result - const result = this._actual.onMessage(listener, thisArgs, disposables); - - // stop listening to buffered messages - this._bufferedMessagesListener.dispose(); - - // capture buffered messages - const bufferedMessages = this._bufferedMessages; - this._bufferedMessages = null; - - // it is important to deliver these messages after this call, but before - // other messages have a chance to be received (to guarantee in order delivery) - // that's why we're using here nextTick and not other types of timeouts - process.nextTick(() => { - // deliver buffered messages - while (bufferedMessages.length > 0) { - const msg = bufferedMessages.shift(); - try { - listener.call(thisArgs, msg); - } catch (e) { - onUnexpectedError(e); - } - } - }); - - return result; - } -} diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts index 55354a43674..44cd272a47e 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts @@ -18,6 +18,7 @@ import { ExtensionHostProfiler } from 'vs/workbench/services/extensions/electron import { ProxyIdentifier } from 'vs/workbench/services/extensions/node/proxyIdentifier'; import { IRPCProtocolLogger, RPCProtocol, RequestInitiator, ResponsiveState } from 'vs/workbench/services/extensions/node/rpcProtocol'; import { ResolvedAuthority } from 'vs/platform/remote/common/remoteAuthorityResolver'; +import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; // Enable to see detailed message communication between window and extension host const LOG_EXTENSION_HOST_COMMUNICATION = false; @@ -177,7 +178,7 @@ export class ExtensionHostProcessManager extends Disposable { return this._extensionHostProcessProxy.then(proxy => proxy.value.$resolveAuthority(remoteAuthority)); } - public start(enabledExtensionIds: string[]): Promise { + public start(enabledExtensionIds: CanonicalExtensionIdentifier[]): Promise { return this._extensionHostProcessProxy.then(proxy => proxy.value.$startExtensionHost(enabledExtensionIds)); } } diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts index aa86775141b..3cc306bd9d8 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts @@ -56,7 +56,7 @@ export class ExtensionHostProfiler { } else if (segmentId === 'self' && node.callFrame.url) { let extension = searchTree.findSubstr(node.callFrame.url); if (extension) { - segmentId = extension.id; + segmentId = extension.identifier.value; } } idsToSegmentId.set(node.id, segmentId); diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index f45b5c6d130..1f168541e8a 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -29,6 +29,7 @@ import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/n import { ResponsiveState } from 'vs/workbench/services/extensions/node/rpcProtocol'; import { CachedExtensionScanner, Logger } from 'vs/workbench/services/extensions/electron-browser/cachedExtensionScanner'; import { ExtensionHostProcessManager } from 'vs/workbench/services/extensions/electron-browser/extensionHostProcessManager'; +import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; const hasOwnProperty = Object.hasOwnProperty; const NO_OP_VOID_PROMISE = Promise.resolve(void 0); @@ -36,6 +37,18 @@ const DYNAMIC_EXTENSION_POINTS = false; schema.properties.engines.properties.vscode.default = `^${pkg.version}`; +let productAllowProposedApi: Set = null; +function allowProposedApiFromProduct(id: CanonicalExtensionIdentifier): boolean { + // create set if needed + if (productAllowProposedApi === null) { + productAllowProposedApi = new Set(); + if (isNonEmptyArray(product.extensionAllowedProposedApi)) { + product.extensionAllowedProposedApi.forEach((id) => productAllowProposedApi.add(CanonicalExtensionIdentifier.toKey(id))); + } + } + return productAllowProposedApi.has(CanonicalExtensionIdentifier.toKey(id)); +} + export class ExtensionService extends Disposable implements IExtensionService { public _serviceBrand: any; @@ -44,15 +57,15 @@ export class ExtensionService extends Disposable implements IExtensionService { private _registry: ExtensionDescriptionRegistry; private readonly _installedExtensionsReady: Barrier; private readonly _isDev: boolean; - private readonly _extensionsMessages: { [id: string]: IMessage[] }; + private readonly _extensionsMessages: Map; private _allRequestedActivateEvents: { [activationEvent: string]: boolean; }; private readonly _extensionScanner: CachedExtensionScanner; - private readonly _onDidRegisterExtensions: Emitter = this._register(new Emitter({ leakWarningThreshold: 500 })); + private readonly _onDidRegisterExtensions: Emitter = this._register(new Emitter()); public readonly onDidRegisterExtensions = this._onDidRegisterExtensions.event; - private readonly _onDidChangeExtensionsStatus: Emitter = this._register(new Emitter()); - public readonly onDidChangeExtensionsStatus: Event = this._onDidChangeExtensionsStatus.event; + private readonly _onDidChangeExtensionsStatus: Emitter = this._register(new Emitter()); + public readonly onDidChangeExtensionsStatus: Event = this._onDidChangeExtensionsStatus.event; private readonly _onWillActivateByEvent = this._register(new Emitter()); public readonly onWillActivateByEvent: Event = this._onWillActivateByEvent.event; @@ -62,9 +75,9 @@ export class ExtensionService extends Disposable implements IExtensionService { // --- Members used per extension host process private _extensionHostProcessManagers: ExtensionHostProcessManager[]; - private _extensionHostActiveExtensions: { [id: string]: boolean; }; - private _extensionHostProcessActivationTimes: { [id: string]: ActivationTimes; }; - private _extensionHostExtensionRuntimeErrors: { [id: string]: Error[]; }; + private _extensionHostActiveExtensions: Map; + private _extensionHostProcessActivationTimes: Map; + private _extensionHostExtensionRuntimeErrors: Map; constructor( @IInstantiationService private readonly _instantiationService: IInstantiationService, @@ -81,14 +94,14 @@ export class ExtensionService extends Disposable implements IExtensionService { this._registry = null; this._installedExtensionsReady = new Barrier(); this._isDev = !this._environmentService.isBuilt || this._environmentService.isExtensionDevelopment; - this._extensionsMessages = {}; + this._extensionsMessages = new Map(); this._allRequestedActivateEvents = Object.create(null); this._extensionScanner = this._instantiationService.createInstance(CachedExtensionScanner); this._extensionHostProcessManagers = []; - this._extensionHostActiveExtensions = Object.create(null); - this._extensionHostProcessActivationTimes = Object.create(null); - this._extensionHostExtensionRuntimeErrors = Object.create(null); + this._extensionHostActiveExtensions = new Map(); + this._extensionHostProcessActivationTimes = new Map(); + this._extensionHostExtensionRuntimeErrors = new Map(); this._startDelayed(this._lifecycleService); @@ -121,7 +134,7 @@ export class ExtensionService extends Disposable implements IExtensionService { return false; } - this._registry.remove(extension.id); + this._registry.remove(extension.identifier); // TODO@Alex: remove from the extension host const affectedExtensionPoints: { [extPointName: string]: boolean; } = Object.create(null); @@ -147,7 +160,7 @@ export class ExtensionService extends Disposable implements IExtensionService { } private _canRemoveExtension(extension: IExtensionDescription): boolean { - if (this._extensionHostActiveExtensions[extension.id]) { + if (this._extensionHostActiveExtensions.has(CanonicalExtensionIdentifier.toKey(extension.identifier))) { // Extension is running, cannot remove it safely return false; } @@ -205,15 +218,18 @@ export class ExtensionService extends Disposable implements IExtensionService { } private _stopExtensionHostProcess(): void { - const previouslyActivatedExtensionIds = Object.keys(this._extensionHostProcessActivationTimes); + let previouslyActivatedExtensionIds: CanonicalExtensionIdentifier[] = []; + this._extensionHostActiveExtensions.forEach((value) => { + previouslyActivatedExtensionIds.push(value); + }); for (let i = 0; i < this._extensionHostProcessManagers.length; i++) { this._extensionHostProcessManagers[i].dispose(); } this._extensionHostProcessManagers = []; - this._extensionHostActiveExtensions = Object.create(null); - this._extensionHostProcessActivationTimes = Object.create(null); - this._extensionHostExtensionRuntimeErrors = Object.create(null); + this._extensionHostActiveExtensions = new Map(); + this._extensionHostProcessActivationTimes = new Map(); + this._extensionHostExtensionRuntimeErrors = new Map(); if (previouslyActivatedExtensionIds.length > 0) { this._onDidChangeExtensionsStatus.fire(previouslyActivatedExtensionIds); @@ -343,11 +359,11 @@ export class ExtensionService extends Disposable implements IExtensionService { const extensions = this._registry.getAllExtensionDescriptions(); for (let i = 0, len = extensions.length; i < len; i++) { const extension = extensions[i]; - const id = extension.id; - result[id] = { - messages: this._extensionsMessages[id], - activationTimes: this._extensionHostProcessActivationTimes[id], - runtimeErrors: this._extensionHostExtensionRuntimeErrors[id], + const extensionKey = CanonicalExtensionIdentifier.toKey(extension.identifier); + result[extension.identifier.value] = { + messages: this._extensionsMessages.get(extensionKey), + activationTimes: this._extensionHostProcessActivationTimes.get(extensionKey), + runtimeErrors: this._extensionHostExtensionRuntimeErrors.get(extensionKey), }; } } @@ -397,7 +413,7 @@ export class ExtensionService extends Disposable implements IExtensionService { const extensionHost = this._extensionHostProcessManagers[0]; const extensions = await this._extensionScanner.scannedExtensions; const enabledExtensions = await this._getRuntimeExtensions(extensions); - extensionHost.start(enabledExtensions.map(extension => extension.id)); + extensionHost.start(enabledExtensions.map(extension => extension.identifier)); this._onHasExtensions(enabledExtensions); } @@ -416,54 +432,59 @@ export class ExtensionService extends Disposable implements IExtensionService { perf.mark('extensionHostReady'); this._installedExtensionsReady.open(); this._onDidRegisterExtensions.fire(void 0); - this._onDidChangeExtensionsStatus.fire(availableExtensions.map(e => e.id)); + this._onDidChangeExtensionsStatus.fire(availableExtensions.map(e => e.identifier)); } private _getRuntimeExtensions(allExtensions: IExtensionDescription[]): Promise { return this._extensionEnablementService.getDisabledExtensions() .then(disabledExtensions => { - const result: { [extensionId: string]: IExtensionDescription; } = {}; + const runtimeExtensions: IExtensionDescription[] = []; const extensionsToDisable: IExtensionIdentifier[] = []; const userMigratedSystemExtensions: IExtensionIdentifier[] = [{ id: BetterMergeId }]; - const enableProposedApiFor: string | string[] = this._environmentService.args['enable-proposed-api'] || []; + let enableProposedApiFor: string | string[] = this._environmentService.args['enable-proposed-api'] || []; const notFound = (id: string) => nls.localize('notFound', "Extension \`{0}\` cannot use PROPOSED API as it cannot be found", id); if (enableProposedApiFor.length) { let allProposed = (enableProposedApiFor instanceof Array ? enableProposedApiFor : [enableProposedApiFor]); allProposed.forEach(id => { - if (!allExtensions.some(description => description.id === id)) { + if (!allExtensions.some(description => CanonicalExtensionIdentifier.equals(description.identifier, id))) { console.error(notFound(id)); } }); + // Make enabled proposed API be lowercase for case insensitive comparison + if (Array.isArray(enableProposedApiFor)) { + enableProposedApiFor = enableProposedApiFor.map(id => id.toLowerCase()); + } else { + enableProposedApiFor = enableProposedApiFor.toLowerCase(); + } } const enableProposedApiForAll = !this._environmentService.isBuilt || - (!!this._environmentService.extensionDevelopmentLocationURI && product.nameLong.indexOf('Insiders') >= 0) || + (!!this._environmentService.extensionDevelopmentLocationURI && product.nameLong !== 'Visual Studio Code') || (enableProposedApiFor.length === 0 && 'enable-proposed-api' in this._environmentService.args); for (const extension of allExtensions) { const isExtensionUnderDevelopment = this._environmentService.isExtensionDevelopment && isEqualOrParent(extension.extensionLocation, this._environmentService.extensionDevelopmentLocationURI); // Do not disable extensions under development if (!isExtensionUnderDevelopment) { - if (disabledExtensions.some(disabled => areSameExtensions(disabled, extension))) { + if (disabledExtensions.some(disabled => areSameExtensions(disabled, { id: extension.identifier.value }))) { continue; } } if (!extension.isBuiltin) { // Check if the extension is changed to system extension - const userMigratedSystemExtension = userMigratedSystemExtensions.filter(userMigratedSystemExtension => areSameExtensions(userMigratedSystemExtension, { id: extension.id }))[0]; + const userMigratedSystemExtension = userMigratedSystemExtensions.filter(userMigratedSystemExtension => areSameExtensions(userMigratedSystemExtension, { id: extension.identifier.value }))[0]; if (userMigratedSystemExtension) { extensionsToDisable.push(userMigratedSystemExtension); continue; } } - result[extension.id] = this._updateEnableProposedApi(extension, enableProposedApiForAll, enableProposedApiFor); + runtimeExtensions.push(this._updateEnableProposedApi(extension, enableProposedApiForAll, enableProposedApiFor)); } - const runtimeExtensions = Object.keys(result).map(name => result[name]); this._telemetryService.publicLog('extensionsScanned', { totalCount: runtimeExtensions.length, @@ -486,9 +507,7 @@ export class ExtensionService extends Disposable implements IExtensionService { } private _updateEnableProposedApi(extension: IExtensionDescription, enableProposedApiForAll: boolean, enableProposedApiFor: string | string[]): IExtensionDescription { - if (isNonEmptyArray(product.extensionAllowedProposedApi) - && product.extensionAllowedProposedApi.indexOf(extension.id) >= 0 - ) { + if (allowProposedApiFromProduct(extension.identifier)) { // fast lane -> proposed api is available to all extensions // that are listed in product.json-files extension.enableProposedApi = true; @@ -496,29 +515,30 @@ export class ExtensionService extends Disposable implements IExtensionService { } else if (extension.enableProposedApi && !extension.isBuiltin) { if ( !enableProposedApiForAll && - enableProposedApiFor.indexOf(extension.id) < 0 + enableProposedApiFor.indexOf(extension.identifier.value.toLowerCase()) < 0 ) { extension.enableProposedApi = false; - console.error(`Extension '${extension.id} cannot use PROPOSED API (must started out of dev or enabled via --enable-proposed-api)`); + console.error(`Extension '${extension.identifier.value} cannot use PROPOSED API (must started out of dev or enabled via --enable-proposed-api)`); } else { // proposed api is available when developing or when an extension was explicitly // spelled out via a command line argument - console.warn(`Extension '${extension.id}' uses PROPOSED API which is subject to change and removal without notice.`); + console.warn(`Extension '${extension.identifier.value}' uses PROPOSED API which is subject to change and removal without notice.`); } } return extension; } private _handleExtensionPointMessage(msg: IMessage) { + const extensionKey = CanonicalExtensionIdentifier.toKey(msg.extensionId); - if (!this._extensionsMessages[msg.extensionId]) { - this._extensionsMessages[msg.extensionId] = []; + if (!this._extensionsMessages.has(extensionKey)) { + this._extensionsMessages.set(extensionKey, []); } - this._extensionsMessages[msg.extensionId].push(msg); + this._extensionsMessages.get(extensionKey).push(msg); const extension = this._registry.getExtensionDescription(msg.extensionId); - const strMsg = `[${msg.extensionId}]: ${msg.message}`; + const strMsg = `[${msg.extensionId.value}]: ${msg.message}`; if (extension && extension.isUnderDevelopment) { // This message is about the extension currently being developed this._showMessageToUser(msg.type, strMsg); @@ -537,7 +557,7 @@ export class ExtensionService extends Disposable implements IExtensionService { } */ this._telemetryService.publicLog('extensionsMessage', { - type, extensionId, extensionPointId, message + type, extensionId: extensionId.value, extensionPointId, message }); } } @@ -587,28 +607,30 @@ export class ExtensionService extends Disposable implements IExtensionService { } } - public _onWillActivateExtension(extensionId: string): void { - this._extensionHostActiveExtensions[extensionId] = true; + public _onWillActivateExtension(extensionId: CanonicalExtensionIdentifier): void { + this._extensionHostActiveExtensions.set(CanonicalExtensionIdentifier.toKey(extensionId), extensionId); } - public _onDidActivateExtension(extensionId: string, startup: boolean, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationEvent: string): void { - this._extensionHostProcessActivationTimes[extensionId] = new ActivationTimes(startup, codeLoadingTime, activateCallTime, activateResolvedTime, activationEvent); + public _onDidActivateExtension(extensionId: CanonicalExtensionIdentifier, startup: boolean, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationEvent: string): void { + this._extensionHostProcessActivationTimes.set(CanonicalExtensionIdentifier.toKey(extensionId), new ActivationTimes(startup, codeLoadingTime, activateCallTime, activateResolvedTime, activationEvent)); this._onDidChangeExtensionsStatus.fire([extensionId]); } - public _onExtensionRuntimeError(extensionId: string, err: Error): void { - if (!this._extensionHostExtensionRuntimeErrors[extensionId]) { - this._extensionHostExtensionRuntimeErrors[extensionId] = []; + public _onExtensionRuntimeError(extensionId: CanonicalExtensionIdentifier, err: Error): void { + const extensionKey = CanonicalExtensionIdentifier.toKey(extensionId); + if (!this._extensionHostExtensionRuntimeErrors.has(extensionKey)) { + this._extensionHostExtensionRuntimeErrors.set(extensionKey, []); } - this._extensionHostExtensionRuntimeErrors[extensionId].push(err); + this._extensionHostExtensionRuntimeErrors.get(extensionKey).push(err); this._onDidChangeExtensionsStatus.fire([extensionId]); } - public _addMessage(extensionId: string, severity: Severity, message: string): void { - if (!this._extensionsMessages[extensionId]) { - this._extensionsMessages[extensionId] = []; + public _addMessage(extensionId: CanonicalExtensionIdentifier, severity: Severity, message: string): void { + const extensionKey = CanonicalExtensionIdentifier.toKey(extensionId); + if (!this._extensionsMessages.has(extensionKey)) { + this._extensionsMessages.set(extensionKey, []); } - this._extensionsMessages[extensionId].push({ + this._extensionsMessages.get(extensionKey).push({ type: severity, message: message, extensionId: null, diff --git a/src/vs/workbench/services/extensions/electron-browser/inactiveExtensionUrlHandler.ts b/src/vs/workbench/services/extensions/electron-browser/inactiveExtensionUrlHandler.ts index 81ea673d7e2..0d1958f91c2 100644 --- a/src/vs/workbench/services/extensions/electron-browser/inactiveExtensionUrlHandler.ts +++ b/src/vs/workbench/services/extensions/electron-browser/inactiveExtensionUrlHandler.ts @@ -16,6 +16,7 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag import { IURLHandler, IURLService } from 'vs/platform/url/common/url'; import { IWindowService } from 'vs/platform/windows/common/windows'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; const FIVE_MINUTES = 5 * 60 * 1000; const THIRTY_SECONDS = 30 * 1000; @@ -29,8 +30,8 @@ export const IExtensionUrlHandler = createDecorator('inact export interface IExtensionUrlHandler { readonly _serviceBrand: any; - registerExtensionHandler(extensionId: string, handler: IURLHandler): void; - unregisterExtensionHandler(extensionId: string): void; + registerExtensionHandler(extensionId: CanonicalExtensionIdentifier, handler: IURLHandler): void; + unregisterExtensionHandler(extensionId: CanonicalExtensionIdentifier): void; } /** @@ -80,7 +81,7 @@ export class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { } const extensionId = uri.authority; - const wasHandlerAvailable = this.extensionHandlers.has(extensionId); + const wasHandlerAvailable = this.extensionHandlers.has(CanonicalExtensionIdentifier.toKey(extensionId)); const extension = await this.extensionService.getExtension(extensionId); if (!extension) { @@ -101,7 +102,7 @@ export class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { } } - const handler = this.extensionHandlers.get(extensionId); + const handler = this.extensionHandlers.get(CanonicalExtensionIdentifier.toKey(extensionId)); if (handler) { if (!wasHandlerAvailable) { @@ -115,34 +116,34 @@ export class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { // collect URI for eventual extension activation const timestamp = new Date().getTime(); - let uris = this.uriBuffer.get(extensionId); + let uris = this.uriBuffer.get(CanonicalExtensionIdentifier.toKey(extensionId)); if (!uris) { uris = []; - this.uriBuffer.set(extensionId, uris); + this.uriBuffer.set(CanonicalExtensionIdentifier.toKey(extensionId), uris); } uris.push({ timestamp, uri }); // activate the extension - await this.extensionService.activateByEvent(`onUri:${extensionId}`); + await this.extensionService.activateByEvent(`onUri:${CanonicalExtensionIdentifier.toKey(extensionId)}`); return true; } - registerExtensionHandler(extensionId: string, handler: IURLHandler): void { - this.extensionHandlers.set(extensionId, handler); + registerExtensionHandler(extensionId: CanonicalExtensionIdentifier, handler: IURLHandler): void { + this.extensionHandlers.set(CanonicalExtensionIdentifier.toKey(extensionId), handler); - const uris = this.uriBuffer.get(extensionId) || []; + const uris = this.uriBuffer.get(CanonicalExtensionIdentifier.toKey(extensionId)) || []; for (const { uri } of uris) { handler.handleURL(uri); } - this.uriBuffer.delete(extensionId); + this.uriBuffer.delete(CanonicalExtensionIdentifier.toKey(extensionId)); } - unregisterExtensionHandler(extensionId: string): void { - this.extensionHandlers.delete(extensionId); + unregisterExtensionHandler(extensionId: CanonicalExtensionIdentifier): void { + this.extensionHandlers.delete(CanonicalExtensionIdentifier.toKey(extensionId)); } private async handleUnhandledURL(uri: URI, extensionIdentifier: IExtensionIdentifier): Promise { diff --git a/src/vs/workbench/services/extensions/node/extensionDescriptionRegistry.ts b/src/vs/workbench/services/extensions/node/extensionDescriptionRegistry.ts index c470b619890..1734019932d 100644 --- a/src/vs/workbench/services/extensions/node/extensionDescriptionRegistry.ts +++ b/src/vs/workbench/services/extensions/node/extensionDescriptionRegistry.ts @@ -4,14 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; - -const hasOwnProperty = Object.hasOwnProperty; +import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; export class ExtensionDescriptionRegistry { private _extensionDescriptions: IExtensionDescription[]; - private _extensionsMap: { [extensionId: string]: IExtensionDescription; }; + private _extensionsMap: Map; private _extensionsArr: IExtensionDescription[]; - private _activationMap: { [activationEvent: string]: IExtensionDescription[]; }; + private _activationMap: Map; constructor(extensionDescriptions: IExtensionDescription[]) { this._extensionDescriptions = extensionDescriptions; @@ -19,20 +18,20 @@ export class ExtensionDescriptionRegistry { } private _initialize(): void { - this._extensionsMap = {}; + this._extensionsMap = new Map(); this._extensionsArr = []; - this._activationMap = {}; + this._activationMap = new Map(); for (let i = 0, len = this._extensionDescriptions.length; i < len; i++) { let extensionDescription = this._extensionDescriptions[i]; - if (hasOwnProperty.call(this._extensionsMap, extensionDescription.id)) { + if (this._extensionsMap.has(CanonicalExtensionIdentifier.toKey(extensionDescription.identifier))) { // No overwriting allowed! - console.error('Extension `' + extensionDescription.id + '` is already registered'); + console.error('Extension `' + extensionDescription.identifier.value + '` is already registered'); continue; } - this._extensionsMap[extensionDescription.id] = extensionDescription; + this._extensionsMap.set(CanonicalExtensionIdentifier.toKey(extensionDescription.identifier), extensionDescription); this._extensionsArr.push(extensionDescription); if (Array.isArray(extensionDescription.activationEvents)) { @@ -41,47 +40,49 @@ export class ExtensionDescriptionRegistry { // TODO@joao: there's no easy way to contribute this if (activationEvent === 'onUri') { - activationEvent = `onUri:${extensionDescription.id}`; + activationEvent = `onUri:${CanonicalExtensionIdentifier.toKey(extensionDescription.identifier)}`; } - this._activationMap[activationEvent] = this._activationMap[activationEvent] || []; - this._activationMap[activationEvent].push(extensionDescription); + if (!this._activationMap.has(activationEvent)) { + this._activationMap.set(activationEvent, []); + } + this._activationMap.get(activationEvent).push(extensionDescription); } } } } - public keepOnly(extensionIds: string[]): void { + public keepOnly(extensionIds: CanonicalExtensionIdentifier[]): void { let toKeep = new Set(); - extensionIds.forEach(extensionId => toKeep.add(extensionId)); - this._extensionDescriptions = this._extensionDescriptions.filter(extension => toKeep.has(extension.id)); + extensionIds.forEach(extensionId => toKeep.add(CanonicalExtensionIdentifier.toKey(extensionId))); + this._extensionDescriptions = this._extensionDescriptions.filter(extension => toKeep.has(CanonicalExtensionIdentifier.toKey(extension.identifier))); this._initialize(); } - public remove(extensionId: string): void { - this._extensionDescriptions = this._extensionDescriptions.filter(extension => extension.id !== extensionId); + public remove(extensionId: CanonicalExtensionIdentifier): void { + this._extensionDescriptions = this._extensionDescriptions.filter(extension => !CanonicalExtensionIdentifier.equals(extension.identifier, extensionId)); this._initialize(); } public containsActivationEvent(activationEvent: string): boolean { - return hasOwnProperty.call(this._activationMap, activationEvent); + return this._activationMap.has(activationEvent); } public getExtensionDescriptionsForActivationEvent(activationEvent: string): IExtensionDescription[] { - if (!hasOwnProperty.call(this._activationMap, activationEvent)) { + if (!this._activationMap.has(activationEvent)) { return []; } - return this._activationMap[activationEvent].slice(0); + return this._activationMap.get(activationEvent).slice(0); } public getAllExtensionDescriptions(): IExtensionDescription[] { return this._extensionsArr.slice(0); } - public getExtensionDescription(extensionId: string): IExtensionDescription | null { - if (!hasOwnProperty.call(this._extensionsMap, extensionId)) { + public getExtensionDescription(extensionId: CanonicalExtensionIdentifier | string): IExtensionDescription | null { + if (!this._extensionsMap.has(CanonicalExtensionIdentifier.toKey(extensionId))) { return null; } - return this._extensionsMap[extensionId]; + return this._extensionsMap.get(CanonicalExtensionIdentifier.toKey(extensionId)); } } diff --git a/src/vs/workbench/services/extensions/node/extensionPoints.ts b/src/vs/workbench/services/extensions/node/extensionPoints.ts index 50b38ceb6f2..62e8d76219b 100644 --- a/src/vs/workbench/services/extensions/node/extensionPoints.ts +++ b/src/vs/workbench/services/extensions/node/extensionPoints.ts @@ -16,6 +16,7 @@ import { getGalleryExtensionId, getLocalExtensionId, groupByExtension } from 'vs import { getIdAndVersionFromLocalExtensionId } from 'vs/platform/extensionManagement/node/extensionManagementUtil'; import { isValidExtensionVersion } from 'vs/platform/extensions/node/extensionValidator'; import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; +import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; const MANIFEST_FILE = 'package.json'; @@ -300,6 +301,8 @@ class ExtensionManifestNLSReplacer extends ExtensionManifestHandler { // Relax the readonly properties here, it is the one place where we check and normalize values export interface IRelaxedExtensionDescription { id: string; + uuid?: string; + identifier: CanonicalExtensionIdentifier; name: string; version: string; publisher: string; @@ -339,6 +342,7 @@ class ExtensionManifestValidator extends ExtensionManifestHandler { // id := `publisher.name` extensionDescription.id = `${extensionDescription.publisher}.${extensionDescription.name}`; + extensionDescription.identifier = new CanonicalExtensionIdentifier(extensionDescription.id); // main := absolutePath(`main`) if (extensionDescription.main) { @@ -577,7 +581,7 @@ export class ExtensionScanner { if (!isBuiltin) { // Filter out outdated extensions - const byExtension: IExtensionDescription[][] = groupByExtension(extensionDescriptions, e => ({ id: e.id, uuid: e.uuid })); + const byExtension: IExtensionDescription[][] = groupByExtension(extensionDescriptions, e => ({ id: e.identifier.value, uuid: e.uuid })); extensionDescriptions = byExtension.map(p => p.sort((a, b) => semver.rcompare(a.version, b.version))[0]); } @@ -624,11 +628,11 @@ export class ExtensionScanner { return Promise.all([builtinExtensions, extraBuiltinExtensions]).then(([builtinExtensions, extraBuiltinExtensions]) => { let resultMap: { [id: string]: IExtensionDescription; } = Object.create(null); for (let i = 0, len = builtinExtensions.length; i < len; i++) { - resultMap[builtinExtensions[i].id] = builtinExtensions[i]; + resultMap[CanonicalExtensionIdentifier.toKey(builtinExtensions[i].identifier)] = builtinExtensions[i]; } // Overwrite with extensions found in extra for (let i = 0, len = extraBuiltinExtensions.length; i < len; i++) { - resultMap[extraBuiltinExtensions[i].id] = extraBuiltinExtensions[i]; + resultMap[CanonicalExtensionIdentifier.toKey(extraBuiltinExtensions[i].identifier)] = extraBuiltinExtensions[i]; } let resultArr = Object.keys(resultMap).map((id) => resultMap[id]); diff --git a/src/vs/workbench/services/extensions/test/node/rpcProtocol.test.ts b/src/vs/workbench/services/extensions/test/node/rpcProtocol.test.ts index 73716a48619..5e7a6163c18 100644 --- a/src/vs/workbench/services/extensions/test/node/rpcProtocol.test.ts +++ b/src/vs/workbench/services/extensions/test/node/rpcProtocol.test.ts @@ -15,7 +15,7 @@ suite('RPCProtocol', () => { class MessagePassingProtocol implements IMessagePassingProtocol { private _pair: MessagePassingProtocol; - private readonly _onMessage: Emitter = new Emitter(); + private readonly _onMessage = new Emitter(); public readonly onMessage: Event = this._onMessage.event; public setPair(other: MessagePassingProtocol) { diff --git a/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts b/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts index 2b0a3c90883..7ce86f4d025 100644 --- a/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts +++ b/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts @@ -47,7 +47,7 @@ export class KeyboardMapperFactory { private _keyboardMapper: IKeyboardMapper | null; private _initialized: boolean; - private readonly _onDidChangeKeyboardMapper: Emitter = new Emitter(); + private readonly _onDidChangeKeyboardMapper = new Emitter(); public readonly onDidChangeKeyboardMapper: Event = this._onDidChangeKeyboardMapper.event; private constructor() { diff --git a/src/vs/workbench/services/keybinding/test/windowsKeyboardMapper.test.ts b/src/vs/workbench/services/keybinding/test/windowsKeyboardMapper.test.ts index 5fafd8cf166..7c3fdefcc64 100644 --- a/src/vs/workbench/services/keybinding/test/windowsKeyboardMapper.test.ts +++ b/src/vs/workbench/services/keybinding/test/windowsKeyboardMapper.test.ts @@ -17,7 +17,8 @@ async function createKeyboardMapper(isUSStandard: boolean, file: string): Promis } function _assertResolveKeybinding(mapper: WindowsKeyboardMapper, k: number, expected: IResolvedKeybinding[]): void { - assertResolveKeybinding(mapper, createKeybinding(k, OperatingSystem.Windows), expected); + const keyBinding = createKeybinding(k, OperatingSystem.Windows); + assertResolveKeybinding(mapper, keyBinding!, expected); } suite('keyboardMapper - WINDOWS de_ch', () => { @@ -73,7 +74,7 @@ suite('keyboardMapper - WINDOWS de_ch', () => { altKey: false, metaKey: false, keyCode: KeyCode.KEY_Z, - code: null + code: null! }, { label: 'Ctrl+Z', @@ -112,7 +113,7 @@ suite('keyboardMapper - WINDOWS de_ch', () => { altKey: false, metaKey: false, keyCode: KeyCode.US_CLOSE_SQUARE_BRACKET, - code: null + code: null! }, { label: 'Ctrl+^', @@ -255,7 +256,7 @@ suite('keyboardMapper - WINDOWS de_ch', () => { altKey: false, metaKey: false, keyCode: KeyCode.Home, - code: null + code: null! }, { label: 'Ctrl+Home', @@ -295,7 +296,7 @@ suite('keyboardMapper - WINDOWS de_ch', () => { altKey: false, metaKey: false, keyCode: KeyCode.Ctrl, - code: null + code: null! }, { label: 'Ctrl+', @@ -359,7 +360,7 @@ suite('keyboardMapper - WINDOWS en_us', () => { assertResolveUserBinding( mapper, new ScanCodeBinding(true, false, false, false, ScanCode.Comma), - null, + null!, [{ label: 'Ctrl+,', ariaLabel: 'Control+,', @@ -381,7 +382,7 @@ suite('keyboardMapper - WINDOWS en_us', () => { altKey: false, metaKey: false, keyCode: KeyCode.Ctrl, - code: null + code: null! }, { label: 'Ctrl+', @@ -417,7 +418,7 @@ suite('keyboardMapper - WINDOWS por_ptb', () => { altKey: false, metaKey: false, keyCode: KeyCode.ABNT_C1, - code: null + code: null! }, { label: 'Ctrl+/', @@ -440,7 +441,7 @@ suite('keyboardMapper - WINDOWS por_ptb', () => { altKey: false, metaKey: false, keyCode: KeyCode.ABNT_C2, - code: null + code: null! }, { label: 'Ctrl+.', diff --git a/src/vs/workbench/services/preferences/browser/preferencesService.ts b/src/vs/workbench/services/preferences/browser/preferencesService.ts index dcbdc06680a..0eafccadbef 100644 --- a/src/vs/workbench/services/preferences/browser/preferencesService.ts +++ b/src/vs/workbench/services/preferences/browser/preferencesService.ts @@ -45,7 +45,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic private lastOpenedSettingsInput: PreferencesEditorInput | null = null; - private readonly _onDispose: Emitter = new Emitter(); + private readonly _onDispose = new Emitter(); private _defaultUserSettingsUriCounter = 0; private _defaultUserSettingsContentModel: DefaultSettings; diff --git a/src/vs/workbench/services/preferences/common/preferencesModels.ts b/src/vs/workbench/services/preferences/common/preferencesModels.ts index a454297d044..c44e2937539 100644 --- a/src/vs/workbench/services/preferences/common/preferencesModels.ts +++ b/src/vs/workbench/services/preferences/common/preferencesModels.ts @@ -453,7 +453,7 @@ export class DefaultSettings extends Disposable { getContent(forceUpdate = false): string { if (!this._content || forceUpdate) { - this._content = this.toContent(true, this.getSettingsGroups(forceUpdate)); + this.initialize(); } return this._content; @@ -461,12 +461,17 @@ export class DefaultSettings extends Disposable { getSettingsGroups(forceUpdate = false): ISettingsGroup[] { if (!this._allSettingsGroups || forceUpdate) { - this._allSettingsGroups = this.parse(); + this.initialize(); } return this._allSettingsGroups; } + private initialize(): void { + this._allSettingsGroups = this.parse(); + this._content = this.toContent(true, this._allSettingsGroups); + } + private parse(): ISettingsGroup[] { const settingsGroups = this.getRegisteredGroups(); this.initAllSettingsMap(settingsGroups); diff --git a/src/vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl.ts b/src/vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl.ts index b34f4545be5..9308de75294 100644 --- a/src/vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl.ts +++ b/src/vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl.ts @@ -77,7 +77,7 @@ class RemoteAgentConnection extends Disposable implements IRemoteAgentConnection private _getOrCreateConnection(): Promise> { if (!this._connection) { this._connection = this._remoteAuthorityResolverService.resolveAuthority(this.remoteAuthority).then((resolvedAuthority) => { - return connectRemoteAgentManagement(this.remoteAuthority, resolvedAuthority.host, resolvedAuthority.port, `renderer`); + return connectRemoteAgentManagement(this.remoteAuthority, resolvedAuthority.host, resolvedAuthority.port, `renderer`, this._environmentService.isBuilt); }); } return this._connection; diff --git a/src/vs/workbench/services/search/node/searchHistoryService.ts b/src/vs/workbench/services/search/node/searchHistoryService.ts index ca4b8d334d9..34cb3afc571 100644 --- a/src/vs/workbench/services/search/node/searchHistoryService.ts +++ b/src/vs/workbench/services/search/node/searchHistoryService.ts @@ -13,7 +13,7 @@ export class SearchHistoryService implements ISearchHistoryService { private static readonly SEARCH_HISTORY_KEY = 'workbench.search.history'; - private readonly _onDidClearHistory: Emitter = new Emitter(); + private readonly _onDidClearHistory = new Emitter(); public readonly onDidClearHistory: Event = this._onDidClearHistory.event; constructor( diff --git a/src/vs/workbench/services/textMate/electron-browser/TMSyntax.ts b/src/vs/workbench/services/textMate/electron-browser/TMSyntax.ts index 7ef8cc05068..60ea1e6dc94 100644 --- a/src/vs/workbench/services/textMate/electron-browser/TMSyntax.ts +++ b/src/vs/workbench/services/textMate/electron-browser/TMSyntax.ts @@ -31,7 +31,7 @@ export class TMScopeRegistry { private _scopeNameToLanguageRegistration: { [scopeName: string]: TMLanguageRegistration; }; private _encounteredLanguages: boolean[]; - private readonly _onDidEncounterLanguage: Emitter = new Emitter(); + private readonly _onDidEncounterLanguage = new Emitter(); public readonly onDidEncounterLanguage: Event = this._onDidEncounterLanguage.event; constructor() { diff --git a/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts b/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts index 38845a0e2fc..8d2ccf80881 100644 --- a/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts +++ b/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts @@ -96,6 +96,10 @@ class ResourceModelCollection extends ReferenceCollection { const resource = URI.parse(key); const providers = this.providers[resource.scheme] || []; @@ -162,4 +166,8 @@ export class TextModelResolverService implements ITextModelService { registerTextModelContentProvider(scheme: string, provider: ITextModelContentProvider): IDisposable { return this.resourceModelCollection.registerTextModelContentProvider(scheme, provider); } + + hasTextModelContentProvider(scheme: string): boolean { + return this.resourceModelCollection.hasTextModelContentProvider(scheme); + } } diff --git a/src/vs/workbench/services/themes/electron-browser/colorThemeStore.ts b/src/vs/workbench/services/themes/electron-browser/colorThemeStore.ts index f40462cbae8..d9a95109acc 100644 --- a/src/vs/workbench/services/themes/electron-browser/colorThemeStore.ts +++ b/src/vs/workbench/services/themes/electron-browser/colorThemeStore.ts @@ -63,7 +63,7 @@ export class ColorThemeStore { themesExtPoint.setHandler((extensions) => { for (let ext of extensions) { let extensionData = { - extensionId: ext.description.id, + extensionId: ext.description.identifier.value, extensionPublisher: ext.description.publisher, extensionName: ext.description.name, extensionIsBuiltin: ext.description.isBuiltin diff --git a/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.ts b/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.ts index 333b229de08..238a2644b24 100644 --- a/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.ts +++ b/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.ts @@ -58,7 +58,7 @@ export class FileIconThemeStore { iconThemeExtPoint.setHandler((extensions) => { for (let ext of extensions) { let extensionData = { - extensionId: ext.description.id, + extensionId: ext.description.identifier.value, extensionPublisher: ext.description.publisher, extensionName: ext.description.name, extensionIsBuiltin: ext.description.isBuiltin diff --git a/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts index d5c5fa0653a..985f2617f21 100644 --- a/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts @@ -103,7 +103,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { this.colorThemeStore = new ColorThemeStore(extensionService, ColorThemeData.createLoadedEmptyTheme(DEFAULT_THEME_ID, DEFAULT_THEME_SETTING_VALUE)); this.onFileIconThemeChange = new Emitter(); this.iconThemeStore = new FileIconThemeStore(extensionService); - this.onColorThemeChange = new Emitter({ leakWarningThreshold: 500 }); + this.onColorThemeChange = new Emitter(); this.currentIconTheme = { id: '', diff --git a/src/vs/workbench/test/common/notifications.test.ts b/src/vs/workbench/test/common/notifications.test.ts index 282f6d63df5..69b3d465ea3 100644 --- a/src/vs/workbench/test/common/notifications.test.ts +++ b/src/vs/workbench/test/common/notifications.test.ts @@ -15,14 +15,14 @@ suite('Notifications', () => { // Invalid assert.ok(!NotificationViewItem.create({ severity: Severity.Error, message: '' })); - assert.ok(!NotificationViewItem.create({ severity: Severity.Error, message: null })); + assert.ok(!NotificationViewItem.create({ severity: Severity.Error, message: null! })); // Duplicates - let item1 = NotificationViewItem.create({ severity: Severity.Error, message: 'Error Message' }); - let item2 = NotificationViewItem.create({ severity: Severity.Error, message: 'Error Message' }); - let item3 = NotificationViewItem.create({ severity: Severity.Info, message: 'Info Message' }); - let item4 = NotificationViewItem.create({ severity: Severity.Error, message: 'Error Message', source: 'Source' }); - let item5 = NotificationViewItem.create({ severity: Severity.Error, message: 'Error Message', actions: { primary: [new Action('id', 'label')] } }); + let item1 = NotificationViewItem.create({ severity: Severity.Error, message: 'Error Message' })!; + let item2 = NotificationViewItem.create({ severity: Severity.Error, message: 'Error Message' })!; + let item3 = NotificationViewItem.create({ severity: Severity.Info, message: 'Info Message' })!; + let item4 = NotificationViewItem.create({ severity: Severity.Error, message: 'Error Message', source: 'Source' })!; + let item5 = NotificationViewItem.create({ severity: Severity.Error, message: 'Error Message', actions: { primary: [new Action('id', 'label')] } })!; assert.equal(item1.equals(item1), true); assert.equal(item2.equals(item2), true); @@ -102,11 +102,11 @@ suite('Notifications', () => { assert.equal(called, 1); // Error with Action - let item6 = NotificationViewItem.create({ severity: Severity.Error, message: createErrorWithActions('Hello Error', { actions: [new Action('id', 'label')] }) }); - assert.equal(item6.actions.primary.length, 1); + let item6 = NotificationViewItem.create({ severity: Severity.Error, message: createErrorWithActions('Hello Error', { actions: [new Action('id', 'label')] }) })!; + assert.equal(item6.actions.primary!.length, 1); // Links - let item7 = NotificationViewItem.create({ severity: Severity.Info, message: 'Unable to [Link 1](http://link1.com) open [Link 2](https://link2.com) and [Invalid Link3](ftp://link3.com)' }); + let item7 = NotificationViewItem.create({ severity: Severity.Info, message: 'Unable to [Link 1](http://link1.com) open [Link 2](https://link2.com) and [Invalid Link3](ftp://link3.com)' })!; const links = item7.message.links; assert.equal(links.length, 2); @@ -124,7 +124,7 @@ suite('Notifications', () => { test('Model', () => { const model = new NotificationsModel(); - let lastEvent: INotificationChangeEvent; + let lastEvent!: INotificationChangeEvent; model.onDidNotificationChange(e => { lastEvent = e; }); diff --git a/src/vs/workbench/test/electron-browser/api/extHostDocumentSaveParticipant.test.ts b/src/vs/workbench/test/electron-browser/api/extHostDocumentSaveParticipant.test.ts index f9d84b6ed09..695bb6098f7 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostDocumentSaveParticipant.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostDocumentSaveParticipant.test.ts @@ -17,6 +17,7 @@ import { IExtensionDescription } from 'vs/workbench/services/extensions/common/e import { NullLogService } from 'vs/platform/log/common/log'; import { isResourceTextEdit, ResourceTextEdit } from 'vs/editor/common/modes'; import { timeout } from 'vs/base/common/async'; +import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; suite('ExtHostDocumentSaveParticipant', () => { @@ -25,7 +26,7 @@ suite('ExtHostDocumentSaveParticipant', () => { let documents: ExtHostDocuments; let nullLogService = new NullLogService(); let nullExtensionDescription: IExtensionDescription = { - id: 'nullExtensionDescription', + identifier: new CanonicalExtensionIdentifier('nullExtensionDescription'), name: 'Null Extension Description', publisher: 'vscode', enableProposedApi: false, diff --git a/src/vs/workbench/test/electron-browser/api/extHostWorkspace.test.ts b/src/vs/workbench/test/electron-browser/api/extHostWorkspace.test.ts index 3e01dbf4078..17af41aaced 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostWorkspace.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostWorkspace.test.ts @@ -14,11 +14,12 @@ import { IExtensionDescription } from 'vs/workbench/services/extensions/common/e import { NullLogService } from 'vs/platform/log/common/log'; import { IMainContext } from 'vs/workbench/api/node/extHost.protocol'; import { Counter } from 'vs/base/common/numbers'; +import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; suite('ExtHostWorkspace', function () { const extensionDescriptor: IExtensionDescription = { - id: 'nullExtensionDescription', + identifier: new CanonicalExtensionIdentifier('nullExtensionDescription'), name: 'ext', publisher: 'vscode', enableProposedApi: false, diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index ec52bd52f9e..5c5404d6e15 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -78,6 +78,7 @@ import { IViewlet } from 'vs/workbench/common/viewlet'; import { IProgressService } from 'vs/platform/progress/common/progress'; import { IStorageService, InMemoryStorageService } from 'vs/platform/storage/common/storage'; import { isLinux, isMacintosh } from 'vs/base/common/platform'; +import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; export function createFileInput(instantiationService: IInstantiationService, resource: URI): FileEditorInput { return instantiationService.createInstance(FileEditorInput, resource, void 0); @@ -307,7 +308,7 @@ export class TestDecorationsService implements IDecorationsService { export class TestExtensionService implements IExtensionService { _serviceBrand: any; onDidRegisterExtensions: Event = Event.None; - onDidChangeExtensionsStatus: Event = Event.None; + onDidChangeExtensionsStatus: Event = Event.None; onWillActivateByEvent: Event = Event.None; onDidChangeResponsiveChange: Event = Event.None; activateByEvent(_activationEvent: string): Promise { return Promise.resolve(void 0); } diff --git a/yarn.lock b/yarn.lock index 74e124053b0..7372409e80c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9428,10 +9428,10 @@ vscode-uri@^1.0.6: resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-1.0.6.tgz#6b8f141b0bbc44ad7b07e94f82f168ac7608ad4d" integrity sha512-sLI2L0uGov3wKVb9EB+vIQBl9tVP90nqRvxSoJ35vI3NjxE8jfsE5DSOhWgSunHSZmKS4OCi2jrtfxK7uyp2ww== -vscode-xterm@3.10.0-beta5: - version "3.10.0-beta5" - resolved "https://registry.yarnpkg.com/vscode-xterm/-/vscode-xterm-3.10.0-beta5.tgz#bc13711de6f75a574dc50295ffbfe06e7fb96579" - integrity sha512-o8DTuXKbN+vCK+aocZQYppsAgsYhmC9SGZGVfg81aibR37pldt59/ffX93fKKBKTOIAaQu+tFitQ6UYw4q3fEw== +vscode-xterm@3.10.0-beta6: + version "3.10.0-beta6" + resolved "https://registry.yarnpkg.com/vscode-xterm/-/vscode-xterm-3.10.0-beta6.tgz#ed2c6fc9bf5eb5a2cb4d6a09941445e3296bacbc" + integrity sha512-BDXmdPpprqBwWwGuHj+Wg4LtCsi9cv7NXGnXNX25/f/mHfNzRW8GqjsaCD188jfDKwxfsWWBkt3upog1rEcDqg== vso-node-api@6.1.2-preview: version "6.1.2-preview"