diff --git a/build/gulpfile.vscode.linux.js b/build/gulpfile.vscode.linux.js index fa4393c4bd4..52660b0df12 100644 --- a/build/gulpfile.vscode.linux.js +++ b/build/gulpfile.vscode.linux.js @@ -42,6 +42,7 @@ function prepareDebPackage(arch) { .pipe(replace('@@NAME_LONG@@', product.nameLong)) .pipe(replace('@@NAME_SHORT@@', product.nameShort)) .pipe(replace('@@NAME@@', product.applicationName)) + .pipe(replace('@@EXEC@@', `/usr/share/${product.applicationName}/${product.applicationName}`)) .pipe(replace('@@ICON@@', product.linuxIconName)) .pipe(replace('@@URLPROTOCOL@@', product.urlProtocol)); @@ -134,6 +135,7 @@ function prepareRpmPackage(arch) { .pipe(replace('@@NAME_LONG@@', product.nameLong)) .pipe(replace('@@NAME_SHORT@@', product.nameShort)) .pipe(replace('@@NAME@@', product.applicationName)) + .pipe(replace('@@EXEC@@', `/usr/share/${product.applicationName}/${product.applicationName}`)) .pipe(replace('@@ICON@@', product.linuxIconName)) .pipe(replace('@@URLPROTOCOL@@', product.urlProtocol)); @@ -203,21 +205,25 @@ function prepareSnapPackage(arch) { const destination = getSnapBuildPath(arch); return function () { + // A desktop file that is placed in snap/gui will be placed into meta/gui verbatim. const desktop = gulp.src('resources/linux/code.desktop', { base: '.' }) - .pipe(rename(`usr/share/applications/${product.applicationName}.desktop`)); + .pipe(rename(`snap/gui/${product.applicationName}.desktop`)); + // A desktop file that is placed in snap/gui will be placed into meta/gui verbatim. const desktopUrlHandler = gulp.src('resources/linux/code-url-handler.desktop', { base: '.' }) - .pipe(rename(`usr/share/applications/${product.applicationName}-url-handler.desktop`)); + .pipe(rename(`snap/gui/${product.applicationName}-url-handler.desktop`)); const desktops = es.merge(desktop, desktopUrlHandler) .pipe(replace('@@NAME_LONG@@', product.nameLong)) .pipe(replace('@@NAME_SHORT@@', product.nameShort)) .pipe(replace('@@NAME@@', product.applicationName)) - .pipe(replace('@@ICON@@', `/usr/share/pixmaps/${product.linuxIconName}.png`)) + .pipe(replace('@@EXEC@@', product.applicationName)) + .pipe(replace('@@ICON@@', `\${SNAP}/meta/gui/${product.linuxIconName}.png`)) .pipe(replace('@@URLPROTOCOL@@', product.urlProtocol)); + // An icon that is placed in snap/gui will be placed into meta/gui verbatim. const icon = gulp.src('resources/linux/code.png', { base: '.' }) - .pipe(rename(`usr/share/pixmaps/${product.linuxIconName}.png`)); + .pipe(rename(`snap/gui/${product.linuxIconName}.png`)); const code = gulp.src(binaryDir + '/**/*', { base: binaryDir }) .pipe(rename(function (p) { p.dirname = `usr/share/${product.applicationName}/${p.dirname}`; })); diff --git a/build/gulpfile.vscode.web.js b/build/gulpfile.vscode.web.js index d0cb6be52c7..846ceb219fd 100644 --- a/build/gulpfile.vscode.web.js +++ b/build/gulpfile.vscode.web.js @@ -35,7 +35,7 @@ const vscodeWebResources = [ // Workbench 'out-build/vs/{base,platform,editor,workbench}/**/*.{svg,png}', - 'out-build/vs/code/browser/workbench/workbench.html', + 'out-build/vs/code/browser/workbench/*.html', 'out-build/vs/base/browser/ui/octiconLabel/octicons/**', 'out-build/vs/**/markdown.css', diff --git a/build/lib/compilation.js b/build/lib/compilation.js index 5074376df4e..170e086b35b 100644 --- a/build/lib/compilation.js +++ b/build/lib/compilation.js @@ -36,8 +36,8 @@ function getTypeScriptCompilerOptions(src) { function createCompile(src, build, emitError) { const projectPath = path.join(__dirname, '../../', src, 'tsconfig.json'); const overrideOptions = Object.assign(Object.assign({}, getTypeScriptCompilerOptions(src)), { inlineSources: Boolean(build) }); - const ts = tsb.create(projectPath, overrideOptions, false, err => reporter(err)); - return function (token) { + const compilation = tsb.create(projectPath, overrideOptions, false, err => reporter(err)); + function pipeline(token) { const utf8Filter = util.filter(data => /(\/|\\)test(\/|\\).*utf8/.test(data.path)); const tsFilter = util.filter(data => /\.ts$/.test(data.path)); const noDeclarationsFilter = util.filter(data => !(/\.d\.ts$/.test(data.path))); @@ -48,7 +48,7 @@ function createCompile(src, build, emitError) { .pipe(utf8Filter.restore) .pipe(tsFilter) .pipe(util.loadSourcemaps()) - .pipe(ts(token)) + .pipe(compilation(token)) .pipe(noDeclarationsFilter) .pipe(build ? nls() : es.through()) .pipe(noDeclarationsFilter.restore) @@ -60,7 +60,11 @@ function createCompile(src, build, emitError) { .pipe(tsFilter.restore) .pipe(reporter.end(!!emitError)); return es.duplex(input, output); + } + pipeline.tsProjectSrc = () => { + return compilation.src({ base: src }); }; + return pipeline; } function compileTask(src, out, build) { return function () { diff --git a/build/lib/compilation.ts b/build/lib/compilation.ts index b52c49c5961..6f37821c775 100644 --- a/build/lib/compilation.ts +++ b/build/lib/compilation.ts @@ -39,13 +39,13 @@ function getTypeScriptCompilerOptions(src: string): ts.CompilerOptions { return options; } -function createCompile(src: string, build: boolean, emitError?: boolean): (token?: util.ICancellationToken) => NodeJS.ReadWriteStream { +function createCompile(src: string, build: boolean, emitError?: boolean) { const projectPath = path.join(__dirname, '../../', src, 'tsconfig.json'); const overrideOptions = { ...getTypeScriptCompilerOptions(src), inlineSources: Boolean(build) }; - const ts = tsb.create(projectPath, overrideOptions, false, err => reporter(err)); + const compilation = tsb.create(projectPath, overrideOptions, false, err => reporter(err)); - return function (token?: util.ICancellationToken) { + function pipeline(token?: util.ICancellationToken) { const utf8Filter = util.filter(data => /(\/|\\)test(\/|\\).*utf8/.test(data.path)); const tsFilter = util.filter(data => /\.ts$/.test(data.path)); @@ -58,7 +58,7 @@ function createCompile(src: string, build: boolean, emitError?: boolean): (token .pipe(utf8Filter.restore) .pipe(tsFilter) .pipe(util.loadSourcemaps()) - .pipe(ts(token)) + .pipe(compilation(token)) .pipe(noDeclarationsFilter) .pipe(build ? nls() : es.through()) .pipe(noDeclarationsFilter.restore) @@ -71,16 +71,18 @@ function createCompile(src: string, build: boolean, emitError?: boolean): (token .pipe(reporter.end(!!emitError)); return es.duplex(input, output); + } + pipeline.tsProjectSrc = () => { + return compilation.src({ base: src }); }; + return pipeline; } export function compileTask(src: string, out: string, build: boolean): () => NodeJS.ReadWriteStream { return function () { const compile = createCompile(src, build, true); - const srcPipe = gulp.src(`${src}/**`, { base: `${src}` }); - let generator = new MonacoGenerator(false); if (src === 'src') { generator.execute(); diff --git a/build/lib/typings/gulp-tsb.d.ts b/build/lib/typings/gulp-tsb.d.ts index 916dd21cd1c..df9bb34d3fb 100644 --- a/build/lib/typings/gulp-tsb.d.ts +++ b/build/lib/typings/gulp-tsb.d.ts @@ -8,7 +8,10 @@ declare module "gulp-tsb" { export interface IncrementalCompiler { (token?: ICancellationToken): NodeJS.ReadWriteStream; - src(): NodeJS.ReadStream; + src(opts?: { + cwd?: string; + base?: string; + }): NodeJS.ReadStream; } export function create(projectPath: string, existingOptions: any, verbose?: boolean, onError?: (message: any) => void): IncrementalCompiler; diff --git a/build/lib/util.js b/build/lib/util.js index be2670261d7..6acb7ab574f 100644 --- a/build/lib/util.js +++ b/build/lib/util.js @@ -121,7 +121,7 @@ function loadSourcemaps() { return; } if (!f.contents) { - cb(new Error('empty file')); + cb(undefined, f); return; } const contents = f.contents.toString('utf8'); diff --git a/build/lib/util.ts b/build/lib/util.ts index 578271e6648..6a82295ae26 100644 --- a/build/lib/util.ts +++ b/build/lib/util.ts @@ -165,7 +165,7 @@ export function loadSourcemaps(): NodeJS.ReadWriteStream { } if (!f.contents) { - cb(new Error('empty file')); + cb(undefined, f); return; } diff --git a/build/lib/watch/index.js b/build/lib/watch/index.js index 0a4af2a798f..a9ab5972977 100644 --- a/build/lib/watch/index.js +++ b/build/lib/watch/index.js @@ -5,17 +5,6 @@ const es = require('event-stream'); -/** Ugly hack for gulp-tsb */ -function handleDeletions() { - return es.mapSync(f => { - if (/\.ts$/.test(f.relative) && !f.contents) { - f.contents = Buffer.from(''); - f.stat = { mtime: new Date() }; - } - - return f; - }); -} let watch = undefined; @@ -24,6 +13,5 @@ if (!watch) { } module.exports = function () { - return watch.apply(null, arguments) - .pipe(handleDeletions()); + return watch.apply(null, arguments); }; diff --git a/extensions/html-language-features/server/package.json b/extensions/html-language-features/server/package.json index ca358e22b9b..ec693b876f7 100644 --- a/extensions/html-language-features/server/package.json +++ b/extensions/html-language-features/server/package.json @@ -10,7 +10,7 @@ "main": "./out/htmlServerMain", "dependencies": { "vscode-css-languageservice": "^4.0.3-next.6", - "vscode-html-languageservice": "^3.0.4-next.2", + "vscode-html-languageservice": "^3.0.4-next.3", "vscode-languageserver": "^5.3.0-next.8", "vscode-languageserver-types": "3.15.0-next.2", "vscode-nls": "^4.1.1", diff --git a/extensions/html-language-features/server/yarn.lock b/extensions/html-language-features/server/yarn.lock index 4ac895b439c..c2b7f141cc6 100644 --- a/extensions/html-language-features/server/yarn.lock +++ b/extensions/html-language-features/server/yarn.lock @@ -238,10 +238,10 @@ vscode-css-languageservice@^4.0.3-next.6: vscode-nls "^4.1.1" vscode-uri "^2.0.3" -vscode-html-languageservice@^3.0.4-next.2: - version "3.0.4-next.2" - resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-3.0.4-next.2.tgz#afdbe2781daa0a72613afac77068593925bd2e07" - integrity sha512-tlyiflBm/k/PoTwGzg4LL0cwq6wS7mnKxDVYSlY2Iw21dONWINaS0MynYCn6Zs4PzIpgueCSMuTBQTey4d+BBQ== +vscode-html-languageservice@^3.0.4-next.3: + version "3.0.4-next.3" + resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-3.0.4-next.3.tgz#7a0fc33aae846165b157acbb8b133cc3fcf2ca0d" + integrity sha512-PGIcKFxqsvVMv51QWreuQx9LhN43Vzhgl8RYI8CcWThjl+J8uUKImjwAWq9zndOiiRUPF2Zk7zME/dMIis1hOw== dependencies: vscode-languageserver-types "^3.15.0-next.2" vscode-nls "^4.1.1" diff --git a/package.json b/package.json index 4a679ae9b91..08445ea919e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.39.0", - "distro": "8516b3c6e37976c7f4a183ec1196a939f0ff11bf", + "distro": "9e306bef060605dfd64f4cd4c76d324a525cae89", "author": { "name": "Microsoft Corporation" }, @@ -97,7 +97,7 @@ "gulp-rename": "^1.2.0", "gulp-replace": "^0.5.4", "gulp-shell": "^0.6.5", - "gulp-tsb": "4.0.1", + "gulp-tsb": "4.0.2", "gulp-tslint": "^8.1.3", "gulp-untar": "^0.0.7", "gulp-vinyl-zip": "^2.1.2", diff --git a/resources/linux/bin/code.sh b/resources/linux/bin/code.sh index 64109f1fa12..564f13ef95c 100755 --- a/resources/linux/bin/code.sh +++ b/resources/linux/bin/code.sh @@ -4,7 +4,7 @@ # Licensed under the MIT License. See License.txt in the project root for license information. # test that VSCode wasn't installed inside WSL -if grep -qi Microsoft /proc/version; then +if grep -qi Microsoft /proc/version && [ -z "$DONT_PROMPT_WSL_INSTALL" ]; then echo "To use VS Code with the Windows Subsystem for Linux, please install VS Code in Windows and uninstall the Linux version in WSL. You can then use the '@@PRODNAME@@' command in a WSL terminal just as you would in a normal command prompt." 1>&2 read -e -p "Do you want to continue anyways ? [y/N] " YN diff --git a/resources/linux/code-url-handler.desktop b/resources/linux/code-url-handler.desktop index 09e39c57c51..7106e0e0969 100644 --- a/resources/linux/code-url-handler.desktop +++ b/resources/linux/code-url-handler.desktop @@ -2,7 +2,7 @@ Name=@@NAME_LONG@@ - URL Handler Comment=Code Editing. Redefined. GenericName=Text Editor -Exec=/usr/share/@@NAME@@/@@NAME@@ --open-url %U +Exec=@@EXEC@@ --open-url %U Icon=@@ICON@@ Type=Application NoDisplay=true diff --git a/resources/linux/code.desktop b/resources/linux/code.desktop index dbc7818cecf..1273bb2db7c 100644 --- a/resources/linux/code.desktop +++ b/resources/linux/code.desktop @@ -2,7 +2,7 @@ Name=@@NAME_LONG@@ Comment=Code Editing. Redefined. GenericName=Text Editor -Exec=/usr/share/@@NAME@@/@@NAME@@ --unity-launch %F +Exec=@@EXEC@@ --unity-launch %F Icon=@@ICON@@ Type=Application StartupNotify=false @@ -14,5 +14,5 @@ Keywords=vscode; [Desktop Action new-empty-window] Name=New Empty Window -Exec=/usr/share/@@NAME@@/@@NAME@@ --new-window %F +Exec=@@EXEC@@ --new-window %F Icon=@@ICON@@ diff --git a/resources/linux/snap/snapcraft.yaml b/resources/linux/snap/snapcraft.yaml index 28b3e59ad7e..0f872089195 100644 --- a/resources/linux/snap/snapcraft.yaml +++ b/resources/linux/snap/snapcraft.yaml @@ -49,16 +49,14 @@ parts: apps: @@NAME@@: - command: electron-launch $SNAP/usr/share/@@NAME@@/bin/@@NAME@@ - desktop: usr/share/applications/@@NAME@@.desktop + command: electron-launch $SNAP/usr/share/@@NAME@@/@@NAME@@ common-id: @@NAME@@.desktop environment: DISABLE_WAYLAND: 1 GSETTINGS_SCHEMA_DIR: $SNAP/usr/share/glib-2.0/schemas url-handler: - command: electron-launch $SNAP/usr/share/@@NAME@@/bin/@@NAME@@ --open-url - desktop: usr/share/applications/@@NAME@@-url-handler.desktop + command: electron-launch $SNAP/usr/share/@@NAME@@/@@NAME@@ --open-url environment: DISABLE_WAYLAND: 1 - GSETTINGS_SCHEMA_DIR: $SNAP/usr/share/glib-2.0/schemas \ No newline at end of file + GSETTINGS_SCHEMA_DIR: $SNAP/usr/share/glib-2.0/schemas diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index c20a8771c87..737a5da79c8 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -66,20 +66,32 @@ interface IDataTreeListTemplateData { templateData: T; } -class AsyncDataTreeNodeWrapper implements ITreeNode { +class NodeMapper { - get element(): T { return this.node.element!.element as T; } - get parent(): ITreeNode | undefined { return this.node.parent && new AsyncDataTreeNodeWrapper(this.node.parent); } - get children(): ITreeNode[] { return this.node.children.map(node => new AsyncDataTreeNodeWrapper(node)); } - get depth(): number { return this.node.depth; } - get visibleChildrenCount(): number { return this.node.visibleChildrenCount; } - get visibleChildIndex(): number { return this.node.visibleChildIndex; } - get collapsible(): boolean { return this.node.collapsible; } - get collapsed(): boolean { return this.node.collapsed; } - get visible(): boolean { return this.node.visible; } - get filterData(): TFilterData | undefined { return this.node.filterData; } + private map = new WeakMap | null, TFilterData>, ITreeNode>(); - constructor(private node: ITreeNode | null, TFilterData>) { } + mapNode(node: ITreeNode | null, TFilterData>): ITreeNode { + const that = this; + let result = this.map.get(node); + + if (!result) { + result = new Proxy(node, { + get(obj, prop) { + if (prop === 'element') { + return node.element!.element; + } else if (prop === 'children') { + return node.children.map(child => that.mapNode(child)); + } + + return (obj as any)[prop]; + } + }) as unknown as ITreeNode; + + this.map.set(node, result); + } + + return result; + } } class DataTreeRenderer implements ITreeRenderer, TFilterData, IDataTreeListTemplateData> { @@ -90,6 +102,7 @@ class DataTreeRenderer implements ITreeRe constructor( private renderer: ITreeRenderer, + private nodeMapper: NodeMapper, readonly onDidChangeTwistieState: Event> ) { this.templateId = renderer.templateId; @@ -101,7 +114,7 @@ class DataTreeRenderer implements ITreeRe } renderElement(node: ITreeNode, TFilterData>, index: number, templateData: IDataTreeListTemplateData, height: number | undefined): void { - this.renderer.renderElement(new AsyncDataTreeNodeWrapper(node), index, templateData.templateData, height); + this.renderer.renderElement(this.nodeMapper.mapNode(node) as ITreeNode, index, templateData.templateData, height); } renderTwistie(element: IAsyncDataTreeNode, twistieElement: HTMLElement): boolean { @@ -111,7 +124,7 @@ class DataTreeRenderer implements ITreeRe disposeElement(node: ITreeNode, TFilterData>, index: number, templateData: IDataTreeListTemplateData, height: number | undefined): void { if (this.renderer.disposeElement) { - this.renderer.disposeElement(new AsyncDataTreeNodeWrapper(node), index, templateData.templateData, height); + this.renderer.disposeElement(this.nodeMapper.mapNode(node) as ITreeNode, index, templateData.templateData, height); } } @@ -306,6 +319,8 @@ export class AsyncDataTree implements IDisposable private readonly _onDidRender = new Emitter(); private readonly _onDidChangeNodeSlowState = new Emitter>(); + private readonly nodeMapper = new NodeMapper(); + protected readonly disposables: IDisposable[] = []; get onDidScroll(): Event { return this.tree.onDidScroll; } @@ -352,7 +367,7 @@ export class AsyncDataTree implements IDisposable this.collapseByDefault = options.collapseByDefault; const objectTreeDelegate = new ComposedTreeDelegate>(delegate); - const objectTreeRenderers = renderers.map(r => new DataTreeRenderer(r, this._onDidChangeNodeSlowState.event)); + const objectTreeRenderers = renderers.map(r => new DataTreeRenderer(r, this.nodeMapper, this._onDidChangeNodeSlowState.event)); const objectTreeOptions = asObjectTreeOptions(options) || {}; this.tree = new ObjectTree(user, container, objectTreeDelegate, objectTreeRenderers, objectTreeOptions); @@ -510,7 +525,7 @@ export class AsyncDataTree implements IDisposable getNode(element: TInput | T = this.root.element): ITreeNode { const dataNode = this.getDataNode(element); const node = this.tree.getNode(dataNode === this.root ? null : dataNode); - return new AsyncDataTreeNodeWrapper(node); + return this.nodeMapper.mapNode(node); } collapse(element: T, recursive: boolean = false): boolean { diff --git a/src/vs/code/browser/workbench/callback.html b/src/vs/code/browser/workbench/callback.html new file mode 100644 index 00000000000..da6c907b666 --- /dev/null +++ b/src/vs/code/browser/workbench/callback.html @@ -0,0 +1,79 @@ + + + + + + + + + + + + + Visual Studio Code + + + + + + + Visual Studio Code + +
+
+ You can close this page now. +
+
+ + diff --git a/src/vs/editor/contrib/snippet/snippetSession.ts b/src/vs/editor/contrib/snippet/snippetSession.ts index 63aa8a230eb..8bc84ccfd52 100644 --- a/src/vs/editor/contrib/snippet/snippetSession.ts +++ b/src/vs/editor/contrib/snippet/snippetSession.ts @@ -23,6 +23,7 @@ import { registerThemingParticipant } from 'vs/platform/theme/common/themeServic import * as colors from 'vs/platform/theme/common/colorRegistry'; import { withNullAsUndefined } from 'vs/base/common/types'; import { ILabelService } from 'vs/platform/label/common/label'; +import { EditorOption } from 'vs/editor/common/config/editorOptions'; registerThemingParticipant((theme, collector) => { @@ -444,7 +445,7 @@ export class SnippetSession { snippet.resolveVariables(new CompositeSnippetVariableResolver([ modelBasedVariableResolver, - new ClipboardBasedVariableResolver(clipboardText, idx, indexedSelections.length), + new ClipboardBasedVariableResolver(clipboardText, idx, indexedSelections.length, editor.getOption(EditorOption.multiCursorPaste) === 'spread'), new SelectionBasedVariableResolver(model, selection), new CommentBasedVariableResolver(model), new TimeBasedVariableResolver, diff --git a/src/vs/editor/contrib/snippet/snippetVariables.ts b/src/vs/editor/contrib/snippet/snippetVariables.ts index a1c080d2b45..4f289c2af95 100644 --- a/src/vs/editor/contrib/snippet/snippetVariables.ts +++ b/src/vs/editor/contrib/snippet/snippetVariables.ts @@ -169,7 +169,8 @@ export class ClipboardBasedVariableResolver implements VariableResolver { constructor( private readonly _clipboardText: string | undefined, private readonly _selectionIdx: number, - private readonly _selectionCount: number + private readonly _selectionCount: number, + private readonly _spread: boolean ) { // } @@ -183,12 +184,16 @@ export class ClipboardBasedVariableResolver implements VariableResolver { return undefined; } - const lines = this._clipboardText.split(/\r\n|\n|\r/).filter(s => !isFalsyOrWhitespace(s)); - if (lines.length === this._selectionCount) { - return lines[this._selectionIdx]; - } else { - return this._clipboardText; + // `spread` is assigning each cursor a line of the clipboard + // text whenever there the line count equals the cursor count + // and when enabled + if (this._spread) { + const lines = this._clipboardText.split(/\r\n|\n|\r/).filter(s => !isFalsyOrWhitespace(s)); + if (lines.length === this._selectionCount) { + return lines[this._selectionIdx]; + } } + return this._clipboardText; } } export class CommentBasedVariableResolver implements VariableResolver { diff --git a/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts b/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts index b7d5c40b8bf..0189cc0de2e 100644 --- a/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts +++ b/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts @@ -236,26 +236,28 @@ suite('Snippet Variables Resolver', function () { test('Add variable to insert value from clipboard to a snippet #40153', function () { - assertVariableResolve(new ClipboardBasedVariableResolver(undefined, 1, 0), 'CLIPBOARD', undefined); + assertVariableResolve(new ClipboardBasedVariableResolver(undefined, 1, 0, true), 'CLIPBOARD', undefined); - assertVariableResolve(new ClipboardBasedVariableResolver(null!, 1, 0), 'CLIPBOARD', undefined); + assertVariableResolve(new ClipboardBasedVariableResolver(null!, 1, 0, true), 'CLIPBOARD', undefined); - assertVariableResolve(new ClipboardBasedVariableResolver('', 1, 0), 'CLIPBOARD', undefined); + assertVariableResolve(new ClipboardBasedVariableResolver('', 1, 0, true), 'CLIPBOARD', undefined); - assertVariableResolve(new ClipboardBasedVariableResolver('foo', 1, 0), 'CLIPBOARD', 'foo'); + assertVariableResolve(new ClipboardBasedVariableResolver('foo', 1, 0, true), 'CLIPBOARD', 'foo'); - assertVariableResolve(new ClipboardBasedVariableResolver('foo', 1, 0), 'foo', undefined); - assertVariableResolve(new ClipboardBasedVariableResolver('foo', 1, 0), 'cLIPBOARD', undefined); + assertVariableResolve(new ClipboardBasedVariableResolver('foo', 1, 0, true), 'foo', undefined); + assertVariableResolve(new ClipboardBasedVariableResolver('foo', 1, 0, true), 'cLIPBOARD', undefined); }); test('Add variable to insert value from clipboard to a snippet #40153', function () { - assertVariableResolve(new ClipboardBasedVariableResolver('line1', 1, 2), 'CLIPBOARD', 'line1'); - assertVariableResolve(new ClipboardBasedVariableResolver('line1\nline2\nline3', 1, 2), 'CLIPBOARD', 'line1\nline2\nline3'); + assertVariableResolve(new ClipboardBasedVariableResolver('line1', 1, 2, true), 'CLIPBOARD', 'line1'); + assertVariableResolve(new ClipboardBasedVariableResolver('line1\nline2\nline3', 1, 2, true), 'CLIPBOARD', 'line1\nline2\nline3'); - assertVariableResolve(new ClipboardBasedVariableResolver('line1\nline2', 1, 2), 'CLIPBOARD', 'line2'); - resolver = new ClipboardBasedVariableResolver('line1\nline2', 0, 2); - assertVariableResolve(new ClipboardBasedVariableResolver('line1\nline2', 0, 2), 'CLIPBOARD', 'line1'); + assertVariableResolve(new ClipboardBasedVariableResolver('line1\nline2', 1, 2, true), 'CLIPBOARD', 'line2'); + resolver = new ClipboardBasedVariableResolver('line1\nline2', 0, 2, true); + assertVariableResolve(new ClipboardBasedVariableResolver('line1\nline2', 0, 2, true), 'CLIPBOARD', 'line1'); + + assertVariableResolve(new ClipboardBasedVariableResolver('line1\nline2', 0, 2, false), 'CLIPBOARD', 'line1\nline2'); }); diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts index f60b09ad56f..7f77b7a084d 100644 --- a/src/vs/platform/environment/common/environment.ts +++ b/src/vs/platform/environment/common/environment.ts @@ -71,17 +71,13 @@ export interface ParsedArgs { 'driver-verbose'?: boolean; remote?: string; 'disable-user-env-probe'?: boolean; - 'enable-remote-auto-shutdown'?: boolean; 'disable-inspect'?: boolean; 'force'?: boolean; - 'gitCredential'?: string; + // node flags 'js-flags'?: string; 'disable-gpu'?: boolean; 'nolazy'?: boolean; - - // Web flags - 'web-user-data-dir'?: string; } export const IEnvironmentService = createDecorator('environmentService'); diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index e639efb6f59..3c72aa15242 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -40,13 +40,14 @@ type OptionTypeName = T extends undefined ? 'undefined' : 'unknown'; -export const OPTIONS: OptionDescriptions = { +export const OPTIONS: OptionDescriptions> = { 'diff': { type: 'boolean', cat: 'o', alias: 'd', args: ['file', 'file'], description: localize('diff', "Compare two files with each other.") }, 'add': { type: 'boolean', cat: 'o', alias: 'a', args: 'folder', description: localize('add', "Add folder(s) to the last active window.") }, 'goto': { type: 'boolean', cat: 'o', alias: 'g', args: 'file:line[:character]', description: localize('goto', "Open a file at the path on the specified line and character position.") }, 'new-window': { type: 'boolean', cat: 'o', alias: 'n', description: localize('newWindow', "Force to open a new window.") }, 'reuse-window': { type: 'boolean', cat: 'o', alias: 'r', description: localize('reuseWindow', "Force to open a file or folder in an already opened window.") }, 'wait': { type: 'boolean', cat: 'o', alias: 'w', description: localize('wait', "Wait for the files to be closed before returning.") }, + 'waitMarkerFilePath': { type: 'string' }, 'locale': { type: 'string', cat: 'o', args: 'locale', description: localize('locale', "The locale to use (e.g. en-US or zh-TW).") }, 'user-data-dir': { type: 'string', cat: 'o', args: 'dir', description: localize('userDataDir', "Specifies the directory that user data is kept in. Can be used to open multiple distinct instances of Code.") }, 'version': { type: 'boolean', cat: 'o', alias: 'v', description: localize('version', "Print version.") }, @@ -56,6 +57,7 @@ export const OPTIONS: OptionDescriptions = { 'file-uri': { type: 'string[]', cat: 'o', args: 'uri', description: localize('fileUri', "Opens a window with given file uri(s)") }, 'extensions-dir': { type: 'string', deprecates: 'extensionHomePath', cat: 'e', args: 'dir', description: localize('extensionHomePath', "Set the root path for extensions.") }, + 'builtin-extensions-dir': { type: 'string' }, 'list-extensions': { type: 'boolean', cat: 'e', description: localize('listExtensions', "List the installed extensions.") }, 'show-versions': { type: 'boolean', cat: 'e', description: localize('showVersions', "Show versions of installed extensions, when using --list-extension.") }, 'category': { type: 'string', cat: 'e', description: localize('category', "Filters installed extensions by provided category, when using --list-extension.") }, @@ -67,6 +69,8 @@ export const OPTIONS: OptionDescriptions = { 'log': { type: 'string', cat: 't', args: 'level', description: localize('log', "Log level to use. Default is 'info'. Allowed values are 'critical', 'error', 'warn', 'info', 'debug', 'trace', 'off'.") }, 'status': { type: 'boolean', alias: 's', cat: 't', description: localize('status', "Print process usage and diagnostics information.") }, 'prof-startup': { type: 'boolean', cat: 't', description: localize('prof-startup', "Run CPU profiler during startup") }, + 'prof-append-timers': { type: 'string' }, + 'prof-startup-prefix': { type: 'string' }, 'disable-extensions': { type: 'boolean', deprecates: 'disableExtensions', cat: 't', description: localize('disableExtensions', "Disable all installed extensions.") }, 'disable-extension': { type: 'string[]', cat: 't', args: 'extension-id', description: localize('disableExtension', "Disable an extension.") }, @@ -94,6 +98,7 @@ export const OPTIONS: OptionDescriptions = { 'disable-telemetry': { type: 'boolean' }, 'disable-updates': { type: 'boolean' }, 'disable-crash-reporter': { type: 'boolean' }, + 'disable-user-env-probe': { type: 'boolean' }, 'skip-add-to-recently-opened': { type: 'boolean' }, 'unity-launch': { type: 'boolean' }, 'open-url': { type: 'boolean' }, @@ -101,12 +106,14 @@ export const OPTIONS: OptionDescriptions = { 'file-chmod': { type: 'boolean' }, 'driver-verbose': { type: 'boolean' }, 'force': { type: 'boolean' }, + 'trace': { type: 'boolean' }, 'trace-category-filter': { type: 'string' }, 'trace-options': { type: 'string' }, 'disable-inspect': { type: 'boolean' }, 'js-flags': { type: 'string' }, // chrome js flags 'nolazy': { type: 'boolean' }, // node inspect + '_urls': { type: 'string[]' }, _: { type: 'string[]' } // main arguments }; @@ -126,6 +133,10 @@ export function parseArgs(args: string[], options: OptionDescriptions, err const string: string[] = []; const boolean: string[] = []; for (let optionId in options) { + if (optionId[0] === '_') { + continue; + } + const o = options[optionId]; if (o.alias) { alias[optionId] = o.alias; diff --git a/src/vs/platform/environment/node/environmentService.ts b/src/vs/platform/environment/node/environmentService.ts index e0cc96ad33b..1c3df4cac7e 100644 --- a/src/vs/platform/environment/node/environmentService.ts +++ b/src/vs/platform/environment/node/environmentService.ts @@ -104,8 +104,6 @@ export class EnvironmentService implements IEnvironmentService { return parseUserDataDir(this._args, process); } - @memoize - get webUserDataHome(): URI { return URI.file(parsePathArg(this._args['web-user-data-dir'], process) || this.userDataPath); } get appNameLong(): string { return product.nameLong; } @@ -285,7 +283,7 @@ function parseDebugPort(debugArg: string | undefined, debugBrkArg: string | unde return { port, break: brk, debugId }; } -function parsePathArg(arg: string | undefined, process: NodeJS.Process): string | undefined { +export function parsePathArg(arg: string | undefined, process: NodeJS.Process): string | undefined { if (!arg) { return undefined; } diff --git a/src/vs/workbench/browser/parts/editor/media/close-dirty-inverse-alt.svg b/src/vs/workbench/browser/parts/editor/media/close-dirty-inverse-alt.svg deleted file mode 100644 index 02dafab76fc..00000000000 --- a/src/vs/workbench/browser/parts/editor/media/close-dirty-inverse-alt.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/editor/media/close-dirty-inverse.svg b/src/vs/workbench/browser/parts/editor/media/close-dirty-inverse.svg deleted file mode 100644 index 02dafab76fc..00000000000 --- a/src/vs/workbench/browser/parts/editor/media/close-dirty-inverse.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/workbench/contrib/debug/browser/media/breakpoint-conditional-disabled.svg b/src/vs/workbench/contrib/debug/browser/media/breakpoint-conditional-disabled.svg deleted file mode 100644 index 75c0a500f4d..00000000000 --- a/src/vs/workbench/contrib/debug/browser/media/breakpoint-conditional-disabled.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/debug/browser/media/breakpoint-conditional-unverified.svg b/src/vs/workbench/contrib/debug/browser/media/breakpoint-conditional-unverified.svg deleted file mode 100644 index 85a35a44ff6..00000000000 --- a/src/vs/workbench/contrib/debug/browser/media/breakpoint-conditional-unverified.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/extensions/browser/media/clear-inverse.svg b/src/vs/workbench/contrib/extensions/browser/media/clear-inverse.svg deleted file mode 100644 index 85e7ec4bdaf..00000000000 --- a/src/vs/workbench/contrib/extensions/browser/media/clear-inverse.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/workbench/contrib/feedback/browser/media/smiley.svg b/src/vs/workbench/contrib/feedback/browser/media/smiley.svg deleted file mode 100644 index 6bc95278904..00000000000 --- a/src/vs/workbench/contrib/feedback/browser/media/smiley.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/workbench/contrib/files/browser/fileActions.ts b/src/vs/workbench/contrib/files/browser/fileActions.ts index e204c90c0bc..cb3afb6e658 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.ts @@ -925,7 +925,7 @@ async function openExplorerAndCreate(accessor: ServicesAccessor, isFolder: boole folder.addChild(newStat); - const onSuccess = async (value: string) => { + const onSuccess = (value: string): Promise => { const createPromise = isFolder ? fileService.createFolder(resources.joinPath(folder.resource, value)) : textFileService.create(resources.joinPath(folder.resource, value)); return createPromise.then(created => { refreshIfSeparator(value, explorerService); @@ -943,8 +943,6 @@ async function openExplorerAndCreate(accessor: ServicesAccessor, isFolder: boole explorerService.setEditable(newStat, null); if (success) { onSuccess(value); - } else { - explorerService.select(folder.resource).then(undefined, onUnexpectedError); } } }); diff --git a/src/vs/workbench/contrib/files/browser/media/action-close.svg b/src/vs/workbench/contrib/files/browser/media/action-close.svg deleted file mode 100644 index fde34404d4e..00000000000 --- a/src/vs/workbench/contrib/files/browser/media/action-close.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/workbench/contrib/files/browser/media/close-all-hc.svg b/src/vs/workbench/contrib/files/browser/media/close-all-hc.svg deleted file mode 100644 index 3cbd40ee697..00000000000 --- a/src/vs/workbench/contrib/files/browser/media/close-all-hc.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/vs/workbench/contrib/files/browser/media/saveall.svg b/src/vs/workbench/contrib/files/browser/media/saveall.svg deleted file mode 100644 index 5f036a20f71..00000000000 --- a/src/vs/workbench/contrib/files/browser/media/saveall.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/workbench/contrib/files/browser/views/explorerView.ts b/src/vs/workbench/contrib/files/browser/views/explorerView.ts index 52583917ba0..fda43517833 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerView.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerView.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import { URI } from 'vs/base/common/uri'; import * as perf from 'vs/base/common/performance'; -import { Action, IAction, WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification } from 'vs/base/common/actions'; +import { IAction, WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification } from 'vs/base/common/actions'; import { memoize } from 'vs/base/common/decorators'; import { IFilesConfiguration, ExplorerFolderContext, FilesExplorerFocusedContext, ExplorerFocusedContext, ExplorerRootContext, ExplorerResourceReadonlyContext, IExplorerService, ExplorerResourceCut, ExplorerResourceMoveableToTrash } from 'vs/workbench/contrib/files/common/files'; import { NewFolderAction, NewFileAction, FileCopiedContext, RefreshExplorerView, CollapseExplorerView } from 'vs/workbench/contrib/files/browser/fileActions'; @@ -68,6 +68,7 @@ export class ExplorerView extends ViewletPanel { private shouldRefresh = true; private dragHandler!: DelayedDragHandler; private autoReveal = false; + private actions: IAction[] | undefined; constructor( options: IViewletPanelOptions, @@ -170,7 +171,12 @@ export class ExplorerView extends ViewletPanel { })); this._register(this.explorerService.onDidChangeRoots(() => this.setTreeInput())); - this._register(this.explorerService.onDidChangeItem(e => this.refresh(e.recursive, e.item))); + this._register(this.explorerService.onDidChangeItem(e => { + if (this.explorerService.isEditable(undefined)) { + this.tree.domFocus(); + } + this.refresh(e.recursive, e.item); + })); this._register(this.explorerService.onDidChangeEditable(async e => { const isEditing = !!this.explorerService.getEditableData(e); @@ -218,14 +224,16 @@ export class ExplorerView extends ViewletPanel { } getActions(): IAction[] { - const actions: Action[] = []; - - actions.push(this.instantiationService.createInstance(NewFileAction)); - actions.push(this.instantiationService.createInstance(NewFolderAction)); - actions.push(this.instantiationService.createInstance(RefreshExplorerView, RefreshExplorerView.ID, RefreshExplorerView.LABEL)); - actions.push(this.instantiationService.createInstance(CollapseExplorerView, CollapseExplorerView.ID, CollapseExplorerView.LABEL)); - - return actions; + if (!this.actions) { + this.actions = [ + this.instantiationService.createInstance(NewFileAction), + this.instantiationService.createInstance(NewFolderAction), + this.instantiationService.createInstance(RefreshExplorerView, RefreshExplorerView.ID, RefreshExplorerView.LABEL), + this.instantiationService.createInstance(CollapseExplorerView, CollapseExplorerView.ID, CollapseExplorerView.LABEL) + ]; + this.actions.forEach(a => this._register(a)); + } + return this.actions; } focus(): void { @@ -334,6 +342,13 @@ export class ExplorerView extends ViewletPanel { this._register(this.tree.onContextMenu(e => this.onContextMenu(e))); + this._register(this.tree.onDidScroll(e => { + let editable = this.explorerService.getEditable(); + if (e.scrollTopChanged && editable && this.tree.getRelativeTop(editable.stat) === null) { + editable.data.onFinish('', false); + } + })); + // save view state on shutdown this._register(this.storageService.onWillSaveState(() => { this.storageService.store(ExplorerView.TREE_VIEW_STATE_STORAGE_KEY, JSON.stringify(this.tree.getViewState()), StorageScope.WORKSPACE); diff --git a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts index 0c368120dff..6061e787c25 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts @@ -228,43 +228,37 @@ export class FilesRenderer implements ITreeRenderer 0 && !stat.isDirectory ? lastDot : value.length }); - const done = once(async (success: boolean, blur: boolean) => { + const done = once((success: boolean, finishEditing: boolean) => { label.element.style.display = 'none'; const value = inputBox.value; dispose(toDispose); - container.removeChild(label.element); - editableData.onFinish(value, success); + label.element.remove(); + if (finishEditing) { + editableData.onFinish(value, success); + } }); - // It can happen that the tree re-renders this node. When that happens, - // we're gonna get a blur event first and only after an element disposable. - // Because of that, we should setTimeout the blur handler to differentiate - // between the blur happening because of a unrender or because of a user action. - let ignoreBlur = false; - const toDispose = [ inputBox, DOM.addStandardDisposableListener(inputBox.inputElement, DOM.EventType.KEY_DOWN, (e: IKeyboardEvent) => { if (e.equals(KeyCode.Enter)) { if (inputBox.validate()) { - done(true, false); + done(true, true); } } else if (e.equals(KeyCode.Escape)) { - done(false, false); + done(false, true); } }), DOM.addDisposableListener(inputBox.inputElement, DOM.EventType.BLUR, () => { - setTimeout(() => { - if (!ignoreBlur) { - done(inputBox.isInputValid(), true); - } - }, 0); + done(inputBox.isInputValid(), true); }), label, styler ]; - return toDisposable(() => ignoreBlur = true); + return toDisposable(() => { + done(false, false); + }); } disposeElement?(element: ITreeNode, index: number, templateData: IFileTemplateData): void { diff --git a/src/vs/workbench/contrib/files/common/explorerService.ts b/src/vs/workbench/contrib/files/common/explorerService.ts index 4f6f55899a1..1674c9448e1 100644 --- a/src/vs/workbench/contrib/files/common/explorerService.ts +++ b/src/vs/workbench/contrib/files/common/explorerService.ts @@ -140,6 +140,10 @@ export class ExplorerService implements IExplorerService { return !!this.cutItems && this.cutItems.indexOf(item) >= 0; } + getEditable(): { stat: ExplorerItem, data: IEditableData } | undefined { + return this.editable; + } + getEditableData(stat: ExplorerItem): IEditableData | undefined { return this.editable && this.editable.stat === stat ? this.editable.data : undefined; } diff --git a/src/vs/workbench/contrib/files/common/files.ts b/src/vs/workbench/contrib/files/common/files.ts index 4d4af35e7e9..8f5470bff77 100644 --- a/src/vs/workbench/contrib/files/common/files.ts +++ b/src/vs/workbench/contrib/files/common/files.ts @@ -51,6 +51,7 @@ export interface IExplorerService { readonly onDidCopyItems: Event<{ items: ExplorerItem[], cut: boolean, previouslyCutItems: ExplorerItem[] | undefined }>; setEditable(stat: ExplorerItem, data: IEditableData | null): void; + getEditable(): { stat: ExplorerItem, data: IEditableData } | undefined; getEditableData(stat: ExplorerItem): IEditableData | undefined; // If undefined is passed checks if any element is currently being edited. isEditable(stat: ExplorerItem | undefined): boolean; diff --git a/src/vs/workbench/contrib/preferences/browser/media/add.svg b/src/vs/workbench/contrib/preferences/browser/media/add.svg deleted file mode 100644 index bdecdb0e45b..00000000000 --- a/src/vs/workbench/contrib/preferences/browser/media/add.svg +++ /dev/null @@ -1 +0,0 @@ -Layer 1 \ No newline at end of file diff --git a/src/vs/workbench/contrib/preferences/browser/media/add_inverse.svg b/src/vs/workbench/contrib/preferences/browser/media/add_inverse.svg deleted file mode 100644 index 3475c1e1963..00000000000 --- a/src/vs/workbench/contrib/preferences/browser/media/add_inverse.svg +++ /dev/null @@ -1 +0,0 @@ -Layer 1 \ No newline at end of file diff --git a/src/vs/workbench/contrib/preferences/browser/media/clean-dark.svg b/src/vs/workbench/contrib/preferences/browser/media/clean-dark.svg deleted file mode 100644 index 3770d63d5f9..00000000000 --- a/src/vs/workbench/contrib/preferences/browser/media/clean-dark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/workbench/contrib/preferences/browser/media/clean.svg b/src/vs/workbench/contrib/preferences/browser/media/clean.svg deleted file mode 100644 index f86ec7d627d..00000000000 --- a/src/vs/workbench/contrib/preferences/browser/media/clean.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/workbench/contrib/preferences/browser/media/collapsed-dark.svg b/src/vs/workbench/contrib/preferences/browser/media/collapsed-dark.svg deleted file mode 100755 index cf5c3641aa7..00000000000 --- a/src/vs/workbench/contrib/preferences/browser/media/collapsed-dark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/workbench/contrib/preferences/browser/media/collapsed.svg b/src/vs/workbench/contrib/preferences/browser/media/collapsed.svg deleted file mode 100755 index 3a63808c358..00000000000 --- a/src/vs/workbench/contrib/preferences/browser/media/collapsed.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/workbench/contrib/preferences/browser/media/edit-json-dark.svg b/src/vs/workbench/contrib/preferences/browser/media/edit-json-dark.svg deleted file mode 100644 index 9a725bb41fd..00000000000 --- a/src/vs/workbench/contrib/preferences/browser/media/edit-json-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/preferences/browser/media/edit-json-light.svg b/src/vs/workbench/contrib/preferences/browser/media/edit-json-light.svg deleted file mode 100644 index 1339da7ce21..00000000000 --- a/src/vs/workbench/contrib/preferences/browser/media/edit-json-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/preferences/browser/media/edit.svg b/src/vs/workbench/contrib/preferences/browser/media/edit.svg deleted file mode 100755 index ecde9240842..00000000000 --- a/src/vs/workbench/contrib/preferences/browser/media/edit.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/workbench/contrib/preferences/browser/media/edit_inverse.svg b/src/vs/workbench/contrib/preferences/browser/media/edit_inverse.svg deleted file mode 100755 index da956cb2c60..00000000000 --- a/src/vs/workbench/contrib/preferences/browser/media/edit_inverse.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/workbench/contrib/preferences/browser/media/ellipsis-inverse.svg b/src/vs/workbench/contrib/preferences/browser/media/ellipsis-inverse.svg deleted file mode 100644 index e3337557a23..00000000000 --- a/src/vs/workbench/contrib/preferences/browser/media/ellipsis-inverse.svg +++ /dev/null @@ -1 +0,0 @@ -Ellipsis_bold_16x \ No newline at end of file diff --git a/src/vs/workbench/contrib/preferences/browser/media/ellipsis.svg b/src/vs/workbench/contrib/preferences/browser/media/ellipsis.svg deleted file mode 100644 index e3f85623356..00000000000 --- a/src/vs/workbench/contrib/preferences/browser/media/ellipsis.svg +++ /dev/null @@ -1 +0,0 @@ -Ellipsis_bold_16x \ No newline at end of file diff --git a/src/vs/workbench/contrib/preferences/browser/media/expanded-dark.svg b/src/vs/workbench/contrib/preferences/browser/media/expanded-dark.svg deleted file mode 100755 index 73d41e63990..00000000000 --- a/src/vs/workbench/contrib/preferences/browser/media/expanded-dark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/workbench/contrib/preferences/browser/media/expanded.svg b/src/vs/workbench/contrib/preferences/browser/media/expanded.svg deleted file mode 100755 index 75f73adbb02..00000000000 --- a/src/vs/workbench/contrib/preferences/browser/media/expanded.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/workbench/contrib/preferences/browser/media/open-file-inverse.svg b/src/vs/workbench/contrib/preferences/browser/media/open-file-inverse.svg deleted file mode 100644 index f6302185aa4..00000000000 --- a/src/vs/workbench/contrib/preferences/browser/media/open-file-inverse.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/workbench/contrib/preferences/browser/media/open-file.svg b/src/vs/workbench/contrib/preferences/browser/media/open-file.svg deleted file mode 100644 index d23a23c6b5f..00000000000 --- a/src/vs/workbench/contrib/preferences/browser/media/open-file.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/workbench/contrib/preferences/browser/media/regex-dark.svg b/src/vs/workbench/contrib/preferences/browser/media/regex-dark.svg deleted file mode 100644 index c303032e6a9..00000000000 --- a/src/vs/workbench/contrib/preferences/browser/media/regex-dark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/workbench/contrib/preferences/browser/media/regex.svg b/src/vs/workbench/contrib/preferences/browser/media/regex.svg deleted file mode 100644 index c677843beef..00000000000 --- a/src/vs/workbench/contrib/preferences/browser/media/regex.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/workbench/contrib/scm/browser/media/check-inverse.svg b/src/vs/workbench/contrib/scm/browser/media/check-inverse.svg deleted file mode 100644 index c225b2f597f..00000000000 --- a/src/vs/workbench/contrib/scm/browser/media/check-inverse.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/workbench/contrib/scm/browser/media/check.svg b/src/vs/workbench/contrib/scm/browser/media/check.svg deleted file mode 100644 index d45df06edf8..00000000000 --- a/src/vs/workbench/contrib/scm/browser/media/check.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index 9fc55917fc8..2353b3f217d 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -278,6 +278,28 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer this._register(lifecycleService.onBeforeShutdown(event => event.veto(this.beforeShutdown()))); this._onDidStateChange = this._register(new Emitter()); this.registerCommands(); + this.configurationResolverService.contributeVariable('defaultBuildTask', async (): Promise => { + let tasks = await this.getTasksForGroup(TaskGroup.Build); + if (tasks.length > 0) { + let { defaults, users } = this.splitPerGroupType(tasks); + if (defaults.length === 1) { + return defaults[0]._label; + } else if (defaults.length + users.length > 0) { + tasks = defaults.concat(users); + } + } + + let entry: TaskQuickPickEntry | null | undefined; + if (tasks && tasks.length > 0) { + entry = await this.showQuickPick(tasks, nls.localize('TaskService.pickBuildTaskForLabel', 'Select the build task')); + } + + let task: Task | undefined | null = entry ? entry.task : undefined; + if (!task) { + return undefined; + } + return task._label; + }); } public get onDidStateChange(): Event { diff --git a/src/vs/workbench/contrib/tasks/common/media/configure-hc.svg b/src/vs/workbench/contrib/tasks/common/media/configure-hc.svg new file mode 100644 index 00000000000..bd59cb81f6d --- /dev/null +++ b/src/vs/workbench/contrib/tasks/common/media/configure-hc.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/vs/workbench/contrib/tasks/common/media/task.contribution.css b/src/vs/workbench/contrib/tasks/common/media/task.contribution.css index 74394a3c971..2a617741ff6 100644 --- a/src/vs/workbench/contrib/tasks/common/media/task.contribution.css +++ b/src/vs/workbench/contrib/tasks/common/media/task.contribution.css @@ -7,7 +7,10 @@ background-image: url('configure-light.svg'); } -.vs-dark .monaco-workbench .quick-open-task-configure, -.hc-black .monaco-workbench .quick-open-task-configure { +.vs-dark .monaco-workbench .quick-open-task-configure { background-image: url('configure-dark.svg'); } + +.hc-black .monaco-workbench .quick-open-task-configure { + background-image: url('configure-hc.svg'); +} diff --git a/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts b/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts index f5d0b60b07b..882b085ff97 100644 --- a/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts +++ b/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts @@ -86,12 +86,12 @@ export abstract class BaseConfigurationResolverService extends AbstractVariableR }, envVariables); } - public resolveWithInteractionReplace(folder: IWorkspaceFolder | undefined, config: any, section?: string, variables?: IStringDictionary): Promise { - // resolve any non-interactive variables - config = this.resolveAny(folder, config); + public async resolveWithInteractionReplace(folder: IWorkspaceFolder | undefined, config: any, section?: string, variables?: IStringDictionary): Promise { + // resolve any non-interactive variables and any contributed variables + config = await this.resolveAny(folder, config); // resolve input variables in the order in which they are encountered - return this.resolveWithInteraction(folder, config, section, variables).then(mapping => { + return this.resolveWithInteraction(folder, config, section, variables, true).then(mapping => { // finally substitute evaluated command variables (if there are any) if (!mapping) { return null; @@ -103,9 +103,9 @@ export abstract class BaseConfigurationResolverService extends AbstractVariableR }); } - public resolveWithInteraction(folder: IWorkspaceFolder | undefined, config: any, section?: string, variables?: IStringDictionary): Promise | undefined> { - // resolve any non-interactive variables - const resolved = this.resolveAnyMap(folder, config); + public async resolveWithInteraction(folder: IWorkspaceFolder | undefined, config: any, section?: string, variables?: IStringDictionary, skipContributed: boolean = false): Promise | undefined> { + // resolve any non-interactive variables and any contributed variables + const resolved = await this.resolveAnyMap(folder, config); config = resolved.newConfig; const allVariableMapping: Map = resolved.resolvedVariables; diff --git a/src/vs/workbench/services/configurationResolver/common/configurationResolver.ts b/src/vs/workbench/services/configurationResolver/common/configurationResolver.ts index 5559fd26ea8..ea98be3d578 100644 --- a/src/vs/workbench/services/configurationResolver/common/configurationResolver.ts +++ b/src/vs/workbench/services/configurationResolver/common/configurationResolver.ts @@ -20,7 +20,7 @@ export interface IConfigurationResolverService { * Recursively resolves all variables in the given config and returns a copy of it with substituted values. * Command variables are only substituted if a "commandValueMapping" dictionary is given and if it contains an entry for the command. */ - resolveAny(folder: IWorkspaceFolder | undefined, config: any, commandValueMapping?: IStringDictionary): any; + resolveAny(folder: IWorkspaceFolder | undefined, config: any, commandValueMapping?: IStringDictionary): Promise; /** * Recursively resolves all variables (including commands and user input) in the given config and returns a copy of it with substituted values. @@ -36,6 +36,12 @@ export interface IConfigurationResolverService { * Keys in the map will be of the format input:variableName or command:variableName. */ resolveWithInteraction(folder: IWorkspaceFolder | undefined, config: any, section?: string, variables?: IStringDictionary): Promise | undefined>; + + /** + * Contributes a variable that can be resolved later. Consumers that use resolveAny, resolveWithInteraction, + * and resolveWithInteractionReplace will have contributed variables resolved. + */ + contributeVariable(variable: string, resolution: () => Promise): void; } export interface PromptStringInputInfo { diff --git a/src/vs/workbench/services/configurationResolver/common/variableResolver.ts b/src/vs/workbench/services/configurationResolver/common/variableResolver.ts index b9284cb24ad..469a3ea58b8 100644 --- a/src/vs/workbench/services/configurationResolver/common/variableResolver.ts +++ b/src/vs/workbench/services/configurationResolver/common/variableResolver.ts @@ -28,9 +28,12 @@ export interface IVariableResolveContext { export class AbstractVariableResolverService implements IConfigurationResolverService { static VARIABLE_REGEXP = /\$\{(.*?)\}/g; + static VARIABLE_REGEXP_SINGLE = /\$\{(.*?)\}/; _serviceBrand: undefined; + private _contributedVariables: Map Promise> = new Map(); + constructor( private _context: IVariableResolveContext, private _envVariables: IProcessEnvironment @@ -50,8 +53,7 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe return this.recursiveResolve(root ? root.uri : undefined, value); } - public resolveAnyBase(workspaceFolder: IWorkspaceFolder | undefined, config: any, commandValueMapping?: IStringDictionary, resolvedVariables?: Map): any { - + private async resolveAnyBase(workspaceFolder: IWorkspaceFolder | undefined, config: any, commandValueMapping?: IStringDictionary, resolvedVariables?: Map): Promise { const result = objects.deepClone(config) as any; // hoist platform specific attributes to top level @@ -69,16 +71,16 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe delete result.linux; // substitute all variables recursively in string values - return this.recursiveResolve(workspaceFolder ? workspaceFolder.uri : undefined, result, commandValueMapping, resolvedVariables); + return this.recursiveResolvePromise(workspaceFolder ? workspaceFolder.uri : undefined, result, commandValueMapping, resolvedVariables); } - public resolveAny(workspaceFolder: IWorkspaceFolder | undefined, config: any, commandValueMapping?: IStringDictionary): any { + public resolveAny(workspaceFolder: IWorkspaceFolder | undefined, config: any, commandValueMapping?: IStringDictionary): Promise { return this.resolveAnyBase(workspaceFolder, config, commandValueMapping); } - public resolveAnyMap(workspaceFolder: IWorkspaceFolder | undefined, config: any, commandValueMapping?: IStringDictionary): { newConfig: any, resolvedVariables: Map } { + protected async resolveAnyMap(workspaceFolder: IWorkspaceFolder | undefined, config: any, commandValueMapping?: IStringDictionary): Promise<{ newConfig: any, resolvedVariables: Map }> { const resolvedVariables = new Map(); - const newConfig = this.resolveAnyBase(workspaceFolder, config, commandValueMapping, resolvedVariables); + const newConfig = await this.resolveAnyBase(workspaceFolder, config, commandValueMapping, resolvedVariables); return { newConfig, resolvedVariables }; } @@ -90,6 +92,14 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe throw new Error('resolveWithInteraction not implemented.'); } + public contributeVariable(variable: string, resolution: () => Promise): void { + if (this._contributedVariables.has(variable)) { + throw new Error('Variable ' + variable + ' is contributed twice.'); + } else { + this._contributedVariables.set(variable, resolution); + } + } + private recursiveResolve(folderUri: uri | undefined, value: any, commandValueMapping?: IStringDictionary, resolvedVariables?: Map): any { if (types.isString(value)) { return this.resolveString(folderUri, value, commandValueMapping, resolvedVariables); @@ -106,6 +116,23 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe return value; } + private async recursiveResolvePromise(folderUri: uri | undefined, value: any, commandValueMapping?: IStringDictionary, resolvedVariables?: Map): Promise { + if (types.isString(value)) { + return this.resolveStringPromise(folderUri, value, commandValueMapping, resolvedVariables); + } else if (types.isArray(value)) { + return Promise.all(value.map(s => this.recursiveResolvePromise(folderUri, s, commandValueMapping, resolvedVariables))); + } else if (types.isObject(value)) { + let result: IStringDictionary | string[]> = Object.create(null); + const keys = Object.keys(value); + for (let key of keys) { + const replaced = await this.resolveStringPromise(folderUri, key, commandValueMapping, resolvedVariables); + result[replaced] = await this.recursiveResolvePromise(folderUri, value[key], commandValueMapping, resolvedVariables); + } + return result; + } + return value; + } + private resolveString(folderUri: uri | undefined, value: string, commandValueMapping: IStringDictionary | undefined, resolvedVariables?: Map): string { // loop through all variables occurrences in 'value' @@ -123,6 +150,37 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe return replaced; } + private async resolveStringPromise(folderUri: uri | undefined, value: string, commandValueMapping: IStringDictionary | undefined, resolvedVariables?: Map): Promise { + // loop through all variables occurrences in 'value' + const matches = value.match(AbstractVariableResolverService.VARIABLE_REGEXP); + const replaces: Map = new Map(); + if (!matches) { + return value; + } + for (const match of matches) { + const evaluate = await this.evaluateSingleContributedVariable(match, match.match(AbstractVariableResolverService.VARIABLE_REGEXP_SINGLE)![1]); + if (evaluate !== match) { + replaces.set(match, evaluate); + } + } + + const replaced = value.replace(AbstractVariableResolverService.VARIABLE_REGEXP, (match: string, variable: string) => { + + let resolvedValue = this.evaluateSingleVariable(match, variable, folderUri, commandValueMapping); + if ((resolvedValue === match) && (replaces.has(match))) { + resolvedValue = replaces.get(match)!; + } + + if (resolvedVariables) { + resolvedVariables.set(variable, resolvedValue); + } + + return resolvedValue; + }); + + return replaced; + } + private evaluateSingleVariable(match: string, variable: string, folderUri: uri | undefined, commandValueMapping: IStringDictionary | undefined): string { // try to separate variable arguments from variable name @@ -271,6 +329,16 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe } } + private async evaluateSingleContributedVariable(match: string, variable: string): Promise { + if (this._contributedVariables.has(variable)) { + const contributedValue: string | undefined = await this._contributedVariables.get(variable)!(); + if (contributedValue !== undefined) { + return contributedValue; + } + } + return match; + } + private resolveFromMap(match: string, argument: string | undefined, commandValueMapping: IStringDictionary | undefined, prefix: string): string { if (argument && commandValueMapping) { const v = commandValueMapping[prefix + ':' + argument]; diff --git a/src/vs/workbench/services/configurationResolver/test/electron-browser/configurationResolverService.test.ts b/src/vs/workbench/services/configurationResolver/test/electron-browser/configurationResolverService.test.ts index 49a80b64df0..6b29d49b2eb 100644 --- a/src/vs/workbench/services/configurationResolver/test/electron-browser/configurationResolverService.test.ts +++ b/src/vs/workbench/services/configurationResolver/test/electron-browser/configurationResolverService.test.ts @@ -487,6 +487,19 @@ suite('Configuration Resolver Service', () => { assert.equal(2, mockCommandService.callCount); }); }); + test('contributed variable', () => { + const buildTask = 'npm: compile'; + const variable = 'defaultBuildTask'; + const configuration = { + 'name': '${' + variable + '}', + }; + configurationResolverService!.contributeVariable(variable, async () => { return buildTask; }); + return configurationResolverService!.resolveAny(workspace, configuration).then(result => { + assert.deepEqual(result, { + 'name': `${buildTask}` + }); + }); + }); }); diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index c3545a5ae06..8caaa7a7ad3 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -226,7 +226,6 @@ import 'vs/workbench/contrib/themes/browser/themes.contribution'; import 'vs/workbench/contrib/watermark/browser/watermark'; // Welcome -import 'vs/workbench/contrib/welcome/walkThrough/browser/walkThrough.contribution'; import 'vs/workbench/contrib/welcome/overlay/browser/welcomeOverlay'; // Call Hierarchy diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index 27ef543f040..562a0195477 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -136,6 +136,7 @@ import 'vs/workbench/contrib/cli/node/cli.contribution'; import 'vs/workbench/contrib/themes/test/electron-browser/themes.test.contribution'; // Welcome +import 'vs/workbench/contrib/welcome/walkThrough/browser/walkThrough.contribution'; import 'vs/workbench/contrib/welcome/gettingStarted/electron-browser/gettingStarted.contribution'; import 'vs/workbench/contrib/welcome/page/browser/welcomePage.contribution'; diff --git a/yarn.lock b/yarn.lock index d1417b2eae6..f8180b64097 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3697,10 +3697,10 @@ gulp-symdest@^1.1.1: queue "^3.1.0" vinyl-fs "^2.4.3" -gulp-tsb@4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/gulp-tsb/-/gulp-tsb-4.0.1.tgz#be2d8900d9227abf0e728a33139891e49b9e85d3" - integrity sha512-HHR5qMjj/NyFlYdY6AIql7bWosFAknNfeJumwdkPkNaw6GtHVoaK+hPmWgmyK9Otf9nqcETtI5KI2vIla6Vhdw== +gulp-tsb@4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/gulp-tsb/-/gulp-tsb-4.0.2.tgz#8717a18f1ce032147e010028f0a59863bd552b96" + integrity sha512-xF88h0vFH8JkunSnmVrYfrR3LGTMAY+KTkHHF/S9BAOCsdaC83/hv4EmpcLxev7B+2yd3+xcitlsDFMBSo/gSw== dependencies: ansi-colors "^1.0.1" fancy-log "^1.3.2"