diff --git a/build/azure-pipelines/common/publish.ts b/build/azure-pipelines/common/publish.ts index 2095c2d2532..ef17c3fd685 100644 --- a/build/azure-pipelines/common/publish.ts +++ b/build/azure-pipelines/common/publish.ts @@ -152,9 +152,13 @@ async function publish(commit: string, quality: string, platform: string, type: const queuedBy = process.env['BUILD_QUEUEDBY']!; const sourceBranch = process.env['BUILD_SOURCEBRANCH']!; - const isReleased = quality === 'insider' - && /^master$|^refs\/heads\/master$/.test(sourceBranch) - && /Project Collection Service Accounts|Microsoft.VisualStudio.Services.TFS/.test(queuedBy); + const isReleased = ( + // Insiders: nightly build from master + (quality === 'insider' && /^master$|^refs\/heads\/master$/.test(sourceBranch) && /Project Collection Service Accounts|Microsoft.VisualStudio.Services.TFS/.test(queuedBy)) || + + // Exploration: any build from electron-4.0.x branch + (quality === 'exploration' && /^electron-4.0.x$|^refs\/heads\/electron-4.0.x$/.test(sourceBranch)) + ); console.log('Publishing...'); console.log('Quality:', quality); diff --git a/build/builtInExtensions.json b/build/builtInExtensions.json index 37d63713475..e0794621304 100644 --- a/build/builtInExtensions.json +++ b/build/builtInExtensions.json @@ -16,7 +16,7 @@ }, { "name": "ms-vscode.node-debug2", - "version": "1.32.0", + "version": "1.32.1", "repo": "https://github.com/Microsoft/vscode-node-debug2", "metadata": { "id": "36d19e17-7569-4841-a001-947eb18602b2", diff --git a/build/download/download.js b/build/download/download.js new file mode 100644 index 00000000000..c70bae336a6 --- /dev/null +++ b/build/download/download.js @@ -0,0 +1,91 @@ +"use strict"; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +Object.defineProperty(exports, "__esModule", { value: true }); +const https = require("https"); +const fs = require("fs"); +const path = require("path"); +const cp = require("child_process"); +function ensureDir(filepath) { + if (!fs.existsSync(filepath)) { + ensureDir(path.dirname(filepath)); + fs.mkdirSync(filepath); + } +} +function download(options, destination) { + ensureDir(path.dirname(destination)); + return new Promise((c, e) => { + const fd = fs.openSync(destination, 'w'); + const req = https.get(options, (res) => { + res.on('data', (chunk) => { + fs.writeSync(fd, chunk); + }); + res.on('end', () => { + fs.closeSync(fd); + c(); + }); + }); + req.on('error', (reqErr) => { + console.error(`request to ${options.host}${options.path} failed.`); + console.error(reqErr); + e(reqErr); + }); + }); +} +const MARKER_ARGUMENT = `_download_fork_`; +function base64encode(str) { + return Buffer.from(str, 'utf8').toString('base64'); +} +function base64decode(str) { + return Buffer.from(str, 'base64').toString('utf8'); +} +function downloadInExternalProcess(options) { + const url = `https://${options.requestOptions.host}${options.requestOptions.path}`; + console.log(`Downloading ${url}...`); + return new Promise((c, e) => { + const child = cp.fork(__filename, [MARKER_ARGUMENT, base64encode(JSON.stringify(options))], { + stdio: ['pipe', 'pipe', 'pipe', 'ipc'] + }); + let stderr = []; + child.stderr.on('data', (chunk) => { + stderr.push(typeof chunk === 'string' ? Buffer.from(chunk) : chunk); + }); + child.on('exit', (code) => { + if (code === 0) { + // normal termination + console.log(`Finished downloading ${url}.`); + c(); + } + else { + // abnormal termination + console.error(Buffer.concat(stderr).toString()); + e(new Error(`Download of ${url} failed.`)); + } + }); + }); +} +exports.downloadInExternalProcess = downloadInExternalProcess; +function _downloadInExternalProcess() { + let options; + try { + options = JSON.parse(base64decode(process.argv[3])); + } + catch (err) { + console.error(`Cannot read arguments`); + console.error(err); + process.exit(-1); + return; + } + download(options.requestOptions, options.destinationPath).then(() => { + process.exit(0); + }, (err) => { + console.error(err); + process.exit(-2); + }); +} +if (process.argv.length >= 4 && process.argv[2] === MARKER_ARGUMENT) { + // running as forked download script + _downloadInExternalProcess(); +} diff --git a/build/download/download.ts b/build/download/download.ts new file mode 100644 index 00000000000..01ac8864d0b --- /dev/null +++ b/build/download/download.ts @@ -0,0 +1,111 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as https from 'https'; +import * as fs from 'fs'; +import * as path from 'path'; +import * as cp from 'child_process'; + +function ensureDir(filepath: string) { + if (!fs.existsSync(filepath)) { + ensureDir(path.dirname(filepath)); + fs.mkdirSync(filepath); + } +} + +function download(options: https.RequestOptions, destination: string): Promise { + ensureDir(path.dirname(destination)); + + return new Promise((c, e) => { + const fd = fs.openSync(destination, 'w'); + const req = https.get(options, (res) => { + res.on('data', (chunk) => { + fs.writeSync(fd, chunk); + }); + res.on('end', () => { + fs.closeSync(fd); + c(); + }); + }); + req.on('error', (reqErr) => { + console.error(`request to ${options.host}${options.path} failed.`); + console.error(reqErr); + e(reqErr); + }); + }); +} + +const MARKER_ARGUMENT = `_download_fork_`; + +function base64encode(str: string): string { + return Buffer.from(str, 'utf8').toString('base64'); +} + +function base64decode(str: string): string { + return Buffer.from(str, 'base64').toString('utf8'); +} + +export interface IDownloadRequestOptions { + host: string; + path: string; +} + +export interface IDownloadOptions { + requestOptions: IDownloadRequestOptions; + destinationPath: string; +} + +export function downloadInExternalProcess(options: IDownloadOptions): Promise { + const url = `https://${options.requestOptions.host}${options.requestOptions.path}`; + console.log(`Downloading ${url}...`); + return new Promise((c, e) => { + const child = cp.fork( + __filename, + [MARKER_ARGUMENT, base64encode(JSON.stringify(options))], + { + stdio: ['pipe', 'pipe', 'pipe', 'ipc'] + } + ); + let stderr: Buffer[] = []; + child.stderr.on('data', (chunk) => { + stderr.push(typeof chunk === 'string' ? Buffer.from(chunk) : chunk); + }); + child.on('exit', (code) => { + if (code === 0) { + // normal termination + console.log(`Finished downloading ${url}.`); + c(); + } else { + // abnormal termination + console.error(Buffer.concat(stderr).toString()); + e(new Error(`Download of ${url} failed.`)); + } + }); + }); +} + +function _downloadInExternalProcess() { + let options: IDownloadOptions; + try { + options = JSON.parse(base64decode(process.argv[3])); + } catch (err) { + console.error(`Cannot read arguments`); + console.error(err); + process.exit(-1); + return; + } + + download(options.requestOptions, options.destinationPath).then(() => { + process.exit(0); + }, (err) => { + console.error(err); + process.exit(-2); + }); +} + +if (process.argv.length >= 4 && process.argv[2] === MARKER_ARGUMENT) { + // running as forked download script + _downloadInExternalProcess(); +} diff --git a/build/gulpfile.editor.js b/build/gulpfile.editor.js index 05c7d4fb8b5..7bbf246a8dc 100644 --- a/build/gulpfile.editor.js +++ b/build/gulpfile.editor.js @@ -234,6 +234,14 @@ const finalEditorResourcesTask = task.define('final-editor-resources', () => { })) .pipe(gulp.dest('out-monaco-editor-core')), + // version.txt + gulp.src('build/monaco/version.txt') + .pipe(es.through(function (data) { + data.contents = Buffer.from(`monaco-editor-core: https://github.com/Microsoft/vscode/tree/${sha1}`); + this.emit('data', data); + })) + .pipe(gulp.dest('out-monaco-editor-core')), + // README.md gulp.src('build/monaco/README-npm.md') .pipe(es.through(function (data) { diff --git a/build/gulpfile.hygiene.js b/build/gulpfile.hygiene.js index 1af30179a06..2525e625e43 100644 --- a/build/gulpfile.hygiene.js +++ b/build/gulpfile.hygiene.js @@ -81,7 +81,7 @@ const indentationFilter = [ '!src/typings/**/*.d.ts', '!extensions/**/*.d.ts', '!**/*.{svg,exe,png,bmp,scpt,bat,cmd,cur,ttf,woff,eot,md,ps1,template,yaml,yml,d.ts.recipe,ico,icns}', - '!build/{lib,tslintRules}/**/*.js', + '!build/{lib,tslintRules,download}/**/*.js', '!build/**/*.sh', '!build/azure-pipelines/**/*.js', '!build/azure-pipelines/**/*.config', @@ -228,7 +228,7 @@ function hygiene(some) { let formatted = result.dest.replace(/\r\n/gm, '\n'); if (original !== formatted) { - console.error('File not formatted:', file.relative); + console.error("File not formatted. Run the 'Format Document' command to fix it:", file.relative); errorCount++; } cb(null, file); diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 1770b2c149d..9c2231119e9 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -69,7 +69,7 @@ const vscodeResources = [ 'out-build/vs/base/browser/ui/octiconLabel/octicons/**', 'out-build/vs/workbench/browser/media/*-theme.css', 'out-build/vs/workbench/contrib/debug/**/*.json', - 'out-build/vs/workbench/contrib/execution/**/*.scpt', + 'out-build/vs/workbench/contrib/externalTerminal/**/*.scpt', 'out-build/vs/workbench/contrib/webview/electron-browser/webview-pre.js', 'out-build/vs/**/markdown.css', 'out-build/vs/workbench/contrib/tasks/**/*.json', diff --git a/build/lib/i18n.resources.json b/build/lib/i18n.resources.json index dc49b431f87..424fd364c57 100644 --- a/build/lib/i18n.resources.json +++ b/build/lib/i18n.resources.json @@ -55,11 +55,11 @@ "project": "vscode-workbench" }, { - "name": "vs/workbench/contrib/execution", + "name": "vs/workbench/contrib/extensions", "project": "vscode-workbench" }, { - "name": "vs/workbench/contrib/extensions", + "name": "vs/workbench/contrib/externalTerminal", "project": "vscode-workbench" }, { diff --git a/build/lib/util.js b/build/lib/util.js index 17edc75f65f..34ee13695fd 100644 --- a/build/lib/util.js +++ b/build/lib/util.js @@ -14,6 +14,8 @@ const fs = require("fs"); const _rimraf = require("rimraf"); const git = require("./git"); const VinylFile = require("vinyl"); +const download_1 = require("../download/download"); +const REPO_ROOT = path.join(__dirname, '../../'); const NoCancellationToken = { isCancellationRequested: () => false }; function incremental(streamProvider, initial, supportsCancellation) { const input = es.through(); @@ -221,3 +223,38 @@ function versionStringToNumber(versionStr) { return parseInt(match[1], 10) * 1e4 + parseInt(match[2], 10) * 1e2 + parseInt(match[3], 10); } exports.versionStringToNumber = versionStringToNumber; +function download(requestOptions) { + const result = es.through(); + const filename = path.join(REPO_ROOT, `.build/tmp-${Date.now()}-${path.posix.basename(requestOptions.path)}`); + const opts = { + requestOptions: requestOptions, + destinationPath: filename + }; + download_1.downloadInExternalProcess(opts).then(() => { + fs.stat(filename, (err, stat) => { + if (err) { + result.emit('error', err); + return; + } + fs.readFile(filename, (err, data) => { + if (err) { + result.emit('error', err); + return; + } + fs.unlink(filename, () => { + result.emit('data', new VinylFile({ + path: path.normalize(requestOptions.path), + stat: stat, + base: path.normalize(requestOptions.path), + contents: data + })); + result.emit('end'); + }); + }); + }); + }, (err) => { + result.emit('error', err); + }); + return result; +} +exports.download = download; diff --git a/build/lib/util.ts b/build/lib/util.ts index a773b2449da..44ac9d0dc74 100644 --- a/build/lib/util.ts +++ b/build/lib/util.ts @@ -17,6 +17,9 @@ import * as git from './git'; import * as VinylFile from 'vinyl'; import { ThroughStream } from 'through'; import * as sm from 'source-map'; +import { IDownloadOptions, downloadInExternalProcess, IDownloadRequestOptions } from '../download/download'; + +const REPO_ROOT = path.join(__dirname, '../../'); export interface ICancellationToken { isCancellationRequested(): boolean; @@ -280,3 +283,38 @@ export function versionStringToNumber(versionStr: string) { return parseInt(match[1], 10) * 1e4 + parseInt(match[2], 10) * 1e2 + parseInt(match[3], 10); } + +export function download(requestOptions: IDownloadRequestOptions): NodeJS.ReadWriteStream { + const result = es.through(); + const filename = path.join(REPO_ROOT, `.build/tmp-${Date.now()}-${path.posix.basename(requestOptions.path)}`); + const opts: IDownloadOptions = { + requestOptions: requestOptions, + destinationPath: filename + }; + downloadInExternalProcess(opts).then(() => { + fs.stat(filename, (err, stat) => { + if (err) { + result.emit('error', err); + return; + } + fs.readFile(filename, (err, data) => { + if (err) { + result.emit('error', err); + return; + } + fs.unlink(filename, () => { + result.emit('data', new VinylFile({ + path: path.normalize(requestOptions.path), + stat: stat, + base: path.normalize(requestOptions.path), + contents: data + })); + result.emit('end'); + }); + }); + }); + }, (err) => { + result.emit('error', err); + }); + return result; +} diff --git a/build/monaco/ThirdPartyNotices.txt b/build/monaco/ThirdPartyNotices.txt index a459893cc97..1de70ddaab6 100644 --- a/build/monaco/ThirdPartyNotices.txt +++ b/build/monaco/ThirdPartyNotices.txt @@ -7,6 +7,31 @@ herein, whether by implication, estoppel or otherwise. +%% nodejs path library (https://github.com/nodejs/node/tree/43dd49c9782848c25e5b03448c8a0f923f13c158) +========================================= +Copyright Joyent, Inc. and other Node contributors. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to permit +persons to whom the Software is furnished to do so, subject to the +following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +USE OR OTHER DEALINGS IN THE SOFTWARE. +========================================= +END OF nodejs path library NOTICES AND INFORMATION + %% promise-polyfill version 8.1.0 (https://github.com/taylorhakes/promise-polyfill) ========================================= diff --git a/build/monaco/monaco.usage.recipe b/build/monaco/monaco.usage.recipe index fad1e8ee724..13290a7abb5 100644 --- a/build/monaco/monaco.usage.recipe +++ b/build/monaco/monaco.usage.recipe @@ -13,6 +13,7 @@ import { QuickOpenWidget } from './vs/base/parts/quickopen/browser/quickOpenWidg import { WorkbenchAsyncDataTree } from './vs/platform/list/browser/listService'; import { SyncDescriptor0, SyncDescriptor1, SyncDescriptor2, SyncDescriptor3, SyncDescriptor4, SyncDescriptor5, SyncDescriptor6, SyncDescriptor7, SyncDescriptor8 } from './vs/platform/instantiation/common/descriptors'; import { DiffNavigator } from './vs/editor/browser/widget/diffNavigator'; +import { DocumentRangeFormattingEditProvider } from './vs/editor/common/modes'; import * as editorAPI from './vs/editor/editor.api'; (function () { @@ -32,6 +33,7 @@ import * as editorAPI from './vs/editor/editor.api'; a = (>b).getProxyObject; // IWorkerClient a = create1; a = create2; + a = (b).extensionId; // injection madness a = (>b).ctor; diff --git a/build/monaco/package.json b/build/monaco/package.json index efd919085b2..1962694ce6d 100644 --- a/build/monaco/package.json +++ b/build/monaco/package.json @@ -1,7 +1,7 @@ { "name": "monaco-editor-core", "private": true, - "version": "0.14.3", + "version": "0.16.0", "description": "A browser based code editor", "author": "Microsoft Corporation", "license": "MIT", diff --git a/build/monaco/version.txt b/build/monaco/version.txt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/extensions/cpp/build/update-grammars.js b/extensions/cpp/build/update-grammars.js index f02f51ca2cc..406b326156c 100644 --- a/extensions/cpp/build/update-grammars.js +++ b/extensions/cpp/build/update-grammars.js @@ -6,8 +6,8 @@ var updateGrammar = require('../../../build/npm/update-grammar'); -updateGrammar.update('atom/language-c', 'grammars/c.cson', './syntaxes/c.tmLanguage.json'); -updateGrammar.update('atom/language-c', 'grammars/c%2B%2B.cson', './syntaxes/cpp.tmLanguage.json'); +updateGrammar.update('jeff-hykin/cpp-textmate-grammar', '/syntaxes/c.tmLanguage.json', './syntaxes/c.tmLanguage.json'); +updateGrammar.update('jeff-hykin/cpp-textmate-grammar', '/syntaxes/cpp.tmLanguage.json', './syntaxes/cpp.tmLanguage.json'); // `source.c.platform` which is still included by other grammars updateGrammar.update('textmate/c.tmbundle', 'Syntaxes/Platform.tmLanguage', './syntaxes/platform.tmLanguage.json'); diff --git a/extensions/cpp/cgmanifest.json b/extensions/cpp/cgmanifest.json index e6666933f61..08cd245eac1 100644 --- a/extensions/cpp/cgmanifest.json +++ b/extensions/cpp/cgmanifest.json @@ -4,14 +4,14 @@ "component": { "type": "git", "git": { - "name": "atom/language-c", - "repositoryUrl": "https://github.com/atom/language-c", - "commitHash": "9c0c5f202741a5647025db8d5df5fefba47b036c" + "name": "jeff-hykin/cpp-textmate-grammar", + "repositoryUrl": "https://github.com/jeff-hykin/cpp-textmate-grammar", + "commitHash": "d57808aa3db2242f1f2be1aec19649a852aaa52e" } }, "license": "MIT", - "version": "0.58.1", - "description": "The files syntaxes/c.json and syntaxes/c++.json were derived from the Atom package https://atom.io/packages/language-c which was originally converted from the C TextMate bundle https://github.com/textmate/c.tmbundle." + "version": "1.4.5", + "description": "The files syntaxes/c.json and syntaxes/c++.json were derived from https://github.com/atom/language-c which was originally converted from the C TextMate bundle https://github.com/textmate/c.tmbundle." }, { "component": { diff --git a/extensions/cpp/syntaxes/c.tmLanguage.json b/extensions/cpp/syntaxes/c.tmLanguage.json index bcd5470a64c..efb692081eb 100644 --- a/extensions/cpp/syntaxes/c.tmLanguage.json +++ b/extensions/cpp/syntaxes/c.tmLanguage.json @@ -1,10 +1,10 @@ { "information_for_contributors": [ - "This file has been converted from https://github.com/atom/language-c/blob/master/grammars/c.cson", + "This file has been converted from https://github.com/jeff-hykin/cpp-textmate-grammar/blob/master//syntaxes/c.tmLanguage.json", "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/atom/language-c/commit/9c0c5f202741a5647025db8d5df5fefba47b036c", + "version": "https://github.com/jeff-hykin/cpp-textmate-grammar/commit/9de911d74546b9ae74c57e404515935a0405e696", "name": "C", "scopeName": "source.c", "patterns": [ @@ -50,6 +50,9 @@ { "include": "#operators" }, + { + "include": "#operator_overload" + }, { "include": "#numbers" }, @@ -57,7 +60,7 @@ "include": "#strings" }, { - "begin": "(?x)\n^\\s* ((\\#)\\s*define) \\s+ # define\n((?[a-zA-Z_$][\\w$]*)) # macro name\n(?:\n (\\()\n (\n \\s* \\g \\s* # first argument\n ((,) \\s* \\g \\s*)* # additional arguments\n (?:\\.\\.\\.)? # varargs ellipsis?\n )\n (\\))\n)?", + "begin": "(?x)\n^\\s* ((\\#)\\s*define) \\s+\t# define\n((?[a-zA-Z_$][\\w$]*))\t # macro name\n(?:\n (\\()\n\t(\n\t \\s* \\g \\s*\t\t # first argument\n\t ((,) \\s* \\g \\s*)* # additional arguments\n\t (?:\\.\\.\\.)?\t\t\t# varargs ellipsis?\n\t)\n (\\))\n)?", "beginCaptures": { "1": { "name": "keyword.control.directive.define.c" @@ -90,8 +93,8 @@ ] }, { - "begin": "^\\s*((#)\\s*(error|warning))\\b", - "captures": { + "begin": "^\\s*((#)\\s*(error|warning))\\b\\s*", + "beginCaptures": { "1": { "name": "keyword.control.directive.diagnostic.$3.c" }, @@ -99,17 +102,61 @@ "name": "punctuation.definition.directive.c" } }, - "end": "(?=+!]+|\\(\\)|\\[\\]))\\s*\\(\n)", - "end": "(?<=\\))(?!\\w)", + "begin": "(?!(?:not|compl|sizeof|new|delete|not_eq|bitand|xor|bitor|and|or|throw|and_eq|xor_eq|or_eq|alignof|alignas|typeid|noexcept|static_cast|dynamic_cast|const_cast|reinterpret_cast|while|for|do|if|else|goto|switch|try|catch|return|break|case|continue|default|auto|void|char|short|int|signed|unsigned|long|float|double|bool|wchar_t|u_char|u_short|u_int|u_long|ushort|uint|u_quad_t|quad_t|qaddr_t|caddr_t|daddr_t|div_t|dev_t|fixpt_t|blkcnt_t|blksize_t|gid_t|in_addr_t|in_port_t|ino_t|key_t|mode_t|nlink_t|id_t|pid_t|off_t|segsz_t|swblk_t|uid_t|id_t|clock_t|size_t|ssize_t|time_t|useconds_t|suseconds_t|pthread_attr_t|pthread_cond_t|pthread_condattr_t|pthread_mutex_t|pthread_mutexattr_t|pthread_once_t|pthread_rwlock_t|pthread_rwlockattr_t|pthread_t|pthread_key_t|int8_t|int16_t|int32_t|int64_t|uint8_t|uint16_t|uint32_t|uint64_t|int_least8_t|int_least16_t|int_least32_t|int_least64_t|uint_least8_t|uint_least16_t|uint_least32_t|uint_least64_t|int_fast8_t|int_fast16_t|int_fast32_t|int_fast64_t|uint_fast8_t|uint_fast16_t|uint_fast32_t|uint_fast64_t|intptr_t|uintptr_t|intmax_t|intmax_t|uintmax_t|uintmax_t|const|static|volatile|register|restrict|constexpr|extern|inline|mutable|friend|NULL|true|false|TRUE|FALSE|nullptr|class|struct|union|enum|explicit|virtual|mutable|constexpr|consteval|private|protected|public|if|elif|else|endif|ifdef|ifndef|define|undef|include|line|error|warning|pragma|_Pragma|defined|__has_include|__has_cpp_attribute|this|template|namespace|using|operator|typedef|decltype|typename|asm|__asm__|atomic_cancel|atomic_commit|atomic_noexcept|concept|co_await|co_return|co_yield|export|import|module|reflexpr|requires|synchronized|thread_local|audit|axiom|transaction_safe|transaction_safe_dynamic)\\s*\\()(?=[a-zA-Z_][a-zA-Z0-9_]*\\s*\\()", + "end": "(?<=\\))", "name": "meta.function.c", "patterns": [ { @@ -282,15 +329,31 @@ "include": "#line_continuation_character" }, { - "match": "(\\[)|(\\])", - "captures": { + "name": "meta.bracket.square.access.c", + "begin": "([a-zA-Z_][a-zA-Z_0-9]*|(?<=[\\]\\)]))?(\\[)(?!\\])", + "beginCaptures": { "1": { - "name": "punctuation.definition.begin.bracket.square.c" + "name": "variable.object.c" }, "2": { + "name": "punctuation.definition.begin.bracket.square.c" + } + }, + "end": "\\]", + "endCaptures": { + "0": { "name": "punctuation.definition.end.bracket.square.c" } - } + }, + "patterns": [ + { + "include": "#function-call-innards" + } + ] + }, + { + "name": "storage.modifier.array.bracket.square.c", + "match": "\\[\\s*\\]" }, { "match": ";", @@ -302,8 +365,56 @@ } ], "repository": { - "access": { + "probably_a_parameter": { + "match": "(?:([a-zA-Z_][a-zA-Z0-9_]*)\\s*(?==)|(?<=(?:[a-zA-Z0-9_]\\s|[&*>\\]\\)]))\\s*([a-zA-Z_][a-zA-Z0-9_]*)\\s*(?=(?:\\[\\]\\s*)?(?:,|\\))))", "captures": { + "1": { + "name": "variable.parameter.probably.defaulted.c" + }, + "2": { + "name": "variable.parameter.probably.c" + } + } + }, + "operator_overload": { + "begin": "((?:[a-zA-Z_][a-zA-Z0-9_]*\\s*(?:<(?:[\\s<>,\\w])*>\\s*)?::)*)(operator)((?:\\s*(?:\\+\\+|\\-\\-|\\(\\)|\\[\\]|\\->|\\+\\+|\\-\\-|\\+|\\-|!|~|\\*|&|\\->\\*|\\*|\\/|%|\\+|\\-|<<|>>|<=>|<|<=|>|>=|==|!=|&|\\^|\\||&&|\\|\\||=|\\+=|\\-=|\\*=|\\/=|%=|<<=|>>=|&=|\\^=|\\|=|,)|\\s+(?:(?:new|new\\[\\]|delete|delete\\[\\])|[a-zA-Z_][a-zA-Z0-9_]*)))\\s*(\\()", + "beginCaptures": { + "1": { + "name": "entity.scope.c" + }, + "2": { + "name": "entity.name.operator.overload.c" + }, + "3": { + "name": "entity.name.operator.overloadee.c" + }, + "4": { + "name": "punctuation.section.parameters.begin.bracket.round.c" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.section.parameters.end.bracket.round.c" + } + }, + "name": "meta.function.definition.parameters.operator-overload.c", + "patterns": [ + { + "include": "#probably_a_parameter" + }, + { + "include": "#function-innards" + } + ] + }, + "access-method": { + "name": "meta.function-call.member.c", + "begin": "([a-zA-Z_][a-zA-Z_0-9]*|(?<=[\\]\\)]))\\s*(?:(\\.)|(->))((?:(?:[a-zA-Z_][a-zA-Z_0-9]*)\\s*(?:(?:\\.)|(?:->)))*)\\s*([a-zA-Z_][a-zA-Z_0-9]*)(\\()", + "beginCaptures": { + "1": { + "name": "variable.object.c" + }, "2": { "name": "punctuation.separator.dot-access.c" }, @@ -311,10 +422,81 @@ "name": "punctuation.separator.pointer-access.c" }, "4": { - "name": "variable.other.member.c" + "patterns": [ + { + "match": "\\.", + "name": "punctuation.separator.dot-access.c" + }, + { + "match": "->", + "name": "punctuation.separator.pointer-access.c" + }, + { + "match": "[a-zA-Z_][a-zA-Z_0-9]*", + "name": "variable.object.c" + }, + { + "name": "everything.else", + "match": ".+" + } + ] + }, + "5": { + "name": "entity.name.function.member.c" + }, + "6": { + "name": "punctuation.section.arguments.begin.bracket.round.function.member.c" } }, - "match": "((\\.)|(->))\\s*(([a-zA-Z_][a-zA-Z_0-9]*)\\b(?!\\s*\\())?" + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.section.arguments.end.bracket.round.function.member.c" + } + }, + "patterns": [ + { + "include": "#function-call-innards" + } + ] + }, + "access-member": { + "name": "variable.object.access.c", + "match": "(?:([a-zA-Z_][a-zA-Z0-9_]*)|(?<=\\]|\\)))\\s*(?:((?:\\.|\\.\\*))|((?:->|->\\*)))\\s*((?:[a-zA-Z_][a-zA-Z0-9_]*\\s*(?:\\.|->)\\s*)*)\\b(?!(?:auto|void|char|short|int|signed|unsigned|long|float|double|bool|wchar_t|u_char|u_short|u_int|u_long|ushort|uint|u_quad_t|quad_t|qaddr_t|caddr_t|daddr_t|div_t|dev_t|fixpt_t|blkcnt_t|blksize_t|gid_t|in_addr_t|in_port_t|ino_t|key_t|mode_t|nlink_t|id_t|pid_t|off_t|segsz_t|swblk_t|uid_t|id_t|clock_t|size_t|ssize_t|time_t|useconds_t|suseconds_t|pthread_attr_t|pthread_cond_t|pthread_condattr_t|pthread_mutex_t|pthread_mutexattr_t|pthread_once_t|pthread_rwlock_t|pthread_rwlockattr_t|pthread_t|pthread_key_t|int8_t|int16_t|int32_t|int64_t|uint8_t|uint16_t|uint32_t|uint64_t|int_least8_t|int_least16_t|int_least32_t|int_least64_t|uint_least8_t|uint_least16_t|uint_least32_t|uint_least64_t|int_fast8_t|int_fast16_t|int_fast32_t|int_fast64_t|uint_fast8_t|uint_fast16_t|uint_fast32_t|uint_fast64_t|intptr_t|uintptr_t|intmax_t|intmax_t|uintmax_t|uintmax_t))([a-zA-Z_][a-zA-Z0-9_]*)\\b(?!\\()", + "captures": { + "1": { + "name": "variable.object.c" + }, + "2": { + "name": "punctuation.separator.dot-access.c" + }, + "3": { + "name": "punctuation.separator.pointer-access.c" + }, + "4": { + "patterns": [ + { + "match": "\\.", + "name": "punctuation.separator.dot-access.c" + }, + { + "match": "->", + "name": "punctuation.separator.pointer-access.c" + }, + { + "match": "[a-zA-Z_][a-zA-Z0-9_]*", + "name": "variable.object.c" + }, + { + "match": ".+", + "name": "everything.else" + } + ] + }, + "5": { + "name": "variable.other.member.c" + } + } }, "block": { "patterns": [ @@ -352,25 +534,36 @@ "include": "#preprocessor-rule-conditional-block" }, { - "include": "#access" + "include": "#access-method" }, { - "include": "#libc" + "include": "#access-member" }, { "include": "#c_function_call" }, { - "captures": { + "name": "meta.initialization.c", + "begin": "(?x)\n(?:\n (?:\n\t(?=\\s)(?=+!]+ | \\(\\) | \\[\\]))\n)\n\\s*(\\() # opening bracket", + "beginCaptures": { "1": { "name": "variable.other.c" }, "2": { - "name": "punctuation.definition.parameters.c" + "name": "punctuation.section.parens.begin.bracket.round.initialization.c" } }, - "match": "(?x)\n(?:\n (?:\n (?=\\s)(?=+!]+ | \\(\\) | \\[\\]))\n)\n\\s*(\\() # opening bracket", - "name": "meta.initialization.c" + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.section.parens.end.bracket.round.initialization.c" + } + }, + "patterns": [ + { + "include": "#function-call-innards" + } + ] }, { "begin": "{", @@ -400,7 +593,7 @@ ] }, "c_function_call": { - "begin": "(?x)\n(?!(?:while|for|do|if|else|switch|catch|enumerate|return|typeid|alignof|alignas|sizeof|[cr]?iterate)\\s*\\()\n(?=\n(?:[A-Za-z_][A-Za-z0-9_]*+|::)++\\s*\\( # actual name\n|\n(?:(?<=operator)(?:[-*&<>=+!]+|\\(\\)|\\[\\]))\\s*\\(\n)", + "begin": "(?x)\n(?!(?:while|for|do|if|else|switch|catch|enumerate|return|typeid|alignof|alignas|sizeof|[cr]?iterate|and|and_eq|bitand|bitor|compl|not|not_eq|or|or_eq|typeid|xor|xor_eq|alignof|alignas)\\s*\\()\n(?=\n(?:[A-Za-z_][A-Za-z0-9_]*+|::)++\\s*\\( # actual name\n|\n(?:(?<=operator)(?:[-*&<>=+!]+|\\(\\)|\\[\\]))\\s*\\(\n)", "end": "(?<=\\))(?!\\w)", "name": "meta.function-call.c", "patterns": [ @@ -488,17 +681,6 @@ } ] }, - "libc": { - "captures": { - "1": { - "name": "punctuation.whitespace.support.function.leading.c" - }, - "2": { - "name": "support.function.C99.c" - } - }, - "match": "(?x) (\\s*) \\b\n(_Exit|(?:nearbyint|nextafter|nexttoward|netoward|nan)[fl]?|a(?:cos|sin)h?[fl]?|abort|abs|asctime|assert\n|atan(?:[h2]?[fl]?)?|atexit|ato[ifl]|atoll|bsearch|btowc|cabs[fl]?|cacos|cacos[fl]|cacosh[fl]?\n|calloc|carg[fl]?|casinh?[fl]?|catanh?[fl]?|cbrt[fl]?|ccosh?[fl]?|ceil[fl]?|cexp[fl]?|cimag[fl]?\n|clearerr|clock|clog[fl]?|conj[fl]?|copysign[fl]?|cosh?[fl]?|cpow[fl]?|cproj[fl]?|creal[fl]?\n|csinh?[fl]?|csqrt[fl]?|ctanh?[fl]?|ctime|difftime|div|erfc?[fl]?|exit|fabs[fl]?\n|exp(?:2[fl]?|[fl]|m1[fl]?)?|fclose|fdim[fl]?|fe[gs]et(?:env|exceptflag|round)|feclearexcept\n|feholdexcept|feof|feraiseexcept|ferror|fetestexcept|feupdateenv|fflush|fgetpos|fgetw?[sc]\n|floor[fl]?|fmax?[fl]?|fmin[fl]?|fmod[fl]?|fopen|fpclassify|fprintf|fputw?[sc]|fread|free|freopen\n|frexp[fl]?|fscanf|fseek|fsetpos|ftell|fwide|fwprintf|fwrite|fwscanf|genv|get[sc]|getchar|gmtime\n|gwc|gwchar|hypot[fl]?|ilogb[fl]?|imaxabs|imaxdiv|isalnum|isalpha|isblank|iscntrl|isdigit|isfinite\n|isgraph|isgreater|isgreaterequal|isinf|isless(?:equal|greater)?|isw?lower|isnan|isnormal|isw?print\n|isw?punct|isw?space|isunordered|isw?upper|iswalnum|iswalpha|iswblank|iswcntrl|iswctype|iswdigit|iswgraph\n|isw?xdigit|labs|ldexp[fl]?|ldiv|lgamma[fl]?|llabs|lldiv|llrint[fl]?|llround[fl]?|localeconv|localtime\n|log[2b]?[fl]?|log1[p0][fl]?|longjmp|lrint[fl]?|lround[fl]?|malloc|mbr?len|mbr?towc|mbsinit|mbsrtowcs\n|mbstowcs|memchr|memcmp|memcpy|memmove|memset|mktime|modf[fl]?|perror|pow[fl]?|printf|puts|putw?c(?:har)?\n|qsort|raise|rand|remainder[fl]?|realloc|remove|remquo[fl]?|rename|rewind|rint[fl]?|round[fl]?|scalbl?n[fl]?\n|scanf|setbuf|setjmp|setlocale|setvbuf|signal|signbit|sinh?[fl]?|snprintf|sprintf|sqrt[fl]?|srand|sscanf\n|strcat|strchr|strcmp|strcoll|strcpy|strcspn|strerror|strftime|strlen|strncat|strncmp|strncpy|strpbrk\n|strrchr|strspn|strstr|strto[kdf]|strtoimax|strtol[dl]?|strtoull?|strtoumax|strxfrm|swprintf|swscanf\n|system|tan|tan[fl]|tanh[fl]?|tgamma[fl]?|time|tmpfile|tmpnam|tolower|toupper|trunc[fl]?|ungetw?c|va_arg\n|va_copy|va_end|va_start|vfw?printf|vfw?scanf|vprintf|vscanf|vsnprintf|vsprintf|vsscanf|vswprintf|vswscanf\n|vwprintf|vwscanf|wcrtomb|wcscat|wcschr|wcscmp|wcscoll|wcscpy|wcscspn|wcsftime|wcslen|wcsncat|wcsncmp|wcsncpy\n|wcspbrk|wcsrchr|wcsrtombs|wcsspn|wcsstr|wcsto[dkf]|wcstoimax|wcstol[dl]?|wcstombs|wcstoull?|wcstoumax|wcsxfrm\n|wctom?b|wmem(?:set|chr|cpy|cmp|move)|wprintf|wscanf)\\b" - }, "line_continuation_character": { "patterns": [ { @@ -520,6 +702,7 @@ ] }, "parens": { + "name": "punctuation.section.parens", "begin": "\\(", "beginCaptures": { "0": { @@ -539,6 +722,7 @@ ] }, "parens-block": { + "name": "punctuation.section.parens.block", "begin": "\\(", "beginCaptures": { "0": { @@ -554,6 +738,10 @@ "patterns": [ { "include": "#block_innards" + }, + { + "match": "(?=+!]+|\\(\\)|\\[\\]))\\s*\\(\n)", + "begin": "(?x)\n(?!(?:while|for|do|if|else|switch|catch|enumerate|return|typeid|alignof|alignas|sizeof|[cr]?iterate|and|and_eq|bitand|bitor|compl|not|not_eq|or|or_eq|typeid|xor|xor_eq|alignof|alignas|asm|__asm__|auto|bool|_Bool|char|_Complex|double|enum|float|_Imaginary|int|long|short|signed|struct|typedef|union|unsigned|void)\\s*\\()\n(?=\n (?:[A-Za-z_][A-Za-z0-9_]*+|::)++\\s*\\( # actual name\n |\n (?:(?<=operator)(?:[-*&<>=+!]+|\\(\\)|\\[\\]))\\s*\\(\n)", "end": "(?<=\\))(?!\\w)|(?=+!]+|\\(\\)|\\[\\]))\n)\n\\s*(\\()", + "begin": "(?x)\n(?!(?:while|for|do|if|else|switch|catch|enumerate|return|typeid|alignof|alignas|sizeof|[cr]?iterate|and|and_eq|bitand|bitor|compl|not|not_eq|or|or_eq|typeid|xor|xor_eq|alignof|alignas)\\s*\\()\n(\n(?:[A-Za-z_][A-Za-z0-9_]*+|::)++ # actual name\n|\n(?:(?<=operator)(?:[-*&<>=+!]+|\\(\\)|\\[\\]))\n)\n\\s*(\\()", "beginCaptures": { "1": { "name": "entity.name.function.c" @@ -1846,7 +2045,8 @@ "include": "#vararg_ellipses" }, { - "begin": "(?x)\n(?!(?:while|for|do|if|else|switch|catch|enumerate|return|typeid|alignof|alignas|sizeof|[cr]?iterate)\\s*\\()\n(\n(?:[A-Za-z_][A-Za-z0-9_]*+|::)++ # actual name\n|\n(?:(?<=operator)(?:[-*&<>=+!]+|\\(\\)|\\[\\]))\n)\n\\s*(\\()", + "name": "meta.function.definition.parameters", + "begin": "(?x)\n(?!(?:while|for|do|if|else|switch|catch|enumerate|return|typeid|alignof|alignas|sizeof|[cr]?iterate|and|and_eq|bitand|bitor|compl|not|not_eq|or|or_eq|typeid|xor|xor_eq|alignof|alignas)\\s*\\()\n(\n(?:[A-Za-z_][A-Za-z0-9_]*+|::)++ # actual name\n|\n(?:(?<=operator)(?:[-*&<>=+!]+|\\(\\)|\\[\\]))\n)\n\\s*(\\()", "beginCaptures": { "1": { "name": "entity.name.function.c" @@ -1862,6 +2062,9 @@ } }, "patterns": [ + { + "include": "#probably_a_parameter" + }, { "include": "#function-innards" } @@ -1900,13 +2103,16 @@ "include": "#storage_types" }, { - "include": "#access" + "include": "#access-method" + }, + { + "include": "#access-member" }, { "include": "#operators" }, { - "begin": "(?x)\n(?!(?:while|for|do|if|else|switch|catch|enumerate|return|typeid|alignof|alignas|sizeof|[cr]?iterate)\\s*\\()\n(\n(?:[A-Za-z_][A-Za-z0-9_]*+|::)++ # actual name\n|\n(?:(?<=operator)(?:[-*&<>=+!]+|\\(\\)|\\[\\]))\n)\n\\s*(\\()", + "begin": "(?x)\n(?!(?:while|for|do|if|else|switch|catch|enumerate|return|typeid|alignof|alignas|sizeof|[cr]?iterate|and|and_eq|bitand|bitor|compl|not|not_eq|or|or_eq|typeid|xor|xor_eq|alignof|alignas)\\s*\\()\n(\n(?:[A-Za-z_][A-Za-z0-9_]*+|::)++ # actual name\n|\n(?:(?<=operator)(?:[-*&<>=+!]+|\\(\\)|\\[\\]))\n)\n\\s*(\\()", "beginCaptures": { "1": { "name": "entity.name.function.c" diff --git a/extensions/cpp/syntaxes/cpp.tmLanguage.json b/extensions/cpp/syntaxes/cpp.tmLanguage.json index f29bc255ba8..c3c6d813d97 100644 --- a/extensions/cpp/syntaxes/cpp.tmLanguage.json +++ b/extensions/cpp/syntaxes/cpp.tmLanguage.json @@ -1,46 +1,83 @@ { "information_for_contributors": [ - "This file has been converted from https://github.com/atom/language-c/blob/master/grammars/c%2B%2B.cson", + "This file has been converted from https://github.com/jeff-hykin/cpp-textmate-grammar/blob/master//syntaxes/cpp.tmLanguage.json", "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/atom/language-c/commit/3a269f88b12e512fb9495dc006a1dabf325d3d7f", + "version": "https://github.com/jeff-hykin/cpp-textmate-grammar/commit/d57808aa3db2242f1f2be1aec19649a852aaa52e", "name": "C++", "scopeName": "source.cpp", "patterns": [ { "include": "#special_block" }, + { + "match": "(?-mix:##[a-zA-Z_]\\w*(?!\\w))", + "name": "variable.other.macro.argument.cpp" + }, { "include": "#strings" }, { - "match": "\\b(friend|explicit|virtual|override|final|noexcept)\\b", - "name": "storage.modifier.cpp" + "match": "(?[a-zA-Z_$][\\w$]*))\t # macro name\n(?:\n (\\()\n\t(\n\t \\s* \\g \\s*\t\t # first argument\n\t ((,) \\s* \\g \\s*)* # additional arguments\n\t (?:\\.\\.\\.)?\t\t\t# varargs ellipsis?\n\t)\n (\\))\n)?", + "beginCaptures": { + "1": { + "name": "keyword.control.directive.define.cpp" + }, + "2": { + "name": "punctuation.definition.directive.cpp" + }, + "3": { + "name": "entity.name.function.preprocessor.cpp" + }, + "5": { + "name": "punctuation.definition.parameters.begin.cpp" + }, + "6": { + "name": "variable.parameter.preprocessor.cpp" + }, + "8": { + "name": "punctuation.separator.parameters.cpp" + }, + "9": { + "name": "punctuation.definition.parameters.end.cpp" + } + }, + "end": "(?=(?://|/\\*))|(?", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.cpp" + } + }, + "name": "string.quoted.other.lt-gt.include.cpp" + } + ] + }, + { + "include": "#pragma-mark" + }, + { + "begin": "^\\s*((#)\\s*line)\\b", + "beginCaptures": { + "1": { + "name": "keyword.control.directive.line.cpp" + }, + "2": { + "name": "punctuation.definition.directive.cpp" + } + }, + "end": "(?=(?://|/\\*))|(?,\\w])*>\\s*", + "captures": { + "0": { + "patterns": [ + { + "include": "#storage_types-c" + }, + { + "include": "#constants" + }, + { + "include": "#scope_resolution" + }, + { + "match": "(?,\\w])*>\\s*)?::)*)\\s*([a-zA-Z_]\\w*)\\s*(?:(<(?:[\\s<>,\\w])*>\\s*))?(::)", + "captures": { + "1": { + "name": "entity.scope.cpp", + "patterns": [ + { + "include": "#scope_resolution" + } + ] + }, + "2": { + "name": "entity.scope.name.cpp" + }, + "3": { + "patterns": [ + { + "include": "#template-call-innards" + } + ] + }, + "4": { + "name": "punctuation.separator.namespace.access.cpp" + } + } + }, + "template_definition": { + "begin": "(?-mix:(?", + "endCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.end.template.definition.cpp" + } + }, + "name": "template.definition.cpp", + "patterns": [ + { + "include": "#scope_resolution" + }, + { + "include": "#template_definition_argument" + }, + { + "include": "#template-call-innards" + } + ] + }, + "template_definition_argument": { + "match": "\\s*(?:(?:(?:([a-zA-Z_]\\w*)|((?:[a-zA-Z_]\\w*\\s+)+)([a-zA-Z_]\\w*))|([a-zA-Z_]\\w*)\\s*(\\.\\.\\.)\\s*([a-zA-Z_]\\w*))|((?:[a-zA-Z_][a-zA-Z_0-9]*\\s+)*)([a-zA-Z_][a-zA-Z_0-9]*)\\s*(=)\\s*(\\w+))\\s*(?:(,)|(?=>))", + "captures": { + "1": { + "name": "storage.type.template.argument.$1.cpp" + }, + "2": { + "name": "storage.type.template.argument.$2.cpp" + }, + "3": { + "name": "entity.name.type.template.cpp" + }, + "4": { + "name": "storage.type.template.cpp" + }, + "5": { + "name": "keyword.operator.ellipsis.template.definition.cpp" + }, + "6": { + "name": "entity.name.type.template.cpp" + }, + "7": { + "name": "storage.type.template.cpp" + }, + "8": { + "name": "entity.name.type.template.cpp" + }, + "9": { + "name": "keyword.operator.assignment.cpp" + }, + "10": { + "name": "keyword.operator.assignment.cpp" + }, + "11": { + "name": "storage.type.template.argument.$10.cpp" + }, + "12": { + "name": "constant.language.cpp" + }, + "13": { + "name": "punctuation.separator.comma.template.argument.cpp" + } + } + }, "angle_brackets": { "begin": "<", "end": ">", @@ -134,13 +638,13 @@ "begin": "\\{", "beginCaptures": { "0": { - "name": "punctuation.section.block.begin.bracket.curly.c" + "name": "punctuation.section.block.begin.bracket.curly.cpp" } }, "end": "\\}", "endCaptures": { "0": { - "name": "punctuation.section.block.end.bracket.curly.c" + "name": "punctuation.section.block.end.bracket.curly.cpp" } }, "name": "meta.block.cpp", @@ -148,14 +652,14 @@ { "captures": { "1": { - "name": "support.function.any-method.c" + "name": "support.function.any-method.cpp" }, "2": { - "name": "punctuation.definition.parameters.c" + "name": "punctuation.definition.parameters.cpp" } }, - "match": "(?x)\n(\n (?!while|for|do|if|else|switch|catch|enumerate|return|r?iterate)\n (?:\\b[A-Za-z_][A-Za-z0-9_]*+\\b|::)*+ # actual name\n)\n\\s*(\\() # opening bracket", - "name": "meta.function-call.c" + "match": "(?x)\n(\n (?!while|for|do|if|else|switch|catch|return)\n (?:\\b[A-Za-z_][A-Za-z0-9_]*+\\b|::)*+ # actual name\n)\n\\s*(\\() # opening bracket", + "name": "meta.function-call.cpp" }, { "include": "$base" @@ -165,25 +669,28 @@ "constructor": { "patterns": [ { - "begin": "(?x)\n(?:^\\s*) # beginning of line\n((?!while|for|do|if|else|switch|catch|enumerate|r?iterate)[A-Za-z_][A-Za-z0-9_:]*) # actual name\n\\s*(\\() # opening bracket", + "begin": "(?x)\n(?:^\\s*) # beginning of line\n((?!while|for|do|if|else|switch|catch)[A-Za-z_][A-Za-z0-9_:]*) # actual name\n\\s*(\\() # opening bracket", "beginCaptures": { "1": { - "name": "entity.name.function.cpp" + "name": "entity.name.function.constructor.cpp" }, "2": { - "name": "punctuation.definition.parameters.begin.c" + "name": "punctuation.definition.parameters.begin.constructor.cpp" } }, "end": "\\)", "endCaptures": { "0": { - "name": "punctuation.definition.parameters.end.c" + "name": "punctuation.definition.parameters.end.constructor.cpp" } }, "name": "meta.function.constructor.cpp", "patterns": [ { - "include": "$base" + "include": "#probably_a_parameter" + }, + { + "include": "#function-innards-c" } ] }, @@ -191,7 +698,7 @@ "begin": "(?x)\n(:)\n(\n (?=\n \\s*[A-Za-z_][A-Za-z0-9_:]* # actual name\n \\s* (\\() # opening bracket\n )\n)", "beginCaptures": { "1": { - "name": "punctuation.definition.parameters.c" + "name": "punctuation.definition.initializer-list.parameters.cpp" } }, "end": "(?=\\{)", @@ -207,34 +714,51 @@ "special_block": { "patterns": [ { - "begin": "\\b(using)\\b\\s*(namespace)\\b\\s*((?:[_A-Za-z][_A-Za-z0-9]*\\b(::)?)*)", + "comment": "https://en.cppreference.com/w/cpp/language/namespace", + "begin": "\\b(using)\\s+(namespace)\\s+(?:((?:[a-zA-Z_]\\w*\\s*(?:<(?:[\\s<>,\\w])*>\\s*)?::)*)\\s*)?((?,\\w])*>\\s*)?::)*[a-zA-Z_]\\w*)|(?={))", "beginCaptures": { "1": { - "name": "storage.type.cpp" + "name": "keyword.other.namespace.definition.cpp" }, "2": { - "name": "entity.name.type.cpp" - } - }, - "captures": { - "1": { - "name": "keyword.control.namespace.$2" + "patterns": [ + { + "match": "(?-mix:(?|\\[|\\]|=))", @@ -271,25 +795,28 @@ ] }, { - "begin": "\\b(class|struct)\\b\\s*([_A-Za-z][_A-Za-z0-9]*\\b)?+(\\s*:\\s*(public|protected|private)\\s*([_A-Za-z][_A-Za-z0-9]*\\b)((\\s*,\\s*(public|protected|private)\\s*[_A-Za-z][_A-Za-z0-9]*\\b)*))?", + "begin": "\\b(?:(class)|(struct))\\b\\s*([_A-Za-z][_A-Za-z0-9]*\\b)?+(\\s*:\\s*(public|protected|private)\\s*([_A-Za-z][_A-Za-z0-9]*\\b)((\\s*,\\s*(public|protected|private)\\s*[_A-Za-z][_A-Za-z0-9]*\\b)*))?", "beginCaptures": { "1": { - "name": "storage.type.cpp" + "name": "storage.type.class.cpp" }, "2": { + "name": "storage.type.struct.cpp" + }, + "3": { "name": "entity.name.type.cpp" }, - "4": { - "name": "storage.type.modifier.cpp" - }, "5": { - "name": "entity.name.type.inherited.cpp" + "name": "storage.type.modifier.access.cpp" }, "6": { + "name": "entity.name.type.inherited.cpp" + }, + "7": { "patterns": [ { "match": "(public|protected|private)", - "name": "storage.type.modifier.cpp" + "name": "storage.type.modifier.access.cpp" }, { "match": "[_A-Za-z][_A-Za-z0-9]*", @@ -298,7 +825,12 @@ ] } }, - "end": "(?<=\\})|(?=(;|\\(|\\)|>|\\[|\\]|=))", + "end": "(?<=\\})|(;)|(?=(\\(|\\)|>|\\[|\\]|=))", + "endCaptures": { + "1": { + "name": "punctuation.terminator.statement.cpp" + } + }, "name": "meta.class-struct-block.cpp", "patterns": [ { @@ -351,13 +883,13 @@ "begin": "\\{", "beginCaptures": { "0": { - "name": "punctuation.section.block.begin.bracket.curly.c" + "name": "punctuation.section.block.begin.bracket.curly.cpp" } }, "end": "\\}|(?=\\s*#\\s*endif\\b)", "endCaptures": { "0": { - "name": "punctuation.section.block.end.bracket.curly.c" + "name": "punctuation.section.block.end.bracket.curly.cpp" } }, "patterns": [ @@ -413,7 +945,7 @@ "name": "constant.character.escape.cpp" }, { - "include": "source.c#string_placeholder" + "include": "#string_placeholder-c" } ] }, @@ -442,6 +974,1858 @@ "name": "string.quoted.double.raw.cpp" } ] + }, + "probably_a_parameter": { + "match": "(?:([a-zA-Z_]\\w*)\\s*(?==)|(?<=(?:\\w\\s|[&*>\\]\\)]))\\s*([a-zA-Z_]\\w*)\\s*(?=(?:\\[\\]\\s*)?(?:,|\\))))", + "captures": { + "1": { + "name": "variable.parameter.probably.defaulted.cpp" + }, + "2": { + "name": "variable.parameter.probably.cpp" + } + } + }, + "operator_overload": { + "begin": "((?:[a-zA-Z_]\\w*\\s*(?:<(?:[\\s<>,\\w])*>\\s*)?::)*)\\s*(operator)((?:\\s*(?:\\+\\+|\\-\\-|\\(\\)|\\[\\]|\\->|\\+\\+|\\-\\-|\\+|\\-|!|~|\\*|&|\\->\\*|\\*|\\/|%|\\+|\\-|<<|>>|<=>|<|<=|>|>=|==|!=|&|\\^|\\||&&|\\|\\||=|\\+=|\\-=|\\*=|\\/=|%=|<<=|>>=|&=|\\^=|\\|=|,)|\\s+(?:(?:new|new\\[\\]|delete|delete\\[\\])|(?:[a-zA-Z_]\\w*\\s*(?:<(?:[\\s<>,\\w])*>\\s*)?::)*[a-zA-Z_]\\w*\\s*(?:&)?)))\\s*(\\()", + "beginCaptures": { + "1": { + "name": "entity.scope.cpp" + }, + "2": { + "name": "entity.name.operator.overload.cpp" + }, + "3": { + "name": "entity.name.operator.overloadee.cpp" + }, + "4": { + "name": "punctuation.section.parameters.begin.bracket.round.cpp" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.section.parameters.end.bracket.round.cpp" + } + }, + "name": "meta.function.definition.parameters.operator-overload.cpp", + "patterns": [ + { + "include": "#probably_a_parameter" + }, + { + "include": "#function-innards-c" + } + ] + }, + "access-method": { + "name": "meta.function-call.member.cpp", + "begin": "([a-zA-Z_][a-zA-Z_0-9]*|(?<=[\\]\\)]))\\s*(?:(\\.)|(->))((?:(?:[a-zA-Z_][a-zA-Z_0-9]*)\\s*(?:(?:\\.)|(?:->)))*)\\s*([a-zA-Z_][a-zA-Z_0-9]*)(\\()", + "beginCaptures": { + "1": { + "name": "variable.object.cpp" + }, + "2": { + "name": "punctuation.separator.dot-access.cpp" + }, + "3": { + "name": "punctuation.separator.pointer-access.cpp" + }, + "4": { + "patterns": [ + { + "match": "\\.", + "name": "punctuation.separator.dot-access.cpp" + }, + { + "match": "->", + "name": "punctuation.separator.pointer-access.cpp" + }, + { + "match": "[a-zA-Z_][a-zA-Z_0-9]*", + "name": "variable.object.cpp" + }, + { + "name": "everything.else.cpp", + "match": ".+" + } + ] + }, + "5": { + "name": "entity.name.function.member.cpp" + }, + "6": { + "name": "punctuation.section.arguments.begin.bracket.round.function.member.cpp" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.section.arguments.end.bracket.round.function.member.cpp" + } + }, + "patterns": [ + { + "include": "#function-call-innards-c" + } + ] + }, + "access-member": { + "name": "variable.object.access.cpp", + "match": "(?:([a-zA-Z_]\\w*)|(?<=\\]|\\)))\\s*(?:((?:\\.|\\.\\*))|((?:->|->\\*)))\\s*((?:[a-zA-Z_]\\w*\\s*(?:\\.|->)\\s*)*)\\b(?!(?:auto|void|char|short|int|signed|unsigned|long|float|double|bool|wchar_t|u_char|u_short|u_int|u_long|ushort|uint|u_quad_t|quad_t|qaddr_t|caddr_t|daddr_t|div_t|dev_t|fixpt_t|blkcnt_t|blksize_t|gid_t|in_addr_t|in_port_t|ino_t|key_t|mode_t|nlink_t|id_t|pid_t|off_t|segsz_t|swblk_t|uid_t|id_t|clock_t|size_t|ssize_t|time_t|useconds_t|suseconds_t|pthread_attr_t|pthread_cond_t|pthread_condattr_t|pthread_mutex_t|pthread_mutexattr_t|pthread_once_t|pthread_rwlock_t|pthread_rwlockattr_t|pthread_t|pthread_key_t|int8_t|int16_t|int32_t|int64_t|uint8_t|uint16_t|uint32_t|uint64_t|int_least8_t|int_least16_t|int_least32_t|int_least64_t|uint_least8_t|uint_least16_t|uint_least32_t|uint_least64_t|int_fast8_t|int_fast16_t|int_fast32_t|int_fast64_t|uint_fast8_t|uint_fast16_t|uint_fast32_t|uint_fast64_t|intptr_t|uintptr_t|intmax_t|intmax_t|uintmax_t|uintmax_t))([a-zA-Z_]\\w*)\\b(?!\\()", + "captures": { + "1": { + "name": "variable.object.cpp" + }, + "2": { + "name": "punctuation.separator.dot-access.cpp" + }, + "3": { + "name": "punctuation.separator.pointer-access.cpp" + }, + "4": { + "patterns": [ + { + "match": "\\.", + "name": "punctuation.separator.dot-access.cpp" + }, + { + "match": "->", + "name": "punctuation.separator.pointer-access.cpp" + }, + { + "match": "[a-zA-Z_]\\w*", + "name": "variable.object.cpp" + }, + { + "match": ".+", + "name": "everything.else.cpp" + } + ] + }, + "5": { + "name": "variable.other.member.cpp" + } + } + }, + "block-c": { + "patterns": [ + { + "begin": "{", + "beginCaptures": { + "0": { + "name": "punctuation.section.block.begin.bracket.curly.cpp" + } + }, + "end": "}|(?=\\s*#\\s*(?:elif|else|endif)\\b)", + "endCaptures": { + "0": { + "name": "punctuation.section.block.end.bracket.curly.cpp" + } + }, + "name": "meta.block.cpp", + "patterns": [ + { + "include": "#block_innards-c" + } + ] + } + ] + }, + "block_innards-c": { + "patterns": [ + { + "include": "#preprocessor-rule-enabled-block" + }, + { + "include": "#preprocessor-rule-disabled-block" + }, + { + "include": "#preprocessor-rule-conditional-block" + }, + { + "include": "#access-method" + }, + { + "include": "#access-member" + }, + { + "include": "#c_function_call" + }, + { + "name": "meta.initialization.cpp", + "begin": "(?x)\n(?:\n (?:\n\t(?=\\s)(?=+!]+ | \\(\\) | \\[\\]))\n)\n\\s*(\\() # opening bracket", + "beginCaptures": { + "1": { + "name": "variable.other.cpp" + }, + "2": { + "name": "punctuation.section.parens.begin.bracket.round.initialization.cpp" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.section.parens.end.bracket.round.initialization.cpp" + } + }, + "patterns": [ + { + "include": "#function-call-innards-c" + } + ] + }, + { + "begin": "{", + "beginCaptures": { + "0": { + "name": "punctuation.section.block.begin.bracket.curly.cpp" + } + }, + "end": "}|(?=\\s*#\\s*(?:elif|else|endif)\\b)", + "endCaptures": { + "0": { + "name": "punctuation.section.block.end.bracket.curly.cpp" + } + }, + "patterns": [ + { + "include": "#block_innards-c" + } + ] + }, + { + "include": "#parens-block-c" + }, + { + "include": "$base" + } + ] + }, + "c_function_call": { + "begin": "(?x)\n(?!(?:while|for|do|if|else|switch|catch|return|typeid|alignof|alignas|sizeof|and|and_eq|bitand|bitor|compl|not|not_eq|or|or_eq|typeid|xor|xor_eq|alignof|alignas)\\s*\\()\n(?=\n(?:[A-Za-z_][A-Za-z0-9_]*+|::)++\\s*(?:<(?:[\\s<>,\\w])*>\\s*)?\\( # actual name\n|\n(?:(?<=operator)(?:[-*&<>=+!]+|\\(\\)|\\[\\]))\\s*\\(\n)", + "end": "(?<=\\))(?!\\w)", + "name": "meta.function-call.cpp", + "patterns": [ + { + "include": "#function-call-innards-c" + } + ] + }, + "comments-c": { + "patterns": [ + { + "captures": { + "1": { + "name": "meta.toc-list.banner.block.cpp" + } + }, + "match": "^/\\* =(\\s*.*?)\\s*= \\*/$\\n?", + "name": "comment.block.cpp" + }, + { + "begin": "/\\*", + "beginCaptures": { + "0": { + "name": "punctuation.definition.comment.begin.cpp" + } + }, + "end": "\\*/", + "endCaptures": { + "0": { + "name": "punctuation.definition.comment.end.cpp" + } + }, + "name": "comment.block.cpp" + }, + { + "match": "\\*/.*\\n", + "name": "invalid.illegal.stray-comment-end.cpp" + }, + { + "captures": { + "1": { + "name": "meta.toc-list.banner.line.cpp" + } + }, + "match": "^// =(\\s*.*?)\\s*=\\s*$\\n?", + "name": "comment.line.banner.cpp" + }, + { + "begin": "(^[ \\t]+)?(?=//)", + "beginCaptures": { + "1": { + "name": "punctuation.whitespace.comment.leading.cpp" + } + }, + "end": "(?!\\G)", + "patterns": [ + { + "begin": "//", + "beginCaptures": { + "0": { + "name": "punctuation.definition.comment.cpp" + } + }, + "end": "(?=\\n)", + "name": "comment.line.double-slash.cpp", + "patterns": [ + { + "include": "#line_continuation_character" + } + ] + } + ] + } + ] + }, + "disabled": { + "begin": "^\\s*#\\s*if(n?def)?\\b.*$", + "end": "^\\s*#\\s*endif\\b", + "patterns": [ + { + "include": "#disabled" + }, + { + "include": "#pragma-mark" + } + ] + }, + "line_continuation_character": { + "patterns": [ + { + "match": "(\\\\)\\n", + "captures": { + "1": { + "name": "constant.character.escape.line-continuation.cpp" + } + } + } + ] + }, + "numbers-c": { + "patterns": [ + { + "match": "\\b((?:0(?:x|X)[0-9a-fA-F](?:[0-9a-fA-F']*[0-9a-fA-F'])?|0(?:b|B)[01](?:[01']*[01'])?)(?:\\.[\\d+a-fA-F']+p[\\d']+)?|(([0-9](?:[0-9']*[0-9'])*(?:\\.[0-9](?:[0-9']*[0-9'])*)?)|(\\.[0-9](?:[0-9']*[0-9'])*))((e|E)(\\+|-)?[0-9](?:[0-9']*[0-9'])*)?)(?:L|l|UL|ul|u|U|F|f|ll|LL|ull|ULL)?\\w*", + "name": "constant.numeric.cpp", + "captures": { + "0": { + "patterns": [ + { + "match": "(?-mix:(?>=|\\|=", + "name": "keyword.operator.assignment.compound.bitwise.cpp" + }, + { + "match": "<<|>>", + "name": "keyword.operator.bitwise.shift.cpp" + }, + { + "match": "!=|<=|>=|==|<|>", + "name": "keyword.operator.comparison.cpp" + }, + { + "match": "&&|!|\\|\\|", + "name": "keyword.operator.logical.cpp" + }, + { + "match": "&|\\||\\^|~", + "name": "keyword.operator.cpp" + }, + { + "match": "=", + "name": "keyword.operator.assignment.cpp" + }, + { + "match": "%|\\*|/|-|\\+", + "name": "keyword.operator.cpp" + }, + { + "begin": "\\?", + "beginCaptures": { + "0": { + "name": "keyword.operator.ternary.cpp" + } + }, + "end": ":", + "applyEndPatternLast": true, + "endCaptures": { + "0": { + "name": "keyword.operator.ternary.cpp" + } + }, + "patterns": [ + { + "include": "#access-method" + }, + { + "include": "#access-member" + }, + { + "include": "#c_function_call" + }, + { + "include": "$base" + } + ] + } + ] + }, + "strings-c": { + "patterns": [ + { + "begin": "\"", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.cpp" + } + }, + "end": "\"", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.cpp" + } + }, + "name": "string.quoted.double.cpp", + "patterns": [ + { + "include": "#string_escaped_char-c" + }, + { + "include": "#string_placeholder-c" + }, + { + "include": "#line_continuation_character" + } + ] + }, + { + "begin": "(?-mix:(?=+!]+|\\(\\)|\\[\\]))\\s*\\(\n)", + "end": "(?<=\\))(?!\\w)|(?=+!]+|\\(\\)|\\[\\]))\n)\n\\s*(\\()", + "beginCaptures": { + "1": { + "name": "entity.name.function.cpp" + }, + "2": { + "name": "punctuation.section.arguments.begin.bracket.round.cpp" + } + }, + "end": "(\\))|(?=+!]+|\\(\\)|\\[\\]))\n)\n\\s*(\\()", + "beginCaptures": { + "1": { + "name": "entity.name.function.cpp" + }, + "2": { + "name": "punctuation.section.parameters.begin.bracket.round.cpp" + } + }, + "end": "\\)|:", + "endCaptures": { + "0": { + "name": "punctuation.section.parameters.end.bracket.round.cpp" + } + }, + "patterns": [ + { + "include": "#probably_a_parameter" + }, + { + "include": "#function-innards-c" + } + ] + }, + { + "begin": "\\(", + "beginCaptures": { + "0": { + "name": "punctuation.section.parens.begin.bracket.round.cpp" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.section.parens.end.bracket.round.cpp" + } + }, + "patterns": [ + { + "include": "#function-innards-c" + } + ] + }, + { + "include": "$base" + } + ] + }, + "function-call-innards-c": { + "patterns": [ + { + "include": "#comments-c" + }, + { + "include": "#storage_types-c" + }, + { + "include": "#access-method" + }, + { + "include": "#access-member" + }, + { + "include": "#operators" + }, + { + "begin": "(?x)\n(?!(?:while|for|do|if|else|switch|catch|return|typeid|alignof|alignas|sizeof|and|and_eq|bitand|bitor|compl|not|not_eq|or|or_eq|typeid|xor|xor_eq|alignof|alignas)\\s*\\()\n(\n(?:new)\\s*((?:<(?:[\\s<>,\\w])*>\\s*)?) # actual name\n|\n(?:(?<=operator)(?:[-*&<>=+!]+|\\(\\)|\\[\\]))\n)\n\\s*(\\()", + "beginCaptures": { + "1": { + "name": "keyword.operator.memory.new.cpp" + }, + "2": { + "patterns": [ + { + "include": "#template-call-innards" + } + ] + }, + "3": { + "name": "punctuation.section.arguments.begin.bracket.round.cpp" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.section.arguments.end.bracket.round.cpp" + } + }, + "patterns": [ + { + "include": "#function-call-innards-c" + } + ] + }, + { + "begin": "(?,\\w])*>\\s*)?::)*)\\s*([a-zA-Z_]\\w*)\\s*(?:(<(?:[\\s<>,\\w])*>\\s*))?(\\()", + "beginCaptures": { + "1": { + "patterns": [ + { + "include": "#scope_resolution" + } + ] + }, + "2": { + "name": "entity.name.function.call.cpp" + }, + "3": { + "patterns": [ + { + "include": "#template-call-innards" + } + ] + }, + "4": { + "name": "punctuation.section.arguments.begin.bracket.round.cpp" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.section.arguments.end.bracket.round.cpp" + } + }, + "patterns": [ + { + "include": "#function-call-innards-c" + } + ] + }, + { + "begin": "\\(", + "beginCaptures": { + "0": { + "name": "punctuation.section.parens.begin.bracket.round.cpp" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.section.parens.end.bracket.round.cpp" + } + }, + "patterns": [ + { + "include": "#function-call-innards-c" + } + ] + }, + { + "include": "#block_innards-c" + } + ] } } } \ No newline at end of file diff --git a/extensions/cpp/test/colorize-results/test-23630_cpp.json b/extensions/cpp/test/colorize-results/test-23630_cpp.json index f22786a105f..a58961ae945 100644 --- a/extensions/cpp/test/colorize-results/test-23630_cpp.json +++ b/extensions/cpp/test/colorize-results/test-23630_cpp.json @@ -1,7 +1,7 @@ [ { "c": "#", - "t": "source.cpp meta.preprocessor.c keyword.control.directive.conditional.c punctuation.definition.directive.c", + "t": "source.cpp meta.preprocessor.cpp keyword.control.directive.conditional.cpp punctuation.definition.directive.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -12,7 +12,7 @@ }, { "c": "ifndef", - "t": "source.cpp meta.preprocessor.c keyword.control.directive.conditional.c", + "t": "source.cpp meta.preprocessor.cpp keyword.control.directive.conditional.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -23,7 +23,7 @@ }, { "c": " ", - "t": "source.cpp meta.preprocessor.c", + "t": "source.cpp meta.preprocessor.cpp", "r": { "dark_plus": "meta.preprocessor: #569CD6", "light_plus": "meta.preprocessor: #0000FF", @@ -34,7 +34,7 @@ }, { "c": "_UCRT", - "t": "source.cpp meta.preprocessor.c entity.name.function.preprocessor.c", + "t": "source.cpp meta.preprocessor.cpp entity.name.function.preprocessor.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -45,7 +45,7 @@ }, { "c": " ", - "t": "source.cpp meta.preprocessor.macro.c", + "t": "source.cpp meta.preprocessor.macro.cpp", "r": { "dark_plus": "meta.preprocessor: #569CD6", "light_plus": "meta.preprocessor: #0000FF", @@ -56,7 +56,7 @@ }, { "c": "#", - "t": "source.cpp meta.preprocessor.macro.c keyword.control.directive.define.c punctuation.definition.directive.c", + "t": "source.cpp meta.preprocessor.macro.cpp keyword.control.directive.define.cpp punctuation.definition.directive.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -67,7 +67,7 @@ }, { "c": "define", - "t": "source.cpp meta.preprocessor.macro.c keyword.control.directive.define.c", + "t": "source.cpp meta.preprocessor.macro.cpp keyword.control.directive.define.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -78,7 +78,7 @@ }, { "c": " ", - "t": "source.cpp meta.preprocessor.macro.c", + "t": "source.cpp meta.preprocessor.macro.cpp", "r": { "dark_plus": "meta.preprocessor: #569CD6", "light_plus": "meta.preprocessor: #0000FF", @@ -89,7 +89,7 @@ }, { "c": "_UCRT", - "t": "source.cpp meta.preprocessor.macro.c entity.name.function.preprocessor.c", + "t": "source.cpp meta.preprocessor.macro.cpp entity.name.function.preprocessor.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -100,7 +100,7 @@ }, { "c": "#", - "t": "source.cpp meta.preprocessor.c keyword.control.directive.conditional.c punctuation.definition.directive.c", + "t": "source.cpp meta.preprocessor.cpp keyword.control.directive.conditional.cpp punctuation.definition.directive.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -111,7 +111,7 @@ }, { "c": "endif", - "t": "source.cpp meta.preprocessor.c keyword.control.directive.conditional.c", + "t": "source.cpp meta.preprocessor.cpp keyword.control.directive.conditional.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", diff --git a/extensions/cpp/test/colorize-results/test-23850_cpp.json b/extensions/cpp/test/colorize-results/test-23850_cpp.json index bbb5237498f..924bbc78243 100644 --- a/extensions/cpp/test/colorize-results/test-23850_cpp.json +++ b/extensions/cpp/test/colorize-results/test-23850_cpp.json @@ -1,7 +1,7 @@ [ { "c": "#", - "t": "source.cpp meta.preprocessor.c keyword.control.directive.conditional.c punctuation.definition.directive.c", + "t": "source.cpp meta.preprocessor.cpp keyword.control.directive.conditional.cpp punctuation.definition.directive.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -12,7 +12,7 @@ }, { "c": "ifndef", - "t": "source.cpp meta.preprocessor.c keyword.control.directive.conditional.c", + "t": "source.cpp meta.preprocessor.cpp keyword.control.directive.conditional.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -23,7 +23,7 @@ }, { "c": " ", - "t": "source.cpp meta.preprocessor.c", + "t": "source.cpp meta.preprocessor.cpp", "r": { "dark_plus": "meta.preprocessor: #569CD6", "light_plus": "meta.preprocessor: #0000FF", @@ -34,7 +34,7 @@ }, { "c": "_UCRT", - "t": "source.cpp meta.preprocessor.c entity.name.function.preprocessor.c", + "t": "source.cpp meta.preprocessor.cpp entity.name.function.preprocessor.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -45,7 +45,7 @@ }, { "c": "#", - "t": "source.cpp meta.preprocessor.macro.c keyword.control.directive.define.c punctuation.definition.directive.c", + "t": "source.cpp meta.preprocessor.macro.cpp keyword.control.directive.define.cpp punctuation.definition.directive.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -56,7 +56,7 @@ }, { "c": "define", - "t": "source.cpp meta.preprocessor.macro.c keyword.control.directive.define.c", + "t": "source.cpp meta.preprocessor.macro.cpp keyword.control.directive.define.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -67,7 +67,7 @@ }, { "c": " ", - "t": "source.cpp meta.preprocessor.macro.c", + "t": "source.cpp meta.preprocessor.macro.cpp", "r": { "dark_plus": "meta.preprocessor: #569CD6", "light_plus": "meta.preprocessor: #0000FF", @@ -78,7 +78,7 @@ }, { "c": "_UCRT", - "t": "source.cpp meta.preprocessor.macro.c entity.name.function.preprocessor.c", + "t": "source.cpp meta.preprocessor.macro.cpp entity.name.function.preprocessor.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -89,7 +89,7 @@ }, { "c": "#", - "t": "source.cpp meta.preprocessor.c keyword.control.directive.conditional.c punctuation.definition.directive.c", + "t": "source.cpp meta.preprocessor.cpp keyword.control.directive.conditional.cpp punctuation.definition.directive.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -100,7 +100,7 @@ }, { "c": "endif", - "t": "source.cpp meta.preprocessor.c keyword.control.directive.conditional.c", + "t": "source.cpp meta.preprocessor.cpp keyword.control.directive.conditional.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", diff --git a/extensions/cpp/test/colorize-results/test_c.json b/extensions/cpp/test/colorize-results/test_c.json index 0725010d8c2..79a3c093e65 100644 --- a/extensions/cpp/test/colorize-results/test_c.json +++ b/extensions/cpp/test/colorize-results/test_c.json @@ -243,7 +243,7 @@ }, { "c": "int", - "t": "source.c storage.type.c", + "t": "source.c storage.type.built-in.primitive.c", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -265,7 +265,7 @@ }, { "c": "main", - "t": "source.c meta.function.c entity.name.function.c", + "t": "source.c meta.function.c meta.function.definition.parameters entity.name.function.c", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -276,7 +276,7 @@ }, { "c": "(", - "t": "source.c meta.function.c punctuation.section.parameters.begin.bracket.round.c", + "t": "source.c meta.function.c meta.function.definition.parameters punctuation.section.parameters.begin.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -287,7 +287,7 @@ }, { "c": ")", - "t": "source.c meta.function.c punctuation.section.parameters.end.bracket.round.c", + "t": "source.c meta.function.c meta.function.definition.parameters punctuation.section.parameters.end.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -320,7 +320,7 @@ }, { "c": "float", - "t": "source.c meta.block.c storage.type.c", + "t": "source.c meta.block.c storage.type.built-in.primitive.c", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -507,7 +507,7 @@ }, { "c": " ", - "t": "source.c meta.block.c punctuation.whitespace.support.function.leading.c", + "t": "source.c meta.block.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -518,18 +518,18 @@ }, { "c": "printf", - "t": "source.c meta.block.c support.function.C99.c", + "t": "source.c meta.block.c meta.function-call.c entity.name.function.c", "r": { - "dark_plus": "support.function: #DCDCAA", - "light_plus": "support.function: #795E26", + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "support.function: #DCDCAA" + "hc_black": "entity.name.function: #DCDCAA" } }, { "c": "(", - "t": "source.c meta.block.c punctuation.section.parens.begin.bracket.round.c", + "t": "source.c meta.block.c meta.function-call.c punctuation.section.arguments.begin.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -540,7 +540,7 @@ }, { "c": "\"", - "t": "source.c meta.block.c string.quoted.double.c punctuation.definition.string.begin.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c punctuation.definition.string.begin.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -551,7 +551,7 @@ }, { "c": "Enter coefficients a, b and c: ", - "t": "source.c meta.block.c string.quoted.double.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -562,7 +562,7 @@ }, { "c": "\"", - "t": "source.c meta.block.c string.quoted.double.c punctuation.definition.string.end.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c punctuation.definition.string.end.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -573,7 +573,7 @@ }, { "c": ")", - "t": "source.c meta.block.c punctuation.section.parens.end.bracket.round.c", + "t": "source.c meta.block.c meta.function-call.c punctuation.section.arguments.end.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -595,7 +595,7 @@ }, { "c": " ", - "t": "source.c meta.block.c punctuation.whitespace.support.function.leading.c", + "t": "source.c meta.block.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -606,18 +606,18 @@ }, { "c": "scanf", - "t": "source.c meta.block.c support.function.C99.c", + "t": "source.c meta.block.c meta.function-call.c entity.name.function.c", "r": { - "dark_plus": "support.function: #DCDCAA", - "light_plus": "support.function: #795E26", + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "support.function: #DCDCAA" + "hc_black": "entity.name.function: #DCDCAA" } }, { "c": "(", - "t": "source.c meta.block.c punctuation.section.parens.begin.bracket.round.c", + "t": "source.c meta.block.c meta.function-call.c punctuation.section.arguments.begin.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -628,7 +628,7 @@ }, { "c": "\"", - "t": "source.c meta.block.c string.quoted.double.c punctuation.definition.string.begin.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c punctuation.definition.string.begin.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -639,7 +639,7 @@ }, { "c": "%f%f%f", - "t": "source.c meta.block.c string.quoted.double.c constant.other.placeholder.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c constant.other.placeholder.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -650,7 +650,7 @@ }, { "c": "\"", - "t": "source.c meta.block.c string.quoted.double.c punctuation.definition.string.end.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c punctuation.definition.string.end.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -661,7 +661,7 @@ }, { "c": ",", - "t": "source.c meta.block.c punctuation.separator.delimiter.c", + "t": "source.c meta.block.c meta.function-call.c punctuation.separator.delimiter.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -672,7 +672,7 @@ }, { "c": "&", - "t": "source.c meta.block.c keyword.operator.c", + "t": "source.c meta.block.c meta.function-call.c keyword.operator.c", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -683,7 +683,7 @@ }, { "c": "a", - "t": "source.c meta.block.c", + "t": "source.c meta.block.c meta.function-call.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -694,7 +694,7 @@ }, { "c": ",", - "t": "source.c meta.block.c punctuation.separator.delimiter.c", + "t": "source.c meta.block.c meta.function-call.c punctuation.separator.delimiter.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -705,7 +705,7 @@ }, { "c": "&", - "t": "source.c meta.block.c keyword.operator.c", + "t": "source.c meta.block.c meta.function-call.c keyword.operator.c", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -716,7 +716,7 @@ }, { "c": "b", - "t": "source.c meta.block.c", + "t": "source.c meta.block.c meta.function-call.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -727,7 +727,7 @@ }, { "c": ",", - "t": "source.c meta.block.c punctuation.separator.delimiter.c", + "t": "source.c meta.block.c meta.function-call.c punctuation.separator.delimiter.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -738,7 +738,7 @@ }, { "c": "&", - "t": "source.c meta.block.c keyword.operator.c", + "t": "source.c meta.block.c meta.function-call.c keyword.operator.c", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -749,7 +749,7 @@ }, { "c": "c", - "t": "source.c meta.block.c", + "t": "source.c meta.block.c meta.function-call.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -760,7 +760,7 @@ }, { "c": ")", - "t": "source.c meta.block.c punctuation.section.parens.end.bracket.round.c", + "t": "source.c meta.block.c meta.function-call.c punctuation.section.arguments.end.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -947,7 +947,7 @@ }, { "c": "(", - "t": "source.c meta.block.c punctuation.section.parens.begin.bracket.round.c", + "t": "source.c meta.block.c punctuation.section.parens.block punctuation.section.parens.begin.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -958,7 +958,7 @@ }, { "c": "determinant", - "t": "source.c meta.block.c", + "t": "source.c meta.block.c punctuation.section.parens.block", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -969,7 +969,7 @@ }, { "c": ">", - "t": "source.c meta.block.c keyword.operator.comparison.c", + "t": "source.c meta.block.c punctuation.section.parens.block keyword.operator.comparison.c", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -980,7 +980,7 @@ }, { "c": "0", - "t": "source.c meta.block.c constant.numeric.c", + "t": "source.c meta.block.c punctuation.section.parens.block constant.numeric.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -991,7 +991,7 @@ }, { "c": ")", - "t": "source.c meta.block.c punctuation.section.parens.end.bracket.round.c", + "t": "source.c meta.block.c punctuation.section.parens.block punctuation.section.parens.end.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1057,7 +1057,7 @@ }, { "c": "(", - "t": "source.c meta.block.c punctuation.section.parens.begin.bracket.round.c", + "t": "source.c meta.block.c punctuation.section.parens.block punctuation.section.parens.begin.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1068,7 +1068,7 @@ }, { "c": "-", - "t": "source.c meta.block.c keyword.operator.c", + "t": "source.c meta.block.c punctuation.section.parens.block keyword.operator.c", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1079,7 +1079,7 @@ }, { "c": "b", - "t": "source.c meta.block.c", + "t": "source.c meta.block.c punctuation.section.parens.block", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1090,7 +1090,7 @@ }, { "c": "+", - "t": "source.c meta.block.c keyword.operator.c", + "t": "source.c meta.block.c punctuation.section.parens.block keyword.operator.c", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1101,18 +1101,18 @@ }, { "c": "sqrt", - "t": "source.c meta.block.c support.function.C99.c", + "t": "source.c meta.block.c punctuation.section.parens.block meta.function-call.c entity.name.function.c", "r": { - "dark_plus": "support.function: #DCDCAA", - "light_plus": "support.function: #795E26", + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "support.function: #DCDCAA" + "hc_black": "entity.name.function: #DCDCAA" } }, { "c": "(", - "t": "source.c meta.block.c punctuation.section.parens.begin.bracket.round.c", + "t": "source.c meta.block.c punctuation.section.parens.block meta.function-call.c punctuation.section.arguments.begin.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1123,7 +1123,7 @@ }, { "c": "determinant", - "t": "source.c meta.block.c", + "t": "source.c meta.block.c punctuation.section.parens.block meta.function-call.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1133,8 +1133,19 @@ } }, { - "c": "))", - "t": "source.c meta.block.c punctuation.section.parens.end.bracket.round.c", + "c": ")", + "t": "source.c meta.block.c punctuation.section.parens.block meta.function-call.c punctuation.section.arguments.end.bracket.round.c", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ")", + "t": "source.c meta.block.c punctuation.section.parens.block punctuation.section.parens.end.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1156,7 +1167,7 @@ }, { "c": "(", - "t": "source.c meta.block.c punctuation.section.parens.begin.bracket.round.c", + "t": "source.c meta.block.c punctuation.section.parens.block punctuation.section.parens.begin.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1167,7 +1178,7 @@ }, { "c": "2", - "t": "source.c meta.block.c constant.numeric.c", + "t": "source.c meta.block.c punctuation.section.parens.block constant.numeric.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -1178,7 +1189,7 @@ }, { "c": "*", - "t": "source.c meta.block.c keyword.operator.c", + "t": "source.c meta.block.c punctuation.section.parens.block keyword.operator.c", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1189,7 +1200,7 @@ }, { "c": "a", - "t": "source.c meta.block.c", + "t": "source.c meta.block.c punctuation.section.parens.block", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1200,7 +1211,7 @@ }, { "c": ")", - "t": "source.c meta.block.c punctuation.section.parens.end.bracket.round.c", + "t": "source.c meta.block.c punctuation.section.parens.block punctuation.section.parens.end.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1255,7 +1266,7 @@ }, { "c": "(", - "t": "source.c meta.block.c punctuation.section.parens.begin.bracket.round.c", + "t": "source.c meta.block.c punctuation.section.parens.block punctuation.section.parens.begin.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1266,7 +1277,7 @@ }, { "c": "-", - "t": "source.c meta.block.c keyword.operator.c", + "t": "source.c meta.block.c punctuation.section.parens.block keyword.operator.c", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1277,7 +1288,7 @@ }, { "c": "b", - "t": "source.c meta.block.c", + "t": "source.c meta.block.c punctuation.section.parens.block", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1288,7 +1299,7 @@ }, { "c": "-", - "t": "source.c meta.block.c keyword.operator.c", + "t": "source.c meta.block.c punctuation.section.parens.block keyword.operator.c", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1299,18 +1310,18 @@ }, { "c": "sqrt", - "t": "source.c meta.block.c support.function.C99.c", + "t": "source.c meta.block.c punctuation.section.parens.block meta.function-call.c entity.name.function.c", "r": { - "dark_plus": "support.function: #DCDCAA", - "light_plus": "support.function: #795E26", + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "support.function: #DCDCAA" + "hc_black": "entity.name.function: #DCDCAA" } }, { "c": "(", - "t": "source.c meta.block.c punctuation.section.parens.begin.bracket.round.c", + "t": "source.c meta.block.c punctuation.section.parens.block meta.function-call.c punctuation.section.arguments.begin.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1321,7 +1332,7 @@ }, { "c": "determinant", - "t": "source.c meta.block.c", + "t": "source.c meta.block.c punctuation.section.parens.block meta.function-call.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1331,8 +1342,19 @@ } }, { - "c": "))", - "t": "source.c meta.block.c punctuation.section.parens.end.bracket.round.c", + "c": ")", + "t": "source.c meta.block.c punctuation.section.parens.block meta.function-call.c punctuation.section.arguments.end.bracket.round.c", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ")", + "t": "source.c meta.block.c punctuation.section.parens.block punctuation.section.parens.end.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1354,7 +1376,7 @@ }, { "c": "(", - "t": "source.c meta.block.c punctuation.section.parens.begin.bracket.round.c", + "t": "source.c meta.block.c punctuation.section.parens.block punctuation.section.parens.begin.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1365,7 +1387,7 @@ }, { "c": "2", - "t": "source.c meta.block.c constant.numeric.c", + "t": "source.c meta.block.c punctuation.section.parens.block constant.numeric.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -1376,7 +1398,7 @@ }, { "c": "*", - "t": "source.c meta.block.c keyword.operator.c", + "t": "source.c meta.block.c punctuation.section.parens.block keyword.operator.c", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1387,7 +1409,7 @@ }, { "c": "a", - "t": "source.c meta.block.c", + "t": "source.c meta.block.c punctuation.section.parens.block", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1398,7 +1420,7 @@ }, { "c": ")", - "t": "source.c meta.block.c punctuation.section.parens.end.bracket.round.c", + "t": "source.c meta.block.c punctuation.section.parens.block punctuation.section.parens.end.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1420,7 +1442,7 @@ }, { "c": " ", - "t": "source.c meta.block.c punctuation.whitespace.support.function.leading.c", + "t": "source.c meta.block.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1431,18 +1453,18 @@ }, { "c": "printf", - "t": "source.c meta.block.c support.function.C99.c", + "t": "source.c meta.block.c meta.function-call.c entity.name.function.c", "r": { - "dark_plus": "support.function: #DCDCAA", - "light_plus": "support.function: #795E26", + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "support.function: #DCDCAA" + "hc_black": "entity.name.function: #DCDCAA" } }, { "c": "(", - "t": "source.c meta.block.c punctuation.section.parens.begin.bracket.round.c", + "t": "source.c meta.block.c meta.function-call.c punctuation.section.arguments.begin.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1453,7 +1475,7 @@ }, { "c": "\"", - "t": "source.c meta.block.c string.quoted.double.c punctuation.definition.string.begin.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c punctuation.definition.string.begin.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1464,7 +1486,7 @@ }, { "c": "Roots are: ", - "t": "source.c meta.block.c string.quoted.double.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1475,7 +1497,7 @@ }, { "c": "%.2f", - "t": "source.c meta.block.c string.quoted.double.c constant.other.placeholder.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c constant.other.placeholder.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1486,7 +1508,7 @@ }, { "c": " and ", - "t": "source.c meta.block.c string.quoted.double.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1497,7 +1519,7 @@ }, { "c": "%.2f", - "t": "source.c meta.block.c string.quoted.double.c constant.other.placeholder.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c constant.other.placeholder.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1508,7 +1530,7 @@ }, { "c": "\"", - "t": "source.c meta.block.c string.quoted.double.c punctuation.definition.string.end.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c punctuation.definition.string.end.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1519,7 +1541,7 @@ }, { "c": ",", - "t": "source.c meta.block.c punctuation.separator.delimiter.c", + "t": "source.c meta.block.c meta.function-call.c punctuation.separator.delimiter.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1530,7 +1552,7 @@ }, { "c": "r1 ", - "t": "source.c meta.block.c", + "t": "source.c meta.block.c meta.function-call.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1541,7 +1563,7 @@ }, { "c": ",", - "t": "source.c meta.block.c punctuation.separator.delimiter.c", + "t": "source.c meta.block.c meta.function-call.c punctuation.separator.delimiter.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1552,7 +1574,7 @@ }, { "c": " r2", - "t": "source.c meta.block.c", + "t": "source.c meta.block.c meta.function-call.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1563,7 +1585,7 @@ }, { "c": ")", - "t": "source.c meta.block.c punctuation.section.parens.end.bracket.round.c", + "t": "source.c meta.block.c meta.function-call.c punctuation.section.arguments.end.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1662,7 +1684,7 @@ }, { "c": "(", - "t": "source.c meta.block.c punctuation.section.parens.begin.bracket.round.c", + "t": "source.c meta.block.c punctuation.section.parens.block punctuation.section.parens.begin.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1673,7 +1695,7 @@ }, { "c": "determinant", - "t": "source.c meta.block.c", + "t": "source.c meta.block.c punctuation.section.parens.block", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1684,7 +1706,7 @@ }, { "c": "==", - "t": "source.c meta.block.c keyword.operator.comparison.c", + "t": "source.c meta.block.c punctuation.section.parens.block keyword.operator.comparison.c", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1695,7 +1717,7 @@ }, { "c": "0", - "t": "source.c meta.block.c constant.numeric.c", + "t": "source.c meta.block.c punctuation.section.parens.block constant.numeric.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -1706,7 +1728,7 @@ }, { "c": ")", - "t": "source.c meta.block.c punctuation.section.parens.end.bracket.round.c", + "t": "source.c meta.block.c punctuation.section.parens.block punctuation.section.parens.end.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1827,7 +1849,7 @@ }, { "c": "(", - "t": "source.c meta.block.c punctuation.section.parens.begin.bracket.round.c", + "t": "source.c meta.block.c punctuation.section.parens.block punctuation.section.parens.begin.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1838,7 +1860,7 @@ }, { "c": "2", - "t": "source.c meta.block.c constant.numeric.c", + "t": "source.c meta.block.c punctuation.section.parens.block constant.numeric.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -1849,7 +1871,7 @@ }, { "c": "*", - "t": "source.c meta.block.c keyword.operator.c", + "t": "source.c meta.block.c punctuation.section.parens.block keyword.operator.c", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1860,7 +1882,7 @@ }, { "c": "a", - "t": "source.c meta.block.c", + "t": "source.c meta.block.c punctuation.section.parens.block", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1871,7 +1893,7 @@ }, { "c": ")", - "t": "source.c meta.block.c punctuation.section.parens.end.bracket.round.c", + "t": "source.c meta.block.c punctuation.section.parens.block punctuation.section.parens.end.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1893,7 +1915,7 @@ }, { "c": " ", - "t": "source.c meta.block.c punctuation.whitespace.support.function.leading.c", + "t": "source.c meta.block.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1904,18 +1926,18 @@ }, { "c": "printf", - "t": "source.c meta.block.c support.function.C99.c", + "t": "source.c meta.block.c meta.function-call.c entity.name.function.c", "r": { - "dark_plus": "support.function: #DCDCAA", - "light_plus": "support.function: #795E26", + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "support.function: #DCDCAA" + "hc_black": "entity.name.function: #DCDCAA" } }, { "c": "(", - "t": "source.c meta.block.c punctuation.section.parens.begin.bracket.round.c", + "t": "source.c meta.block.c meta.function-call.c punctuation.section.arguments.begin.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1926,7 +1948,7 @@ }, { "c": "\"", - "t": "source.c meta.block.c string.quoted.double.c punctuation.definition.string.begin.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c punctuation.definition.string.begin.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1937,7 +1959,7 @@ }, { "c": "Roots are: ", - "t": "source.c meta.block.c string.quoted.double.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1948,7 +1970,7 @@ }, { "c": "%.2f", - "t": "source.c meta.block.c string.quoted.double.c constant.other.placeholder.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c constant.other.placeholder.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1959,7 +1981,7 @@ }, { "c": " and ", - "t": "source.c meta.block.c string.quoted.double.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1970,7 +1992,7 @@ }, { "c": "%.2f", - "t": "source.c meta.block.c string.quoted.double.c constant.other.placeholder.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c constant.other.placeholder.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1981,7 +2003,7 @@ }, { "c": "\"", - "t": "source.c meta.block.c string.quoted.double.c punctuation.definition.string.end.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c punctuation.definition.string.end.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1992,7 +2014,7 @@ }, { "c": ",", - "t": "source.c meta.block.c punctuation.separator.delimiter.c", + "t": "source.c meta.block.c meta.function-call.c punctuation.separator.delimiter.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2003,7 +2025,7 @@ }, { "c": " r1", - "t": "source.c meta.block.c", + "t": "source.c meta.block.c meta.function-call.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2014,7 +2036,7 @@ }, { "c": ",", - "t": "source.c meta.block.c punctuation.separator.delimiter.c", + "t": "source.c meta.block.c meta.function-call.c punctuation.separator.delimiter.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2025,7 +2047,7 @@ }, { "c": " r2", - "t": "source.c meta.block.c", + "t": "source.c meta.block.c meta.function-call.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2036,7 +2058,7 @@ }, { "c": ")", - "t": "source.c meta.block.c punctuation.section.parens.end.bracket.round.c", + "t": "source.c meta.block.c meta.function-call.c punctuation.section.arguments.end.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2190,7 +2212,7 @@ }, { "c": "(", - "t": "source.c meta.block.c punctuation.section.parens.begin.bracket.round.c", + "t": "source.c meta.block.c punctuation.section.parens.block punctuation.section.parens.begin.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2201,7 +2223,7 @@ }, { "c": "2", - "t": "source.c meta.block.c constant.numeric.c", + "t": "source.c meta.block.c punctuation.section.parens.block constant.numeric.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -2212,7 +2234,7 @@ }, { "c": "*", - "t": "source.c meta.block.c keyword.operator.c", + "t": "source.c meta.block.c punctuation.section.parens.block keyword.operator.c", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -2223,7 +2245,7 @@ }, { "c": "a", - "t": "source.c meta.block.c", + "t": "source.c meta.block.c punctuation.section.parens.block", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2234,7 +2256,7 @@ }, { "c": ")", - "t": "source.c meta.block.c punctuation.section.parens.end.bracket.round.c", + "t": "source.c meta.block.c punctuation.section.parens.block punctuation.section.parens.end.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2278,50 +2300,6 @@ }, { "c": " ", - "t": "source.c meta.block.c punctuation.whitespace.support.function.leading.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "sqrt", - "t": "source.c meta.block.c support.function.C99.c", - "r": { - "dark_plus": "support.function: #DCDCAA", - "light_plus": "support.function: #795E26", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "support.function: #DCDCAA" - } - }, - { - "c": "(", - "t": "source.c meta.block.c punctuation.section.parens.begin.bracket.round.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "-", - "t": "source.c meta.block.c keyword.operator.c", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": "determinant", "t": "source.c meta.block.c", "r": { "dark_plus": "default: #D4D4D4", @@ -2331,9 +2309,53 @@ "hc_black": "default: #FFFFFF" } }, + { + "c": "sqrt", + "t": "source.c meta.block.c meta.function-call.c entity.name.function.c", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA" + } + }, + { + "c": "(", + "t": "source.c meta.block.c meta.function-call.c punctuation.section.arguments.begin.bracket.round.c", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "-", + "t": "source.c meta.block.c meta.function-call.c keyword.operator.c", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "determinant", + "t": "source.c meta.block.c meta.function-call.c", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, { "c": ")", - "t": "source.c meta.block.c punctuation.section.parens.end.bracket.round.c", + "t": "source.c meta.block.c meta.function-call.c punctuation.section.arguments.end.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2355,7 +2377,7 @@ }, { "c": "(", - "t": "source.c meta.block.c punctuation.section.parens.begin.bracket.round.c", + "t": "source.c meta.block.c punctuation.section.parens.block punctuation.section.parens.begin.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2366,7 +2388,7 @@ }, { "c": "2", - "t": "source.c meta.block.c constant.numeric.c", + "t": "source.c meta.block.c punctuation.section.parens.block constant.numeric.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -2377,7 +2399,7 @@ }, { "c": "*", - "t": "source.c meta.block.c keyword.operator.c", + "t": "source.c meta.block.c punctuation.section.parens.block keyword.operator.c", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -2388,7 +2410,7 @@ }, { "c": "a", - "t": "source.c meta.block.c", + "t": "source.c meta.block.c punctuation.section.parens.block", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2399,7 +2421,7 @@ }, { "c": ")", - "t": "source.c meta.block.c punctuation.section.parens.end.bracket.round.c", + "t": "source.c meta.block.c punctuation.section.parens.block punctuation.section.parens.end.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2421,7 +2443,7 @@ }, { "c": " ", - "t": "source.c meta.block.c punctuation.whitespace.support.function.leading.c", + "t": "source.c meta.block.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2432,18 +2454,18 @@ }, { "c": "printf", - "t": "source.c meta.block.c support.function.C99.c", + "t": "source.c meta.block.c meta.function-call.c entity.name.function.c", "r": { - "dark_plus": "support.function: #DCDCAA", - "light_plus": "support.function: #795E26", + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "support.function: #DCDCAA" + "hc_black": "entity.name.function: #DCDCAA" } }, { "c": "(", - "t": "source.c meta.block.c punctuation.section.parens.begin.bracket.round.c", + "t": "source.c meta.block.c meta.function-call.c punctuation.section.arguments.begin.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2454,7 +2476,7 @@ }, { "c": "\"", - "t": "source.c meta.block.c string.quoted.double.c punctuation.definition.string.begin.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c punctuation.definition.string.begin.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -2465,7 +2487,7 @@ }, { "c": "Roots are: ", - "t": "source.c meta.block.c string.quoted.double.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -2476,7 +2498,7 @@ }, { "c": "%.2f", - "t": "source.c meta.block.c string.quoted.double.c constant.other.placeholder.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c constant.other.placeholder.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -2487,7 +2509,7 @@ }, { "c": "+", - "t": "source.c meta.block.c string.quoted.double.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -2498,7 +2520,7 @@ }, { "c": "%.2f", - "t": "source.c meta.block.c string.quoted.double.c constant.other.placeholder.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c constant.other.placeholder.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -2509,7 +2531,7 @@ }, { "c": "i and ", - "t": "source.c meta.block.c string.quoted.double.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -2520,7 +2542,7 @@ }, { "c": "%.2f", - "t": "source.c meta.block.c string.quoted.double.c constant.other.placeholder.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c constant.other.placeholder.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -2531,7 +2553,7 @@ }, { "c": "-", - "t": "source.c meta.block.c string.quoted.double.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -2542,7 +2564,7 @@ }, { "c": "%.2f", - "t": "source.c meta.block.c string.quoted.double.c constant.other.placeholder.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c constant.other.placeholder.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -2553,7 +2575,7 @@ }, { "c": "i", - "t": "source.c meta.block.c string.quoted.double.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -2564,7 +2586,7 @@ }, { "c": "\"", - "t": "source.c meta.block.c string.quoted.double.c punctuation.definition.string.end.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c punctuation.definition.string.end.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -2575,7 +2597,7 @@ }, { "c": ",", - "t": "source.c meta.block.c punctuation.separator.delimiter.c", + "t": "source.c meta.block.c meta.function-call.c punctuation.separator.delimiter.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2586,7 +2608,7 @@ }, { "c": " real", - "t": "source.c meta.block.c", + "t": "source.c meta.block.c meta.function-call.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2597,7 +2619,7 @@ }, { "c": ",", - "t": "source.c meta.block.c punctuation.separator.delimiter.c", + "t": "source.c meta.block.c meta.function-call.c punctuation.separator.delimiter.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2608,7 +2630,7 @@ }, { "c": " imag", - "t": "source.c meta.block.c", + "t": "source.c meta.block.c meta.function-call.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2619,7 +2641,7 @@ }, { "c": ",", - "t": "source.c meta.block.c punctuation.separator.delimiter.c", + "t": "source.c meta.block.c meta.function-call.c punctuation.separator.delimiter.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2630,7 +2652,7 @@ }, { "c": " real", - "t": "source.c meta.block.c", + "t": "source.c meta.block.c meta.function-call.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2641,7 +2663,7 @@ }, { "c": ",", - "t": "source.c meta.block.c punctuation.separator.delimiter.c", + "t": "source.c meta.block.c meta.function-call.c punctuation.separator.delimiter.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2652,7 +2674,7 @@ }, { "c": " imag", - "t": "source.c meta.block.c", + "t": "source.c meta.block.c meta.function-call.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2663,7 +2685,7 @@ }, { "c": ")", - "t": "source.c meta.block.c punctuation.section.parens.end.bracket.round.c", + "t": "source.c meta.block.c meta.function-call.c punctuation.section.arguments.end.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", diff --git a/extensions/cpp/test/colorize-results/test_cc.json b/extensions/cpp/test/colorize-results/test_cc.json index f3f72320fb5..96e8792159f 100644 --- a/extensions/cpp/test/colorize-results/test_cc.json +++ b/extensions/cpp/test/colorize-results/test_cc.json @@ -1,7 +1,7 @@ [ { "c": "#", - "t": "source.cpp meta.preprocessor.c keyword.control.directive.conditional.c punctuation.definition.directive.c", + "t": "source.cpp meta.preprocessor.cpp keyword.control.directive.conditional.cpp punctuation.definition.directive.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -12,7 +12,7 @@ }, { "c": "if", - "t": "source.cpp meta.preprocessor.c keyword.control.directive.conditional.c", + "t": "source.cpp meta.preprocessor.cpp keyword.control.directive.conditional.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -23,7 +23,7 @@ }, { "c": " ", - "t": "source.cpp meta.preprocessor.c", + "t": "source.cpp meta.preprocessor.cpp", "r": { "dark_plus": "meta.preprocessor: #569CD6", "light_plus": "meta.preprocessor: #0000FF", @@ -34,7 +34,7 @@ }, { "c": "B4G_DEBUG_CHECK", - "t": "source.cpp meta.preprocessor.c entity.name.function.preprocessor.c", + "t": "source.cpp meta.preprocessor.cpp entity.name.function.preprocessor.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -56,7 +56,7 @@ }, { "c": "fprintf", - "t": "source.cpp meta.function.c entity.name.function.c", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp entity.name.function.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -67,7 +67,7 @@ }, { "c": "(", - "t": "source.cpp meta.function.c punctuation.section.parameters.begin.bracket.round.c", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.begin.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -78,7 +78,7 @@ }, { "c": "stderr", - "t": "source.cpp meta.function.c", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -89,7 +89,7 @@ }, { "c": ",", - "t": "source.cpp meta.function.c punctuation.separator.delimiter.c", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp punctuation.separator.delimiter.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -100,7 +100,7 @@ }, { "c": "\"", - "t": "source.cpp meta.function.c string.quoted.double.cpp punctuation.definition.string.begin.cpp", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp string.quoted.double.cpp punctuation.definition.string.begin.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -111,7 +111,7 @@ }, { "c": "num_candidate_ret=", - "t": "source.cpp meta.function.c string.quoted.double.cpp", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp string.quoted.double.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -122,7 +122,7 @@ }, { "c": "%d", - "t": "source.cpp meta.function.c string.quoted.double.cpp constant.other.placeholder.c", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp string.quoted.double.cpp constant.other.placeholder.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -133,7 +133,7 @@ }, { "c": ":", - "t": "source.cpp meta.function.c string.quoted.double.cpp", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp string.quoted.double.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -144,7 +144,7 @@ }, { "c": "\"", - "t": "source.cpp meta.function.c string.quoted.double.cpp punctuation.definition.string.end.cpp", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp string.quoted.double.cpp punctuation.definition.string.end.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -155,7 +155,7 @@ }, { "c": ",", - "t": "source.cpp meta.function.c punctuation.separator.delimiter.c", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp punctuation.separator.delimiter.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -166,7 +166,7 @@ }, { "c": " num_candidate", - "t": "source.cpp meta.function.c", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -177,7 +177,7 @@ }, { "c": ")", - "t": "source.cpp meta.function.c punctuation.section.parameters.end.bracket.round.c", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.end.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -188,7 +188,7 @@ }, { "c": ";", - "t": "source.cpp punctuation.terminator.statement.c", + "t": "source.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -210,7 +210,7 @@ }, { "c": "for", - "t": "source.cpp keyword.control.c", + "t": "source.cpp keyword.control.for.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -221,7 +221,7 @@ }, { "c": "(", - "t": "source.cpp punctuation.section.parens.begin.bracket.round.c", + "t": "source.cpp punctuation.section.parens-c\b.cpp punctuation.section.parens.begin.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -232,7 +232,7 @@ }, { "c": "int", - "t": "source.cpp storage.type.c", + "t": "source.cpp punctuation.section.parens-c\b.cpp storage.type.language.primitive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -243,7 +243,7 @@ }, { "c": " i", - "t": "source.cpp", + "t": "source.cpp punctuation.section.parens-c\b.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -254,7 +254,7 @@ }, { "c": "=", - "t": "source.cpp keyword.operator.assignment.c", + "t": "source.cpp punctuation.section.parens-c\b.cpp keyword.operator.assignment.cpp", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -265,7 +265,7 @@ }, { "c": "0", - "t": "source.cpp constant.numeric.c", + "t": "source.cpp punctuation.section.parens-c\b.cpp constant.numeric.cpp", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -276,7 +276,7 @@ }, { "c": ";", - "t": "source.cpp punctuation.terminator.statement.c", + "t": "source.cpp punctuation.section.parens-c\b.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -287,7 +287,7 @@ }, { "c": "i", - "t": "source.cpp", + "t": "source.cpp punctuation.section.parens-c\b.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -298,7 +298,7 @@ }, { "c": "<", - "t": "source.cpp keyword.operator.comparison.c", + "t": "source.cpp punctuation.section.parens-c\b.cpp keyword.operator.comparison.cpp", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -309,7 +309,7 @@ }, { "c": "num_candidate", - "t": "source.cpp", + "t": "source.cpp punctuation.section.parens-c\b.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -320,7 +320,7 @@ }, { "c": ";", - "t": "source.cpp punctuation.terminator.statement.c", + "t": "source.cpp punctuation.section.parens-c\b.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -331,7 +331,7 @@ }, { "c": "++", - "t": "source.cpp keyword.operator.increment.c", + "t": "source.cpp punctuation.section.parens-c\b.cpp keyword.operator.increment.cpp", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -342,7 +342,7 @@ }, { "c": "i", - "t": "source.cpp", + "t": "source.cpp punctuation.section.parens-c\b.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -353,7 +353,7 @@ }, { "c": ")", - "t": "source.cpp punctuation.section.parens.end.bracket.round.c", + "t": "source.cpp punctuation.section.parens-c\b.cpp punctuation.section.parens.end.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -375,7 +375,7 @@ }, { "c": "fprintf", - "t": "source.cpp meta.function.c entity.name.function.c", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp entity.name.function.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -386,7 +386,7 @@ }, { "c": "(", - "t": "source.cpp meta.function.c punctuation.section.parameters.begin.bracket.round.c", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.begin.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -397,7 +397,7 @@ }, { "c": "stderr", - "t": "source.cpp meta.function.c", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -408,7 +408,7 @@ }, { "c": ",", - "t": "source.cpp meta.function.c punctuation.separator.delimiter.c", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp punctuation.separator.delimiter.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -419,7 +419,7 @@ }, { "c": "\"", - "t": "source.cpp meta.function.c string.quoted.double.cpp punctuation.definition.string.begin.cpp", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp string.quoted.double.cpp punctuation.definition.string.begin.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -430,7 +430,7 @@ }, { "c": "%d", - "t": "source.cpp meta.function.c string.quoted.double.cpp constant.other.placeholder.c", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp string.quoted.double.cpp constant.other.placeholder.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -441,7 +441,7 @@ }, { "c": ",", - "t": "source.cpp meta.function.c string.quoted.double.cpp", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp string.quoted.double.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -452,7 +452,7 @@ }, { "c": "\"", - "t": "source.cpp meta.function.c string.quoted.double.cpp punctuation.definition.string.end.cpp", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp string.quoted.double.cpp punctuation.definition.string.end.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -463,7 +463,7 @@ }, { "c": ",", - "t": "source.cpp meta.function.c punctuation.separator.delimiter.c", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp punctuation.separator.delimiter.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -474,18 +474,18 @@ }, { "c": "user_candidate", - "t": "source.cpp meta.function.c", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp meta.bracket.square.access.cpp variable.object.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "variable: #9CDCFE" } }, { "c": "[", - "t": "source.cpp meta.function.c punctuation.definition.begin.bracket.square.c", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp meta.bracket.square.access.cpp punctuation.definition.begin.bracket.square.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -496,7 +496,7 @@ }, { "c": "i", - "t": "source.cpp meta.function.c", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp meta.bracket.square.access.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -507,7 +507,7 @@ }, { "c": "]", - "t": "source.cpp meta.function.c punctuation.definition.end.bracket.square.c", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp meta.bracket.square.access.cpp punctuation.definition.end.bracket.square.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -518,7 +518,7 @@ }, { "c": ")", - "t": "source.cpp meta.function.c punctuation.section.parameters.end.bracket.round.c", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.end.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -529,7 +529,7 @@ }, { "c": ";", - "t": "source.cpp punctuation.terminator.statement.c", + "t": "source.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -551,7 +551,7 @@ }, { "c": "fprintf", - "t": "source.cpp meta.function.c entity.name.function.c", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp entity.name.function.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -562,7 +562,7 @@ }, { "c": "(", - "t": "source.cpp meta.function.c punctuation.section.parameters.begin.bracket.round.c", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.begin.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -573,7 +573,7 @@ }, { "c": "stderr", - "t": "source.cpp meta.function.c", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -584,7 +584,7 @@ }, { "c": ",", - "t": "source.cpp meta.function.c punctuation.separator.delimiter.c", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp punctuation.separator.delimiter.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -595,7 +595,7 @@ }, { "c": "\"", - "t": "source.cpp meta.function.c string.quoted.double.cpp punctuation.definition.string.begin.cpp", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp string.quoted.double.cpp punctuation.definition.string.begin.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -606,7 +606,7 @@ }, { "c": ";", - "t": "source.cpp meta.function.c string.quoted.double.cpp", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp string.quoted.double.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -617,7 +617,7 @@ }, { "c": "\"", - "t": "source.cpp meta.function.c string.quoted.double.cpp punctuation.definition.string.end.cpp", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp string.quoted.double.cpp punctuation.definition.string.end.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -628,7 +628,7 @@ }, { "c": ")", - "t": "source.cpp meta.function.c punctuation.section.parameters.end.bracket.round.c", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.end.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -639,7 +639,7 @@ }, { "c": ";", - "t": "source.cpp punctuation.terminator.statement.c", + "t": "source.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -650,7 +650,7 @@ }, { "c": "#", - "t": "source.cpp meta.preprocessor.c keyword.control.directive.conditional.c punctuation.definition.directive.c", + "t": "source.cpp meta.preprocessor.cpp keyword.control.directive.conditional.cpp punctuation.definition.directive.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -661,7 +661,7 @@ }, { "c": "endif", - "t": "source.cpp meta.preprocessor.c keyword.control.directive.conditional.c", + "t": "source.cpp meta.preprocessor.cpp keyword.control.directive.conditional.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -683,7 +683,7 @@ }, { "c": "void", - "t": "source.cpp storage.type.c", + "t": "source.cpp storage.type.language.primitive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -705,7 +705,7 @@ }, { "c": "main", - "t": "source.cpp meta.function.c entity.name.function.c", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp entity.name.function.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -716,7 +716,7 @@ }, { "c": "(", - "t": "source.cpp meta.function.c punctuation.section.parameters.begin.bracket.round.c", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.begin.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -726,8 +726,8 @@ } }, { - "c": "O obj", - "t": "source.cpp meta.function.c", + "c": "O ", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -736,9 +736,20 @@ "hc_black": "default: #FFFFFF" } }, + { + "c": "obj", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp variable.parameter.probably.cpp", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, { "c": ")", - "t": "source.cpp meta.function.c punctuation.section.parameters.end.bracket.round.c", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.end.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -760,7 +771,7 @@ }, { "c": "{", - "t": "source.cpp meta.block.c punctuation.section.block.begin.bracket.curly.c", + "t": "source.cpp meta.block.cpp punctuation.section.block.begin.bracket.curly.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -771,7 +782,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.c", + "t": "source.cpp meta.block.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -782,7 +793,7 @@ }, { "c": "LOG_INFO", - "t": "source.cpp meta.block.c meta.function-call.c entity.name.function.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp entity.name.function.call.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -793,7 +804,7 @@ }, { "c": "(", - "t": "source.cpp meta.block.c meta.function-call.c punctuation.section.arguments.begin.bracket.round.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp punctuation.section.arguments.begin.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -804,7 +815,7 @@ }, { "c": "\"", - "t": "source.cpp meta.block.c meta.function-call.c string.quoted.double.cpp punctuation.definition.string.begin.cpp", + "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp punctuation.definition.string.begin.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -815,7 +826,7 @@ }, { "c": "not hilighted as string", - "t": "source.cpp meta.block.c meta.function-call.c string.quoted.double.cpp", + "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -826,7 +837,7 @@ }, { "c": "\"", - "t": "source.cpp meta.block.c meta.function-call.c string.quoted.double.cpp punctuation.definition.string.end.cpp", + "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp punctuation.definition.string.end.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -837,7 +848,7 @@ }, { "c": ")", - "t": "source.cpp meta.block.c meta.function-call.c punctuation.section.arguments.end.bracket.round.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp punctuation.section.arguments.end.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -848,7 +859,7 @@ }, { "c": ";", - "t": "source.cpp meta.block.c punctuation.terminator.statement.c", + "t": "source.cpp meta.block.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -859,7 +870,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.c", + "t": "source.cpp meta.block.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -870,7 +881,7 @@ }, { "c": "LOG_INFO", - "t": "source.cpp meta.block.c meta.function-call.c entity.name.function.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp entity.name.function.call.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -881,7 +892,7 @@ }, { "c": "(", - "t": "source.cpp meta.block.c meta.function-call.c punctuation.section.arguments.begin.bracket.round.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp punctuation.section.arguments.begin.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -892,7 +903,7 @@ }, { "c": "obj ", - "t": "source.cpp meta.block.c meta.function-call.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -903,7 +914,7 @@ }, { "c": "<<", - "t": "source.cpp meta.block.c meta.function-call.c keyword.operator.bitwise.shift.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp keyword.operator.bitwise.shift.cpp", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -914,7 +925,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.c meta.function-call.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -925,7 +936,7 @@ }, { "c": "\"", - "t": "source.cpp meta.block.c meta.function-call.c string.quoted.double.cpp punctuation.definition.string.begin.cpp", + "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp punctuation.definition.string.begin.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -936,7 +947,7 @@ }, { "c": ", even worse; ", - "t": "source.cpp meta.block.c meta.function-call.c string.quoted.double.cpp", + "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -947,7 +958,7 @@ }, { "c": "\"", - "t": "source.cpp meta.block.c meta.function-call.c string.quoted.double.cpp punctuation.definition.string.end.cpp", + "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp punctuation.definition.string.end.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -958,7 +969,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.c meta.function-call.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -969,7 +980,7 @@ }, { "c": "<<", - "t": "source.cpp meta.block.c meta.function-call.c keyword.operator.bitwise.shift.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp keyword.operator.bitwise.shift.cpp", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -979,8 +990,8 @@ } }, { - "c": " obj", - "t": "source.cpp meta.block.c meta.function-call.c", + "c": " ", + "t": "source.cpp meta.block.cpp meta.function-call.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -989,20 +1000,31 @@ "hc_black": "default: #FFFFFF" } }, + { + "c": "obj", + "t": "source.cpp meta.block.cpp meta.function-call.cpp variable.object.access.cpp variable.object.cpp", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, { "c": ".", - "t": "source.cpp meta.block.c meta.function-call.c punctuation.separator.dot-access.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp variable.object.access.cpp punctuation.separator.dot-access.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "variable: #9CDCFE" } }, { "c": "x", - "t": "source.cpp meta.block.c meta.function-call.c variable.other.member.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp variable.object.access.cpp variable.other.member.cpp", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -1013,7 +1035,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.c meta.function-call.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1024,7 +1046,7 @@ }, { "c": "<<", - "t": "source.cpp meta.block.c meta.function-call.c keyword.operator.bitwise.shift.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp keyword.operator.bitwise.shift.cpp", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1035,7 +1057,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.c meta.function-call.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1046,7 +1068,7 @@ }, { "c": "\"", - "t": "source.cpp meta.block.c meta.function-call.c string.quoted.double.cpp punctuation.definition.string.begin.cpp", + "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp punctuation.definition.string.begin.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1057,7 +1079,7 @@ }, { "c": " check this out.", - "t": "source.cpp meta.block.c meta.function-call.c string.quoted.double.cpp", + "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1068,7 +1090,7 @@ }, { "c": "\"", - "t": "source.cpp meta.block.c meta.function-call.c string.quoted.double.cpp punctuation.definition.string.end.cpp", + "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp punctuation.definition.string.end.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1079,7 +1101,7 @@ }, { "c": ")", - "t": "source.cpp meta.block.c meta.function-call.c punctuation.section.arguments.end.bracket.round.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp punctuation.section.arguments.end.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1090,7 +1112,7 @@ }, { "c": ";", - "t": "source.cpp meta.block.c punctuation.terminator.statement.c", + "t": "source.cpp meta.block.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1101,7 +1123,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.c punctuation.whitespace.comment.leading.cpp", + "t": "source.cpp meta.block.cpp punctuation.whitespace.comment.leading.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1112,7 +1134,7 @@ }, { "c": "//", - "t": "source.cpp meta.block.c comment.line.double-slash.cpp punctuation.definition.comment.cpp", + "t": "source.cpp meta.block.cpp comment.line.double-slash.cpp punctuation.definition.comment.cpp", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -1123,7 +1145,7 @@ }, { "c": " everything from this point on is interpeted as a string literal...", - "t": "source.cpp meta.block.c comment.line.double-slash.cpp", + "t": "source.cpp meta.block.cpp comment.line.double-slash.cpp", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -1134,7 +1156,7 @@ }, { "c": " O x", - "t": "source.cpp meta.block.c", + "t": "source.cpp meta.block.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1145,7 +1167,7 @@ }, { "c": ";", - "t": "source.cpp meta.block.c punctuation.terminator.statement.c", + "t": "source.cpp meta.block.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1155,8 +1177,19 @@ } }, { - "c": " std", - "t": "source.cpp meta.block.c", + "c": " ", + "t": "source.cpp meta.block.cpp punctuation.separator.namespace.access.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "std", + "t": "source.cpp meta.block.cpp punctuation.separator.namespace.access.cpp entity.scope.name.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1167,7 +1200,7 @@ }, { "c": "::", - "t": "source.cpp meta.block.c punctuation.separator.namespace.access.cpp", + "t": "source.cpp meta.block.cpp punctuation.separator.namespace.access.cpp punctuation.separator.namespace.access.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1178,7 +1211,7 @@ }, { "c": "unique_ptr", - "t": "source.cpp meta.block.c", + "t": "source.cpp meta.block.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1189,7 +1222,7 @@ }, { "c": "<", - "t": "source.cpp meta.block.c keyword.operator.comparison.c", + "t": "source.cpp meta.block.cpp keyword.operator.comparison.cpp", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1200,7 +1233,7 @@ }, { "c": "O", - "t": "source.cpp meta.block.c", + "t": "source.cpp meta.block.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1211,7 +1244,7 @@ }, { "c": ">", - "t": "source.cpp meta.block.c keyword.operator.comparison.c", + "t": "source.cpp meta.block.cpp keyword.operator.comparison.cpp", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1222,7 +1255,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.c", + "t": "source.cpp meta.block.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1233,7 +1266,7 @@ }, { "c": "o", - "t": "source.cpp meta.block.c meta.function-call.c entity.name.function.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp entity.name.function.call.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -1244,7 +1277,7 @@ }, { "c": "(", - "t": "source.cpp meta.block.c meta.function-call.c punctuation.section.arguments.begin.bracket.round.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp punctuation.section.arguments.begin.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1255,18 +1288,18 @@ }, { "c": "new", - "t": "source.cpp meta.block.c meta.function-call.c keyword.control.cpp", + "t": "source.cpp meta.block.cpp meta.function-call.cpp keyword.operator.new.cpp", "r": { - "dark_plus": "keyword.control: #C586C0", - "light_plus": "keyword.control: #AF00DB", - "dark_vs": "keyword.control: #569CD6", - "light_vs": "keyword.control: #0000FF", - "hc_black": "keyword.control: #C586C0" + "dark_plus": "keyword.operator.new: #569CD6", + "light_plus": "keyword.operator.new: #0000FF", + "dark_vs": "keyword.operator.new: #569CD6", + "light_vs": "keyword.operator.new: #0000FF", + "hc_black": "keyword.operator.new: #569CD6" } }, { "c": " O", - "t": "source.cpp meta.block.c meta.function-call.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1277,7 +1310,7 @@ }, { "c": ")", - "t": "source.cpp meta.block.c meta.function-call.c punctuation.section.arguments.end.bracket.round.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp punctuation.section.arguments.end.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1288,7 +1321,7 @@ }, { "c": ";", - "t": "source.cpp meta.block.c punctuation.terminator.statement.c", + "t": "source.cpp meta.block.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1299,7 +1332,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.c punctuation.whitespace.comment.leading.cpp", + "t": "source.cpp meta.block.cpp punctuation.whitespace.comment.leading.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1310,7 +1343,7 @@ }, { "c": "//", - "t": "source.cpp meta.block.c comment.line.double-slash.cpp punctuation.definition.comment.cpp", + "t": "source.cpp meta.block.cpp comment.line.double-slash.cpp punctuation.definition.comment.cpp", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -1321,7 +1354,7 @@ }, { "c": " sadness.", - "t": "source.cpp meta.block.c comment.line.double-slash.cpp", + "t": "source.cpp meta.block.cpp comment.line.double-slash.cpp", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -1332,7 +1365,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.c punctuation.whitespace.support.function.leading.c", + "t": "source.cpp meta.block.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1343,18 +1376,18 @@ }, { "c": "sprintf", - "t": "source.cpp meta.block.c support.function.C99.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp entity.name.function.call.cpp", "r": { - "dark_plus": "support.function: #DCDCAA", - "light_plus": "support.function: #795E26", + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "support.function: #DCDCAA" + "hc_black": "entity.name.function: #DCDCAA" } }, { "c": "(", - "t": "source.cpp meta.block.c punctuation.section.parens.begin.bracket.round.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp punctuation.section.arguments.begin.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1365,7 +1398,7 @@ }, { "c": "options", - "t": "source.cpp meta.block.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1376,7 +1409,7 @@ }, { "c": ",", - "t": "source.cpp meta.block.c punctuation.separator.delimiter.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp punctuation.separator.delimiter.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1387,7 +1420,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1398,7 +1431,7 @@ }, { "c": "\"", - "t": "source.cpp meta.block.c string.quoted.double.cpp punctuation.definition.string.begin.cpp", + "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp punctuation.definition.string.begin.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1409,7 +1442,7 @@ }, { "c": "STYLE=Keramik;TITLE=", - "t": "source.cpp meta.block.c string.quoted.double.cpp", + "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1420,7 +1453,7 @@ }, { "c": "%s", - "t": "source.cpp meta.block.c string.quoted.double.cpp constant.other.placeholder.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp constant.other.placeholder.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1431,7 +1464,7 @@ }, { "c": ";THEME=", - "t": "source.cpp meta.block.c string.quoted.double.cpp", + "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1442,7 +1475,7 @@ }, { "c": "%s", - "t": "source.cpp meta.block.c string.quoted.double.cpp constant.other.placeholder.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp constant.other.placeholder.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1453,7 +1486,7 @@ }, { "c": "\"", - "t": "source.cpp meta.block.c string.quoted.double.cpp punctuation.definition.string.end.cpp", + "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp punctuation.definition.string.end.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1464,7 +1497,7 @@ }, { "c": ",", - "t": "source.cpp meta.block.c punctuation.separator.delimiter.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp punctuation.separator.delimiter.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1474,19 +1507,8 @@ } }, { - "c": " ", - "t": "source.cpp meta.block.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "...", - "t": "source.cpp meta.block.c punctuation.separator.dot-access.c", + "c": " ...", + "t": "source.cpp meta.block.cpp meta.function-call.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1497,7 +1519,7 @@ }, { "c": ")", - "t": "source.cpp meta.block.c punctuation.section.parens.end.bracket.round.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp punctuation.section.arguments.end.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1508,7 +1530,7 @@ }, { "c": ";", - "t": "source.cpp meta.block.c punctuation.terminator.statement.c", + "t": "source.cpp meta.block.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1519,7 +1541,7 @@ }, { "c": "}", - "t": "source.cpp meta.block.c punctuation.section.block.end.bracket.curly.c", + "t": "source.cpp meta.block.cpp punctuation.section.block.end.bracket.curly.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1530,7 +1552,7 @@ }, { "c": "int", - "t": "source.cpp storage.type.c", + "t": "source.cpp storage.type.language.primitive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -1552,7 +1574,7 @@ }, { "c": "main2", - "t": "source.cpp meta.function.c entity.name.function.c", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp entity.name.function.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -1563,7 +1585,7 @@ }, { "c": "(", - "t": "source.cpp meta.function.c punctuation.section.parameters.begin.bracket.round.c", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.begin.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1574,7 +1596,7 @@ }, { "c": ")", - "t": "source.cpp meta.function.c punctuation.section.parameters.end.bracket.round.c", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.end.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1596,7 +1618,7 @@ }, { "c": "{", - "t": "source.cpp meta.block.c punctuation.section.block.begin.bracket.curly.c", + "t": "source.cpp meta.block.cpp punctuation.section.block.begin.bracket.curly.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1607,7 +1629,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.c punctuation.whitespace.support.function.leading.c", + "t": "source.cpp meta.block.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1618,260 +1640,7 @@ }, { "c": "printf", - "t": "source.cpp meta.block.c support.function.C99.c", - "r": { - "dark_plus": "support.function: #DCDCAA", - "light_plus": "support.function: #795E26", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "support.function: #DCDCAA" - } - }, - { - "c": "(", - "t": "source.cpp meta.block.c punctuation.section.parens.begin.bracket.round.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "\"", - "t": "source.cpp meta.block.c string.quoted.double.cpp punctuation.definition.string.begin.cpp", - "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" - } - }, - { - "c": ";", - "t": "source.cpp meta.block.c string.quoted.double.cpp", - "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" - } - }, - { - "c": "\"", - "t": "source.cpp meta.block.c string.quoted.double.cpp punctuation.definition.string.end.cpp", - "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" - } - }, - { - "c": ")", - "t": "source.cpp meta.block.c punctuation.section.parens.end.bracket.round.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ";", - "t": "source.cpp meta.block.c punctuation.terminator.statement.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " ", - "t": "source.cpp meta.block.c punctuation.whitespace.comment.leading.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "//", - "t": "source.cpp meta.block.c comment.line.double-slash.cpp punctuation.definition.comment.cpp", - "r": { - "dark_plus": "comment: #6A9955", - "light_plus": "comment: #008000", - "dark_vs": "comment: #6A9955", - "light_vs": "comment: #008000", - "hc_black": "comment: #7CA668" - } - }, - { - "c": " the rest of", - "t": "source.cpp meta.block.c comment.line.double-slash.cpp", - "r": { - "dark_plus": "comment: #6A9955", - "light_plus": "comment: #008000", - "dark_vs": "comment: #6A9955", - "light_vs": "comment: #008000", - "hc_black": "comment: #7CA668" - } - }, - { - "c": " ", - "t": "source.cpp meta.block.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "asm", - "t": "source.cpp meta.block.c meta.function-call.c storage.type.c", - "r": { - "dark_plus": "storage.type: #569CD6", - "light_plus": "storage.type: #0000FF", - "dark_vs": "storage.type: #569CD6", - "light_vs": "storage.type: #0000FF", - "hc_black": "storage.type: #569CD6" - } - }, - { - "c": "(", - "t": "source.cpp meta.block.c meta.function-call.c punctuation.section.parens.begin.bracket.round.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "\"", - "t": "source.cpp meta.block.c meta.function-call.c string.quoted.double.cpp punctuation.definition.string.begin.cpp", - "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" - } - }, - { - "c": "movw $0x38, ", - "t": "source.cpp meta.block.c meta.function-call.c string.quoted.double.cpp", - "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" - } - }, - { - "c": "%a", - "t": "source.cpp meta.block.c meta.function-call.c string.quoted.double.cpp constant.other.placeholder.c", - "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" - } - }, - { - "c": "x; ltr ", - "t": "source.cpp meta.block.c meta.function-call.c string.quoted.double.cpp", - "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" - } - }, - { - "c": "%a", - "t": "source.cpp meta.block.c meta.function-call.c string.quoted.double.cpp constant.other.placeholder.c", - "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" - } - }, - { - "c": "x", - "t": "source.cpp meta.block.c meta.function-call.c string.quoted.double.cpp", - "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" - } - }, - { - "c": "\"", - "t": "source.cpp meta.block.c meta.function-call.c string.quoted.double.cpp punctuation.definition.string.end.cpp", - "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" - } - }, - { - "c": ")", - "t": "source.cpp meta.block.c meta.function-call.c punctuation.section.parens.end.bracket.round.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ";", - "t": "source.cpp meta.block.c punctuation.terminator.statement.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " ", - "t": "source.cpp meta.block.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "fn", - "t": "source.cpp meta.block.c meta.function-call.c entity.name.function.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp entity.name.function.call.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -1882,7 +1651,7 @@ }, { "c": "(", - "t": "source.cpp meta.block.c meta.function-call.c punctuation.section.arguments.begin.bracket.round.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp punctuation.section.arguments.begin.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1893,7 +1662,7 @@ }, { "c": "\"", - "t": "source.cpp meta.block.c meta.function-call.c string.quoted.double.cpp punctuation.definition.string.begin.cpp", + "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp punctuation.definition.string.begin.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1903,8 +1672,8 @@ } }, { - "c": "{};", - "t": "source.cpp meta.block.c meta.function-call.c string.quoted.double.cpp", + "c": ";", + "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1915,7 +1684,7 @@ }, { "c": "\"", - "t": "source.cpp meta.block.c meta.function-call.c string.quoted.double.cpp punctuation.definition.string.end.cpp", + "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp punctuation.definition.string.end.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1926,7 +1695,7 @@ }, { "c": ")", - "t": "source.cpp meta.block.c meta.function-call.c punctuation.section.arguments.end.bracket.round.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp punctuation.section.arguments.end.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1937,7 +1706,7 @@ }, { "c": ";", - "t": "source.cpp meta.block.c punctuation.terminator.statement.c", + "t": "source.cpp meta.block.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1948,7 +1717,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.c punctuation.whitespace.comment.leading.cpp", + "t": "source.cpp meta.block.cpp punctuation.whitespace.comment.leading.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1959,7 +1728,7 @@ }, { "c": "//", - "t": "source.cpp meta.block.c comment.line.double-slash.cpp punctuation.definition.comment.cpp", + "t": "source.cpp meta.block.cpp comment.line.double-slash.cpp punctuation.definition.comment.cpp", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -1970,7 +1739,260 @@ }, { "c": " the rest of", - "t": "source.cpp meta.block.c comment.line.double-slash.cpp", + "t": "source.cpp meta.block.cpp comment.line.double-slash.cpp", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " ", + "t": "source.cpp meta.block.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "asm", + "t": "source.cpp meta.block.cpp meta.function-call.cpp storage.type.asm.cpp", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": "(", + "t": "source.cpp meta.block.cpp meta.function-call.cpp punctuation.section.parens.begin.bracket.round.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp punctuation.definition.string.begin.cpp", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "movw $0x38, ", + "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "%a", + "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp constant.other.placeholder.cpp", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "x; ltr ", + "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "%a", + "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp constant.other.placeholder.cpp", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "x", + "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp punctuation.definition.string.end.cpp", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ")", + "t": "source.cpp meta.block.cpp meta.function-call.cpp punctuation.section.parens.end.bracket.round.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ";", + "t": "source.cpp meta.block.cpp punctuation.terminator.statement.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.cpp meta.block.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "fn", + "t": "source.cpp meta.block.cpp meta.function-call.cpp entity.name.function.call.cpp", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA" + } + }, + { + "c": "(", + "t": "source.cpp meta.block.cpp meta.function-call.cpp punctuation.section.arguments.begin.bracket.round.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp punctuation.definition.string.begin.cpp", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "{};", + "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp punctuation.definition.string.end.cpp", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ")", + "t": "source.cpp meta.block.cpp meta.function-call.cpp punctuation.section.arguments.end.bracket.round.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ";", + "t": "source.cpp meta.block.cpp punctuation.terminator.statement.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.cpp meta.block.cpp punctuation.whitespace.comment.leading.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "//", + "t": "source.cpp meta.block.cpp comment.line.double-slash.cpp punctuation.definition.comment.cpp", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " the rest of", + "t": "source.cpp meta.block.cpp comment.line.double-slash.cpp", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -1981,7 +2003,7 @@ }, { "c": "}", - "t": "source.cpp meta.block.c punctuation.section.block.end.bracket.curly.c", + "t": "source.cpp meta.block.cpp punctuation.section.block.end.bracket.curly.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", diff --git a/extensions/cpp/test/colorize-results/test_cpp.json b/extensions/cpp/test/colorize-results/test_cpp.json index b3c9a841cc4..134c8274ca7 100644 --- a/extensions/cpp/test/colorize-results/test_cpp.json +++ b/extensions/cpp/test/colorize-results/test_cpp.json @@ -23,7 +23,7 @@ }, { "c": "#", - "t": "source.cpp meta.preprocessor.include.c keyword.control.directive.include.c punctuation.definition.directive.c", + "t": "source.cpp meta.preprocessor.include.cpp keyword.control.directive.include.cpp punctuation.definition.directive.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -34,7 +34,7 @@ }, { "c": "include", - "t": "source.cpp meta.preprocessor.include.c keyword.control.directive.include.c", + "t": "source.cpp meta.preprocessor.include.cpp keyword.control.directive.include.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -45,7 +45,7 @@ }, { "c": " ", - "t": "source.cpp meta.preprocessor.include.c", + "t": "source.cpp meta.preprocessor.include.cpp", "r": { "dark_plus": "meta.preprocessor: #569CD6", "light_plus": "meta.preprocessor: #0000FF", @@ -56,7 +56,7 @@ }, { "c": "<", - "t": "source.cpp meta.preprocessor.include.c string.quoted.other.lt-gt.include.c punctuation.definition.string.begin.c", + "t": "source.cpp meta.preprocessor.include.cpp string.quoted.other.lt-gt.include.cpp punctuation.definition.string.begin.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -67,7 +67,7 @@ }, { "c": "iostream", - "t": "source.cpp meta.preprocessor.include.c string.quoted.other.lt-gt.include.c", + "t": "source.cpp meta.preprocessor.include.cpp string.quoted.other.lt-gt.include.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -78,7 +78,7 @@ }, { "c": ">", - "t": "source.cpp meta.preprocessor.include.c string.quoted.other.lt-gt.include.c punctuation.definition.string.end.c", + "t": "source.cpp meta.preprocessor.include.cpp string.quoted.other.lt-gt.include.cpp punctuation.definition.string.end.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -89,13 +89,13 @@ }, { "c": "using", - "t": "source.cpp meta.using-namespace-declaration.cpp keyword.control.cpp", + "t": "source.cpp meta.using-namespace-declaration.cpp keyword.other.using.directive.cpp", "r": { - "dark_plus": "keyword.control: #C586C0", - "light_plus": "keyword.control: #AF00DB", - "dark_vs": "keyword.control: #569CD6", - "light_vs": "keyword.control: #0000FF", - "hc_black": "keyword.control: #C586C0" + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" } }, { @@ -111,13 +111,13 @@ }, { "c": "namespace", - "t": "source.cpp meta.using-namespace-declaration.cpp storage.type.cpp", + "t": "source.cpp meta.using-namespace-declaration.cpp keyword.other.namespace.directive.cpp", "r": { - "dark_plus": "storage.type: #569CD6", - "light_plus": "storage.type: #0000FF", - "dark_vs": "storage.type: #569CD6", - "light_vs": "storage.type: #0000FF", - "hc_black": "storage.type: #569CD6" + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" } }, { @@ -133,7 +133,7 @@ }, { "c": "std", - "t": "source.cpp meta.using-namespace-declaration.cpp entity.name.type.cpp", + "t": "source.cpp meta.using-namespace-declaration.cpp entity.name.type.namespace.cpp", "r": { "dark_plus": "entity.name.type: #4EC9B0", "light_plus": "entity.name.type: #267F99", @@ -144,7 +144,7 @@ }, { "c": ";", - "t": "source.cpp meta.using-namespace-declaration.cpp", + "t": "source.cpp meta.using-namespace-declaration.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -155,7 +155,7 @@ }, { "c": "class", - "t": "source.cpp meta.class-struct-block.cpp storage.type.cpp", + "t": "source.cpp meta.class-struct-block.cpp storage.type.class.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -221,7 +221,7 @@ }, { "c": "int", - "t": "source.cpp meta.class-struct-block.cpp storage.type.c", + "t": "source.cpp meta.class-struct-block.cpp storage.type.language.primitive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -243,7 +243,7 @@ }, { "c": ",", - "t": "source.cpp meta.class-struct-block.cpp punctuation.separator.delimiter.c", + "t": "source.cpp meta.class-struct-block.cpp punctuation.separator.delimiter.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -265,7 +265,7 @@ }, { "c": ";", - "t": "source.cpp meta.class-struct-block.cpp punctuation.terminator.statement.c", + "t": "source.cpp meta.class-struct-block.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -287,13 +287,13 @@ }, { "c": "public:", - "t": "source.cpp meta.class-struct-block.cpp storage.modifier.cpp", + "t": "source.cpp meta.class-struct-block.cpp storage.type.modifier.access.control.public.cpp", "r": { - "dark_plus": "storage.modifier: #569CD6", - "light_plus": "storage.modifier: #0000FF", - "dark_vs": "storage.modifier: #569CD6", - "light_vs": "storage.modifier: #0000FF", - "hc_black": "storage.modifier: #569CD6" + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" } }, { @@ -309,7 +309,7 @@ }, { "c": "void", - "t": "source.cpp meta.class-struct-block.cpp storage.type.c", + "t": "source.cpp meta.class-struct-block.cpp storage.type.language.primitive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -331,7 +331,7 @@ }, { "c": "set_values", - "t": "source.cpp meta.class-struct-block.cpp meta.function.c entity.name.function.c", + "t": "source.cpp meta.class-struct-block.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp entity.name.function.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -342,7 +342,7 @@ }, { "c": " ", - "t": "source.cpp meta.class-struct-block.cpp meta.function.c", + "t": "source.cpp meta.class-struct-block.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -353,7 +353,7 @@ }, { "c": "(", - "t": "source.cpp meta.class-struct-block.cpp meta.function.c punctuation.section.parameters.begin.bracket.round.c", + "t": "source.cpp meta.class-struct-block.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.begin.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -364,7 +364,7 @@ }, { "c": "int", - "t": "source.cpp meta.class-struct-block.cpp meta.function.c storage.type.c", + "t": "source.cpp meta.class-struct-block.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp storage.type.language.primitive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -375,7 +375,7 @@ }, { "c": ",", - "t": "source.cpp meta.class-struct-block.cpp meta.function.c punctuation.separator.delimiter.c", + "t": "source.cpp meta.class-struct-block.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp punctuation.separator.delimiter.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -386,7 +386,7 @@ }, { "c": "int", - "t": "source.cpp meta.class-struct-block.cpp meta.function.c storage.type.c", + "t": "source.cpp meta.class-struct-block.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp storage.type.language.primitive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -397,7 +397,7 @@ }, { "c": ")", - "t": "source.cpp meta.class-struct-block.cpp meta.function.c punctuation.section.parameters.end.bracket.round.c", + "t": "source.cpp meta.class-struct-block.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.end.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -408,7 +408,7 @@ }, { "c": ";", - "t": "source.cpp meta.class-struct-block.cpp punctuation.terminator.statement.c", + "t": "source.cpp meta.class-struct-block.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -430,7 +430,7 @@ }, { "c": "int", - "t": "source.cpp meta.class-struct-block.cpp storage.type.c", + "t": "source.cpp meta.class-struct-block.cpp storage.type.language.primitive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -452,7 +452,7 @@ }, { "c": "area", - "t": "source.cpp meta.class-struct-block.cpp meta.function.c entity.name.function.c", + "t": "source.cpp meta.class-struct-block.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp entity.name.function.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -463,7 +463,7 @@ }, { "c": "(", - "t": "source.cpp meta.class-struct-block.cpp meta.function.c punctuation.section.parameters.begin.bracket.round.c", + "t": "source.cpp meta.class-struct-block.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.begin.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -474,7 +474,7 @@ }, { "c": ")", - "t": "source.cpp meta.class-struct-block.cpp meta.function.c punctuation.section.parameters.end.bracket.round.c", + "t": "source.cpp meta.class-struct-block.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.end.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -496,7 +496,7 @@ }, { "c": "{", - "t": "source.cpp meta.class-struct-block.cpp meta.block.c punctuation.section.block.begin.bracket.curly.c", + "t": "source.cpp meta.class-struct-block.cpp meta.block.cpp punctuation.section.block.begin.bracket.curly.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -507,7 +507,7 @@ }, { "c": "return", - "t": "source.cpp meta.class-struct-block.cpp meta.block.c keyword.control.c", + "t": "source.cpp meta.class-struct-block.cpp meta.block.cpp keyword.control.return.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -518,7 +518,7 @@ }, { "c": " width", - "t": "source.cpp meta.class-struct-block.cpp meta.block.c", + "t": "source.cpp meta.class-struct-block.cpp meta.block.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -529,7 +529,7 @@ }, { "c": "*", - "t": "source.cpp meta.class-struct-block.cpp meta.block.c keyword.operator.c", + "t": "source.cpp meta.class-struct-block.cpp meta.block.cpp keyword.operator.cpp", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -540,7 +540,7 @@ }, { "c": "height", - "t": "source.cpp meta.class-struct-block.cpp meta.block.c", + "t": "source.cpp meta.class-struct-block.cpp meta.block.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -551,7 +551,7 @@ }, { "c": ";", - "t": "source.cpp meta.class-struct-block.cpp meta.block.c punctuation.terminator.statement.c", + "t": "source.cpp meta.class-struct-block.cpp meta.block.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -562,7 +562,7 @@ }, { "c": "}", - "t": "source.cpp meta.class-struct-block.cpp meta.block.c punctuation.section.block.end.bracket.curly.c", + "t": "source.cpp meta.class-struct-block.cpp meta.block.cpp punctuation.section.block.end.bracket.curly.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -584,7 +584,7 @@ }, { "c": ";", - "t": "source.cpp punctuation.terminator.statement.c", + "t": "source.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -595,7 +595,7 @@ }, { "c": "void", - "t": "source.cpp storage.type.c", + "t": "source.cpp storage.type.language.primitive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -606,7 +606,7 @@ }, { "c": " ", - "t": "source.cpp", + "t": "source.cpp punctuation.separator.namespace.access.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -616,8 +616,30 @@ } }, { - "c": "Rectangle::set_values", - "t": "source.cpp meta.function.c entity.name.function.c", + "c": "Rectangle", + "t": "source.cpp punctuation.separator.namespace.access.cpp entity.scope.name.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "::", + "t": "source.cpp punctuation.separator.namespace.access.cpp punctuation.separator.namespace.access.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "set_values", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp entity.name.function.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -628,7 +650,7 @@ }, { "c": " ", - "t": "source.cpp meta.function.c", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -639,7 +661,7 @@ }, { "c": "(", - "t": "source.cpp meta.function.c punctuation.section.parameters.begin.bracket.round.c", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.begin.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -650,7 +672,7 @@ }, { "c": "int", - "t": "source.cpp meta.function.c storage.type.c", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp storage.type.language.primitive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -660,8 +682,8 @@ } }, { - "c": " x", - "t": "source.cpp meta.function.c", + "c": " ", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -670,9 +692,20 @@ "hc_black": "default: #FFFFFF" } }, + { + "c": "x", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp variable.parameter.probably.cpp", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, { "c": ",", - "t": "source.cpp meta.function.c punctuation.separator.delimiter.c", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp punctuation.separator.delimiter.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -683,7 +716,7 @@ }, { "c": " ", - "t": "source.cpp meta.function.c", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -694,7 +727,7 @@ }, { "c": "int", - "t": "source.cpp meta.function.c storage.type.c", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp storage.type.language.primitive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -704,8 +737,8 @@ } }, { - "c": " y", - "t": "source.cpp meta.function.c", + "c": " ", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -714,9 +747,20 @@ "hc_black": "default: #FFFFFF" } }, + { + "c": "y", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp variable.parameter.probably.cpp", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, { "c": ")", - "t": "source.cpp meta.function.c punctuation.section.parameters.end.bracket.round.c", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.end.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -738,7 +782,7 @@ }, { "c": "{", - "t": "source.cpp meta.block.c punctuation.section.block.begin.bracket.curly.c", + "t": "source.cpp meta.block.cpp punctuation.section.block.begin.bracket.curly.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -749,7 +793,7 @@ }, { "c": " width ", - "t": "source.cpp meta.block.c", + "t": "source.cpp meta.block.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -760,7 +804,7 @@ }, { "c": "=", - "t": "source.cpp meta.block.c keyword.operator.assignment.c", + "t": "source.cpp meta.block.cpp keyword.operator.assignment.cpp", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -771,7 +815,7 @@ }, { "c": " x", - "t": "source.cpp meta.block.c", + "t": "source.cpp meta.block.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -782,7 +826,7 @@ }, { "c": ";", - "t": "source.cpp meta.block.c punctuation.terminator.statement.c", + "t": "source.cpp meta.block.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -793,7 +837,7 @@ }, { "c": " height ", - "t": "source.cpp meta.block.c", + "t": "source.cpp meta.block.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -804,7 +848,7 @@ }, { "c": "=", - "t": "source.cpp meta.block.c keyword.operator.assignment.c", + "t": "source.cpp meta.block.cpp keyword.operator.assignment.cpp", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -815,7 +859,7 @@ }, { "c": " y", - "t": "source.cpp meta.block.c", + "t": "source.cpp meta.block.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -826,7 +870,7 @@ }, { "c": ";", - "t": "source.cpp meta.block.c punctuation.terminator.statement.c", + "t": "source.cpp meta.block.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -837,7 +881,7 @@ }, { "c": "}", - "t": "source.cpp meta.block.c punctuation.section.block.end.bracket.curly.c", + "t": "source.cpp meta.block.cpp punctuation.section.block.end.bracket.curly.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -848,7 +892,7 @@ }, { "c": "int", - "t": "source.cpp storage.type.c", + "t": "source.cpp storage.type.language.primitive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -870,7 +914,7 @@ }, { "c": "main", - "t": "source.cpp meta.function.c entity.name.function.c", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp entity.name.function.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -881,7 +925,7 @@ }, { "c": " ", - "t": "source.cpp meta.function.c", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -892,7 +936,7 @@ }, { "c": "(", - "t": "source.cpp meta.function.c punctuation.section.parameters.begin.bracket.round.c", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.begin.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -903,7 +947,7 @@ }, { "c": ")", - "t": "source.cpp meta.function.c punctuation.section.parameters.end.bracket.round.c", + "t": "source.cpp meta.function.definition.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.end.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -925,7 +969,7 @@ }, { "c": "{", - "t": "source.cpp meta.block.c punctuation.section.block.begin.bracket.curly.c", + "t": "source.cpp meta.block.cpp punctuation.section.block.begin.bracket.curly.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -936,7 +980,7 @@ }, { "c": " Rectangle rect", - "t": "source.cpp meta.block.c", + "t": "source.cpp meta.block.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -947,271 +991,7 @@ }, { "c": ";", - "t": "source.cpp meta.block.c punctuation.terminator.statement.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " rect", - "t": "source.cpp meta.block.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ".", - "t": "source.cpp meta.block.c punctuation.separator.dot-access.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "set_values", - "t": "source.cpp meta.block.c meta.function-call.c entity.name.function.c", - "r": { - "dark_plus": "entity.name.function: #DCDCAA", - "light_plus": "entity.name.function: #795E26", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.function: #DCDCAA" - } - }, - { - "c": " ", - "t": "source.cpp meta.block.c meta.function-call.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "(", - "t": "source.cpp meta.block.c meta.function-call.c punctuation.section.arguments.begin.bracket.round.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "3", - "t": "source.cpp meta.block.c meta.function-call.c constant.numeric.c", - "r": { - "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", - "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", - "hc_black": "constant.numeric: #B5CEA8" - } - }, - { - "c": ",", - "t": "source.cpp meta.block.c meta.function-call.c punctuation.separator.delimiter.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "4", - "t": "source.cpp meta.block.c meta.function-call.c constant.numeric.c", - "r": { - "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", - "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", - "hc_black": "constant.numeric: #B5CEA8" - } - }, - { - "c": ")", - "t": "source.cpp meta.block.c meta.function-call.c punctuation.section.arguments.end.bracket.round.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ";", - "t": "source.cpp meta.block.c punctuation.terminator.statement.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " cout ", - "t": "source.cpp meta.block.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "<<", - "t": "source.cpp meta.block.c keyword.operator.bitwise.shift.c", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": " ", - "t": "source.cpp meta.block.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "\"", - "t": "source.cpp meta.block.c string.quoted.double.cpp punctuation.definition.string.begin.cpp", - "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" - } - }, - { - "c": "area: ", - "t": "source.cpp meta.block.c string.quoted.double.cpp", - "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" - } - }, - { - "c": "\"", - "t": "source.cpp meta.block.c string.quoted.double.cpp punctuation.definition.string.end.cpp", - "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" - } - }, - { - "c": " ", - "t": "source.cpp meta.block.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "<<", - "t": "source.cpp meta.block.c keyword.operator.bitwise.shift.c", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": " rect", - "t": "source.cpp meta.block.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ".", - "t": "source.cpp meta.block.c punctuation.separator.dot-access.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "area", - "t": "source.cpp meta.block.c meta.function-call.c entity.name.function.c", - "r": { - "dark_plus": "entity.name.function: #DCDCAA", - "light_plus": "entity.name.function: #795E26", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.function: #DCDCAA" - } - }, - { - "c": "(", - "t": "source.cpp meta.block.c meta.function-call.c punctuation.section.arguments.begin.bracket.round.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ")", - "t": "source.cpp meta.block.c meta.function-call.c punctuation.section.arguments.end.bracket.round.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ";", - "t": "source.cpp meta.block.c punctuation.terminator.statement.c", + "t": "source.cpp meta.block.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1222,7 +1002,293 @@ }, { "c": " ", - "t": "source.cpp meta.block.c", + "t": "source.cpp meta.block.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "rect", + "t": "source.cpp meta.block.cpp variable.object.access.cpp variable.object.cpp", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ".", + "t": "source.cpp meta.block.cpp variable.object.access.cpp punctuation.separator.dot-access.cpp", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "set_values", + "t": "source.cpp meta.block.cpp variable.object.access.cpp variable.other.member.cpp", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": " ", + "t": "source.cpp meta.block.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "(", + "t": "source.cpp meta.block.cpp meta.block.parens.cpp punctuation.section.parens.begin.bracket.round.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "3", + "t": "source.cpp meta.block.cpp meta.block.parens.cpp constant.numeric.cpp", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": ",", + "t": "source.cpp meta.block.cpp meta.block.parens.cpp punctuation.separator.delimiter.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "4", + "t": "source.cpp meta.block.cpp meta.block.parens.cpp constant.numeric.cpp", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": ")", + "t": "source.cpp meta.block.cpp meta.block.parens.cpp punctuation.section.parens.end.bracket.round.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ";", + "t": "source.cpp meta.block.cpp punctuation.terminator.statement.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " cout ", + "t": "source.cpp meta.block.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<<", + "t": "source.cpp meta.block.cpp keyword.operator.bitwise.shift.cpp", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.cpp meta.block.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "source.cpp meta.block.cpp string.quoted.double.cpp punctuation.definition.string.begin.cpp", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "area: ", + "t": "source.cpp meta.block.cpp string.quoted.double.cpp", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "source.cpp meta.block.cpp string.quoted.double.cpp punctuation.definition.string.end.cpp", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": " ", + "t": "source.cpp meta.block.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<<", + "t": "source.cpp meta.block.cpp keyword.operator.bitwise.shift.cpp", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.cpp meta.block.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "rect", + "t": "source.cpp meta.block.cpp meta.function-call.member.cpp variable.object.cpp", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ".", + "t": "source.cpp meta.block.cpp meta.function-call.member.cpp punctuation.separator.dot-access.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "area", + "t": "source.cpp meta.block.cpp meta.function-call.member.cpp entity.name.function.member.cpp", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA" + } + }, + { + "c": "(", + "t": "source.cpp meta.block.cpp meta.function-call.member.cpp punctuation.section.arguments.begin.bracket.round.function.member.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ")", + "t": "source.cpp meta.block.cpp meta.function-call.member.cpp punctuation.section.arguments.end.bracket.round.function.member.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ";", + "t": "source.cpp meta.block.cpp punctuation.terminator.statement.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.cpp meta.block.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1233,7 +1299,7 @@ }, { "c": "return", - "t": "source.cpp meta.block.c keyword.control.c", + "t": "source.cpp meta.block.cpp keyword.control.return.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -1244,7 +1310,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.c", + "t": "source.cpp meta.block.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1255,7 +1321,7 @@ }, { "c": "0", - "t": "source.cpp meta.block.c constant.numeric.c", + "t": "source.cpp meta.block.cpp constant.numeric.cpp", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -1266,7 +1332,7 @@ }, { "c": ";", - "t": "source.cpp meta.block.c punctuation.terminator.statement.c", + "t": "source.cpp meta.block.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1277,7 +1343,7 @@ }, { "c": "}", - "t": "source.cpp meta.block.c punctuation.section.block.end.bracket.curly.c", + "t": "source.cpp meta.block.cpp punctuation.section.block.end.bracket.curly.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", diff --git a/extensions/css-language-features/client/src/cssMain.ts b/extensions/css-language-features/client/src/cssMain.ts index 0312498f672..641d11f335b 100644 --- a/extensions/css-language-features/client/src/cssMain.ts +++ b/extensions/css-language-features/client/src/cssMain.ts @@ -9,8 +9,8 @@ import * as fs from 'fs'; import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); -import { languages, window, commands, ExtensionContext, Range, Position, CompletionItem, CompletionItemKind, TextEdit, SnippetString, workspace, TextDocument, SelectionRange, SelectionRangeKind } from 'vscode'; -import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind, Disposable, TextDocumentIdentifier } from 'vscode-languageclient'; +import { languages, window, commands, ExtensionContext, Range, Position, CompletionItem, CompletionItemKind, TextEdit, SnippetString, workspace, TextDocument, SelectionRange } from 'vscode'; +import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind, Disposable } from 'vscode-languageclient'; import { getCustomDataPathsInAllWorkspaces, getCustomDataPathsFromAllExtensions } from './customData'; // this method is called when vs code is activated @@ -83,43 +83,23 @@ export function activate(context: ExtensionContext) { context.subscriptions.push(languages.registerSelectionRangeProvider(selector, { async provideSelectionRanges(document: TextDocument, positions: Position[]): Promise { const textDocument = client.code2ProtocolConverter.asTextDocumentIdentifier(document); - return Promise.all(positions.map(async position => { - const rawRanges = await client.sendRequest('$/textDocument/selectionRange', { textDocument, position }); - if (Array.isArray(rawRanges)) { - return rawRanges.map(r => { + const rawResult = await client.sendRequest('$/textDocument/selectionRanges', { textDocument, positions: positions.map(client.code2ProtocolConverter.asPosition) }); + if (Array.isArray(rawResult)) { + return rawResult.map(rawSelectionRanges => { + return rawSelectionRanges.map(selectionRange => { return { - range: client.protocol2CodeConverter.asRange(r), - kind: SelectionRangeKind.Declaration + range: client.protocol2CodeConverter.asRange(selectionRange.range), + kind: selectionRange.kind }; }); - } - return []; - })); + }); + } + return []; } })); }); }); - const selectionRangeProvider = { - async provideSelectionRanges(document: TextDocument, positions: Position[]): Promise { - const textDocument = TextDocumentIdentifier.create(document.uri.toString()); - return Promise.all(positions.map(async position => { - const rawRanges: Range[] = await client.sendRequest('$/textDocument/selectionRange', { textDocument, position }); - - return rawRanges.map(r => { - const actualRange = new Range(new Position(r.start.line, r.start.character), new Position(r.end.line, r.end.character)); - return { - range: actualRange, - kind: SelectionRangeKind.Declaration - }; - }); - })); - } - }; - documentSelector.forEach(selector => { - languages.registerSelectionRangeProvider(selector, selectionRangeProvider); - }); - function initCompletionProvider(): Disposable { const regionCompletionRegExpr = /^(\s*)(\/(\*\s*(#\w*)?)?)?$/; diff --git a/extensions/css-language-features/package.json b/extensions/css-language-features/package.json index 99b022469d6..ac8a4b23c72 100644 --- a/extensions/css-language-features/package.json +++ b/extensions/css-language-features/package.json @@ -726,7 +726,7 @@ ] }, "dependencies": { - "vscode-languageclient": "^5.1.0", + "vscode-languageclient": "^5.2.1", "vscode-nls": "^4.0.0" }, "devDependencies": { diff --git a/extensions/css-language-features/server/build/filesFillIn.js b/extensions/css-language-features/server/build/filesFillIn.js deleted file mode 100644 index 906617384e0..00000000000 --- a/extensions/css-language-features/server/build/filesFillIn.js +++ /dev/null @@ -1,5 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -module.exports = {}; \ No newline at end of file diff --git a/extensions/css-language-features/server/extension.webpack.config.js b/extensions/css-language-features/server/extension.webpack.config.js index 17dc2d39e34..68b850b3773 100644 --- a/extensions/css-language-features/server/extension.webpack.config.js +++ b/extensions/css-language-features/server/extension.webpack.config.js @@ -9,7 +9,6 @@ const withDefaults = require('../../shared.webpack.config'); const path = require('path'); -var webpack = require('webpack'); module.exports = withDefaults({ context: path.join(__dirname), @@ -19,12 +18,5 @@ module.exports = withDefaults({ output: { filename: 'cssServerMain.js', path: path.join(__dirname, 'dist') - }, - plugins: [ - new webpack.NormalModuleReplacementPlugin( - /[/\\]vscode-languageserver[/\\]lib[/\\]files\.js/, - require.resolve('./build/filesFillIn') - ), - new webpack.IgnorePlugin(/vertx/) - ], + } }); diff --git a/extensions/css-language-features/server/package.json b/extensions/css-language-features/server/package.json index 9bc32c0a788..71e14f56ea2 100644 --- a/extensions/css-language-features/server/package.json +++ b/extensions/css-language-features/server/package.json @@ -9,8 +9,8 @@ }, "main": "./out/cssServerMain", "dependencies": { - "vscode-css-languageservice": "^3.0.13-next.12", - "vscode-languageserver": "^5.1.0" + "vscode-css-languageservice": "^4.0.0-next.3", + "vscode-languageserver": "^5.3.0-next.2" }, "devDependencies": { "@types/mocha": "2.2.33", diff --git a/extensions/css-language-features/server/src/cssServerMain.ts b/extensions/css-language-features/server/src/cssServerMain.ts index 3ef166f1b80..e1c78b158c1 100644 --- a/extensions/css-language-features/server/src/cssServerMain.ts +++ b/extensions/css-language-features/server/src/cssServerMain.ts @@ -335,14 +335,14 @@ connection.onFoldingRanges((params, token) => { }, null, `Error while computing folding ranges for ${params.textDocument.uri}`, token); }); -connection.onRequest('$/textDocument/selectionRange', async (params, token) => { +connection.onRequest('$/textDocument/selectionRanges', async (params, token) => { return runSafe(() => { const document = documents.get(params.textDocument.uri); - const position: Position = params.position; + const positions: Position[] = params.positions; if (document) { const stylesheet = stylesheets.get(document); - return getLanguageService(document).getSelectionRanges(document, position, stylesheet); + return getLanguageService(document).getSelectionRanges(document, positions, stylesheet); } return Promise.resolve(null); }, null, `Error while computing selection ranges for ${params.textDocument.uri}`, token); diff --git a/extensions/css-language-features/server/yarn.lock b/extensions/css-language-features/server/yarn.lock index a2fa388731d..bfbcab4c931 100644 --- a/extensions/css-language-features/server/yarn.lock +++ b/extensions/css-language-features/server/yarn.lock @@ -229,12 +229,12 @@ supports-color@5.4.0: dependencies: has-flag "^3.0.0" -vscode-css-languageservice@^3.0.13-next.12: - version "3.0.13-next.12" - resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-3.0.13-next.12.tgz#8d20828e41bc7dcf44cdba4b2e476393780a7793" - integrity sha512-5B3NYU2DBFhbUvMuTg7kBlc9COHyr/pbR1cDzXGFwemQG8W6ERsgn+eftPHFbcug1kwBjPVSoMgtw/czKpboHQ== +vscode-css-languageservice@^4.0.0-next.3: + version "4.0.0-next.3" + resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-4.0.0-next.3.tgz#e9529f3b4ddf95c9a3e5dc2a6d701a38280ffa98" + integrity sha512-/xmbWpIQLw+HZ/3LsaE2drHFSNJbM9mZ8bKR5NUiu2ZUr10WbGxX0j/GDZB3LlMmdSHQGgRQ5hTM/Ic2PuBDRw== dependencies: - vscode-languageserver-types "^3.13.0" + vscode-languageserver-types "^3.14.0" vscode-nls "^4.0.0" vscode-jsonrpc@^4.0.0: @@ -242,25 +242,25 @@ vscode-jsonrpc@^4.0.0: resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-4.0.0.tgz#a7bf74ef3254d0a0c272fab15c82128e378b3be9" integrity sha512-perEnXQdQOJMTDFNv+UF3h1Y0z4iSiaN9jIlb0OqIYgosPCZGYh/MCUlkFtV2668PL69lRDO32hmvL2yiidUYg== -vscode-languageserver-protocol@3.13.0: - version "3.13.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.13.0.tgz#710d8e42119bb3affb1416e1e104bd6b4d503595" - integrity sha512-2ZGKwI+P2ovQll2PGAp+2UfJH+FK9eait86VBUdkPd9HRlm8e58aYT9pV/NYanHOcp3pL6x2yTLVCFMcTer0mg== +vscode-languageserver-protocol@3.15.0-next.1: + version "3.15.0-next.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.0-next.1.tgz#1e45e224d7eef8c79b4bed75b9dcb1930d2ab8ed" + integrity sha512-LXF0d9s3vxFBxVQ4aKl/XghdEMAncGt3dh4urIYa9Is43g3MfIQL9fC44YZtP+XXOrI2rpZU8lRNN01U1V6CDg== dependencies: vscode-jsonrpc "^4.0.0" - vscode-languageserver-types "3.13.0" + vscode-languageserver-types "3.14.0" -vscode-languageserver-types@3.13.0, vscode-languageserver-types@^3.13.0: - version "3.13.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.13.0.tgz#b704b024cef059f7b326611c99b9c8753c0a18b4" - integrity sha512-BnJIxS+5+8UWiNKCP7W3g9FlE7fErFw0ofP5BXJe7c2tl0VeWh+nNHFbwAS2vmVC4a5kYxHBjRy0UeOtziemVA== +vscode-languageserver-types@3.14.0, vscode-languageserver-types@^3.14.0: + version "3.14.0" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.14.0.tgz#d3b5952246d30e5241592b6dde8280e03942e743" + integrity sha512-lTmS6AlAlMHOvPQemVwo3CezxBp0sNB95KNPkqp3Nxd5VFEnuG1ByM0zlRWos0zjO3ZWtkvhal0COgiV1xIA4A== -vscode-languageserver@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-5.1.0.tgz#012a28f154cc7a848c443d217894942e4c3eeb39" - integrity sha512-CIsrgx2Y5VHS317g/HwkSTWYBIQmy0DwEyZPmB2pEpVOhYFwVsYpbiJwHIIyLQsQtmRaO4eA2xM8KPjNSdXpBw== +vscode-languageserver@^5.3.0-next.2: + version "5.3.0-next.2" + resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-5.3.0-next.2.tgz#31ce4c34d68b517b400ca9e211e43f8d868b8dcc" + integrity sha512-n5onRw9naMrRHp2jnOn+ZwN1n+tTfzftWLPonjp1FWf/iCZWIlnw2TyF/Hn+SDGhLoVtoghmxhwEQaxEAfLHvw== dependencies: - vscode-languageserver-protocol "3.13.0" + vscode-languageserver-protocol "3.15.0-next.1" vscode-uri "^1.0.6" vscode-nls@^4.0.0: diff --git a/extensions/css-language-features/yarn.lock b/extensions/css-language-features/yarn.lock index 6314214090a..385d59738dd 100644 --- a/extensions/css-language-features/yarn.lock +++ b/extensions/css-language-features/yarn.lock @@ -167,26 +167,26 @@ vscode-jsonrpc@^4.0.0: resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-4.0.0.tgz#a7bf74ef3254d0a0c272fab15c82128e378b3be9" integrity sha512-perEnXQdQOJMTDFNv+UF3h1Y0z4iSiaN9jIlb0OqIYgosPCZGYh/MCUlkFtV2668PL69lRDO32hmvL2yiidUYg== -vscode-languageclient@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-5.1.0.tgz#650ab0dc9fd0daaade058a8471aaff5bc3f9580e" - integrity sha512-Z95Kps8UqD4o17HE3uCkZuvenOsxHVH46dKmaGVpGixEFZigPaVuVxLM/JWeIY9aRenoC0ZD9CK1O7L4jpffKg== +vscode-languageclient@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-5.2.1.tgz#7cfc83a294c409f58cfa2b910a8cfeaad0397193" + integrity sha512-7jrS/9WnV0ruqPamN1nE7qCxn0phkH5LjSgSp9h6qoJGoeAKzwKz/PF6M+iGA/aklx4GLZg1prddhEPQtuXI1Q== dependencies: semver "^5.5.0" - vscode-languageserver-protocol "3.13.0" + vscode-languageserver-protocol "3.14.1" -vscode-languageserver-protocol@3.13.0: - version "3.13.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.13.0.tgz#710d8e42119bb3affb1416e1e104bd6b4d503595" - integrity sha512-2ZGKwI+P2ovQll2PGAp+2UfJH+FK9eait86VBUdkPd9HRlm8e58aYT9pV/NYanHOcp3pL6x2yTLVCFMcTer0mg== +vscode-languageserver-protocol@3.14.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.14.1.tgz#b8aab6afae2849c84a8983d39a1cf742417afe2f" + integrity sha512-IL66BLb2g20uIKog5Y2dQ0IiigW0XKrvmWiOvc0yXw80z3tMEzEnHjaGAb3ENuU7MnQqgnYJ1Cl2l9RvNgDi4g== dependencies: vscode-jsonrpc "^4.0.0" - vscode-languageserver-types "3.13.0" + vscode-languageserver-types "3.14.0" -vscode-languageserver-types@3.13.0: - version "3.13.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.13.0.tgz#b704b024cef059f7b326611c99b9c8753c0a18b4" - integrity sha512-BnJIxS+5+8UWiNKCP7W3g9FlE7fErFw0ofP5BXJe7c2tl0VeWh+nNHFbwAS2vmVC4a5kYxHBjRy0UeOtziemVA== +vscode-languageserver-types@3.14.0: + version "3.14.0" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.14.0.tgz#d3b5952246d30e5241592b6dde8280e03942e743" + integrity sha512-lTmS6AlAlMHOvPQemVwo3CezxBp0sNB95KNPkqp3Nxd5VFEnuG1ByM0zlRWos0zjO3ZWtkvhal0COgiV1xIA4A== vscode-nls@^4.0.0: version "4.0.0" diff --git a/extensions/debug-server-ready/package.json b/extensions/debug-server-ready/package.json index 7d84a8e27d5..ee66be8012d 100644 --- a/extensions/debug-server-ready/package.json +++ b/extensions/debug-server-ready/package.json @@ -23,41 +23,77 @@ "launch": { "properties": { "serverReadyAction": { - "type": "object", - "markdownDescription": "%debug.server.ready.serverReadyAction.description%", - "default": { - "action": "openExternally" - }, - "properties": { - "pattern": { - "type": "string", - "markdownDescription": "%debug.server.ready.pattern.description%", - "default": "listening on port ([0-9]+)" + "oneOf": [ + { + "type": "object", + "additionalProperties": false, + "markdownDescription": "%debug.server.ready.serverReadyAction.description%", + "default": { + "action": "openExternally" + }, + "properties": { + "action": { + "type": "string", + "enum": [ + "openExternally" + ], + "enumDescriptions": [ + "%debug.server.ready.action.openExternally.description%", + "%debug.server.ready.action.debugWithChrome.description%" + ], + "markdownDescription": "%debug.server.ready.action.description%", + "default": "openExternally" + }, + "pattern": { + "type": "string", + "markdownDescription": "%debug.server.ready.pattern.description%", + "default": "listening on port ([0-9]+)" + }, + "uriFormat": { + "type": "string", + "markdownDescription": "%debug.server.ready.uriFormat.description%", + "default": "http://localhost:%s" + } + } }, - "uriFormat": { - "type": "string", - "markdownDescription": "%debug.server.ready.uriFormat.description%", - "default": "http://localhost:%s" - }, - "action": { - "type": "string", - "enum": [ - "openExternally", - "debugWithChrome" - ], - "enumDescriptions": [ - "%debug.server.ready.action.openExternally.description%", - "%debug.server.ready.action.debugWithChrome.description%" - ], - "markdownDescription": "%debug.server.ready.action.description%", - "default": "openExternally" - }, - "webRoot": { - "type": "string", - "markdownDescription": "%debug.server.ready.webRoot.description%", - "default": "${workspaceFolder}" + { + "type": "object", + "additionalProperties": false, + "markdownDescription": "%debug.server.ready.serverReadyAction.description%", + "default": { + "action": "openExternally" + }, + "properties": { + "action": { + "type": "string", + "enum": [ + "debugWithChrome" + ], + "enumDescriptions": [ + "%debug.server.ready.action.openExternally.description%", + "%debug.server.ready.action.debugWithChrome.description%" + ], + "markdownDescription": "%debug.server.ready.action.description%", + "default": "openExternally" + }, + "pattern": { + "type": "string", + "markdownDescription": "%debug.server.ready.pattern.description%", + "default": "listening on port ([0-9]+)" + }, + "uriFormat": { + "type": "string", + "markdownDescription": "%debug.server.ready.uriFormat.description%", + "default": "http://localhost:%s" + }, + "webRoot": { + "type": "string", + "markdownDescription": "%debug.server.ready.webRoot.description%", + "default": "${workspaceFolder}" + } + } } - } + ] } } } diff --git a/extensions/debug-server-ready/src/extension.ts b/extensions/debug-server-ready/src/extension.ts index 1aecc7ec852..413c4c3529f 100644 --- a/extensions/debug-server-ready/src/extension.ts +++ b/extensions/debug-server-ready/src/extension.ts @@ -7,7 +7,6 @@ import * as vscode from 'vscode'; //import * as nls from 'vscode-nls'; import * as util from 'util'; -const trackers = new Set(); const PATTERN = 'listening on.* (https?://\\S+|[0-9]+)'; // matches "listening on port 3000" or "Now listening on: https://localhost:5001" const URI_FORMAT = 'http://localhost:%s'; @@ -20,13 +19,123 @@ interface ServerReadyAction { webRoot?: string; } +class ServerReadyDetector extends vscode.Disposable { + static detectors = new Map(); + + private hasFired = false; + private regexp: RegExp; + private disposables: vscode.Disposable[] = []; + + static start(session: vscode.DebugSession): ServerReadyDetector | undefined { + if (session.configuration.serverReadyAction) { + let detector = ServerReadyDetector.detectors.get(session); + if (!detector) { + detector = new ServerReadyDetector(session); + ServerReadyDetector.detectors.set(session, detector); + } + return detector; + } + return undefined; + } + + static stop(session: vscode.DebugSession): void { + let detector = ServerReadyDetector.detectors.get(session); + if (detector) { + ServerReadyDetector.detectors.delete(session); + detector.dispose(); + } + } + + private constructor(private session: vscode.DebugSession) { + super(() => this.internalDispose()); + + this.regexp = new RegExp(session.configuration.serverReadyAction.pattern || PATTERN); + } + + private internalDispose() { + this.disposables.forEach(d => d.dispose()); + this.disposables = []; + } + + trackTerminals() { + // TODO: listen only on the Terminal associated with the debug session + vscode.window.terminals.forEach(terminal => { + this.disposables.push(terminal.onDidWriteData(s => { + this.detectPattern(s); + })); + }); + } + + detectPattern(s: string): void { + + if (!this.hasFired) { + const result = this.regexp.exec(s); + if (result && result.length === 2) { + this.openExternalWithString(this.session, result[1]); + this.hasFired = true; + this.internalDispose(); + } + } + } + + private openExternalWithString(session: vscode.DebugSession, portOrUriString: string) { + + if (portOrUriString) { + if (/^[0-9]+$/.test(portOrUriString)) { + const args: ServerReadyAction = session.configuration.serverReadyAction; + portOrUriString = util.format(args.uriFormat || URI_FORMAT, portOrUriString); + } + this.openExternalWithUri(session, portOrUriString); + } + } + + private openExternalWithUri(session: vscode.DebugSession, uri: string) { + + const args: ServerReadyAction = session.configuration.serverReadyAction; + switch (args.action || 'openExternally') { + case 'openExternally': + vscode.env.openExternal(vscode.Uri.parse(uri)); + break; + case 'debugWithChrome': + vscode.debug.startDebugging(session.workspaceFolder, { + type: 'chrome', + name: 'Chrome Debug', + request: 'launch', + url: uri, + webRoot: args.webRoot || WEB_ROOT + }); + break; + default: + // not supported + break; + } + } +} + export function activate(context: vscode.ExtensionContext) { + context.subscriptions.push(vscode.debug.onDidChangeActiveDebugSession(session => { + if (session && session.configuration.serverReadyAction) { + const detector = ServerReadyDetector.start(session); + if (detector) { + detector.trackTerminals(); + } + } + })); + + context.subscriptions.push(vscode.debug.onDidTerminateDebugSession(session => { + ServerReadyDetector.stop(session); + })); + + const trackers = new Set(); + context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider('*', { resolveDebugConfiguration(_folder: vscode.WorkspaceFolder | undefined, debugConfiguration: vscode.DebugConfiguration) { - const args: ServerReadyAction = debugConfiguration.serverReadyAction; - if (debugConfiguration.type && args) { - startTrackerForType(context, debugConfiguration.type); + if (debugConfiguration.type && debugConfiguration.serverReadyAction) { + if (!trackers.has(debugConfiguration.type)) { + trackers.add(debugConfiguration.type); + startTrackerForType(context, debugConfiguration.type); + } } return debugConfiguration; } @@ -35,63 +144,20 @@ export function activate(context: vscode.ExtensionContext) { function startTrackerForType(context: vscode.ExtensionContext, type: string) { - if (!trackers.has(type)) { - trackers.add(type); - - // scan debug console output for a PORT message - context.subscriptions.push(vscode.debug.registerDebugAdapterTrackerFactory(type, { - createDebugAdapterTracker(session: vscode.DebugSession) { - const args: ServerReadyAction = session.configuration.serverReadyAction; - if (args) { - const regexp = new RegExp(args.pattern || PATTERN); - let hasFired = false; - return { - onDidSendMessage: m => { - if (!hasFired && m.type === 'event' && m.event === 'output' && m.body.output) { - const result = regexp.exec(m.body.output); - if (result && result.length === 2) { - openExternalWithString(session, result[1]); - hasFired = true; - } - } + // scan debug console output for a PORT message + context.subscriptions.push(vscode.debug.registerDebugAdapterTrackerFactory(type, { + createDebugAdapterTracker(session: vscode.DebugSession) { + const detector = ServerReadyDetector.start(session); + if (detector) { + return { + onDidSendMessage: m => { + if (m.type === 'event' && m.event === 'output' && m.body.output) { + detector.detectPattern(m.body.output); } - }; - } - return undefined; + } + }; } - })); - } -} - -function openExternalWithString(session: vscode.DebugSession, portOrUriString: string) { - - if (portOrUriString) { - if (/^[0-9]+$/.test(portOrUriString)) { - const args: ServerReadyAction = session.configuration.serverReadyAction; - portOrUriString = util.format(args.uriFormat || URI_FORMAT, portOrUriString); + return undefined; } - openExternalWithUri(session, portOrUriString); - } -} - -function openExternalWithUri(session: vscode.DebugSession, uri: string) { - - const args: ServerReadyAction = session.configuration.serverReadyAction; - switch (args.action || 'openExternally') { - case 'openExternally': - vscode.env.openExternal(vscode.Uri.parse(uri)); - break; - case 'debugWithChrome': - vscode.debug.startDebugging(session.workspaceFolder, { - type: 'chrome', - name: 'Chrome Debug', - request: 'launch', - url: uri, - webRoot: args.webRoot || WEB_ROOT - }); - break; - default: - // not supported - break; - } + })); } diff --git a/extensions/debug-server-ready/src/typings/ref.d.ts b/extensions/debug-server-ready/src/typings/ref.d.ts index bc057c55878..954bab971e3 100644 --- a/extensions/debug-server-ready/src/typings/ref.d.ts +++ b/extensions/debug-server-ready/src/typings/ref.d.ts @@ -4,4 +4,5 @@ *--------------------------------------------------------------------------------------------*/ /// +/// /// diff --git a/extensions/git/package.json b/extensions/git/package.json index 834563c3102..f858863dd7b 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -1150,6 +1150,7 @@ "%config.postCommitCommand.sync%" ], "markdownDescription": "%config.postCommitCommand%", + "scope": "resource", "default": "none" }, "git.showInlineOpenFileAction": { diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 8344525db3d..8e798b0cc77 100755 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -649,14 +649,16 @@ export class CommandCenter { if (!(resource instanceof Resource)) { // can happen when called from a keybinding + console.log('WHAT'); resource = this.getSCMResource(); } if (resource) { - const resources = ([resource, ...resourceStates] as Resource[]) - .filter(r => r.type !== Status.DELETED && r.type !== Status.INDEX_DELETED); - - uris = resources.map(r => r.resourceUri); + uris = ([resource, ...resourceStates] as Resource[]) + .filter(r => r.type !== Status.DELETED && r.type !== Status.INDEX_DELETED) + .map(r => r.resourceUri); + } else if (window.activeTextEditor) { + uris = [window.activeTextEditor.document.uri]; } } @@ -665,6 +667,7 @@ export class CommandCenter { } const activeTextEditor = window.activeTextEditor; + for (const uri of uris) { const opts: TextDocumentShowOptions = { preserveFocus, @@ -2117,6 +2120,7 @@ export class CommandCenter { uri = uri ? uri : (window.activeTextEditor && window.activeTextEditor.document.uri); this.outputChannel.appendLine(`git.getSCMResource.uri ${uri && uri.toString()}`); + for (const r of this.model.repositories.map(r => r.root)) { this.outputChannel.appendLine(`repo root ${r}`); } diff --git a/extensions/git/src/decorationProvider.ts b/extensions/git/src/decorationProvider.ts index 0c27365bc51..9f17b20ee8e 100644 --- a/extensions/git/src/decorationProvider.ts +++ b/extensions/git/src/decorationProvider.ts @@ -20,7 +20,6 @@ class GitIgnoreDecorationProvider implements DecorationProvider { private disposables: Disposable[] = []; constructor(private model: Model) { - //todo@joh -> events when the ignore status actually changes, not only when the file changes this.onDidChangeDecorations = fireEvent(anyEvent( filterEvent(workspace.onDidSaveTextDocument, e => e.fileName.endsWith('.gitignore')), model.onDidOpenRepository, @@ -119,7 +118,7 @@ class GitDecorationProvider implements DecorationProvider { const uris = new Set([...this.decorations.keys()].concat([...newDecorations.keys()])); this.decorations = newDecorations; - this._onDidChangeDecorations.fire([...uris.values()].map(Uri.parse)); + this._onDidChangeDecorations.fire([...uris.values()].map(value => Uri.parse(value, true))); } private collectDecorationData(group: GitResourceGroup, bucket: Map): void { diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 388a1f28043..2fac9e2a39f 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -206,7 +206,7 @@ export class Resource implements SourceControlResourceState { case Status.INDEX_ADDED: case Status.INTENT_TO_ADD: return new ThemeColor('gitDecoration.addedResourceForeground'); - case Status.INDEX_RENAMED: // todo@joh - special color? + case Status.INDEX_RENAMED: case Status.UNTRACKED: return new ThemeColor('gitDecoration.untrackedResourceForeground'); case Status.IGNORED: diff --git a/extensions/html-language-features/client/src/htmlMain.ts b/extensions/html-language-features/client/src/htmlMain.ts index 696bc074c84..27f3ad2d626 100644 --- a/extensions/html-language-features/client/src/htmlMain.ts +++ b/extensions/html-language-features/client/src/htmlMain.ts @@ -8,7 +8,7 @@ import * as fs from 'fs'; import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); -import { languages, ExtensionContext, IndentAction, Position, TextDocument, Range, CompletionItem, CompletionItemKind, SnippetString, workspace, SelectionRange, SelectionRangeKind } from 'vscode'; +import { languages, ExtensionContext, IndentAction, Position, TextDocument, Range, CompletionItem, CompletionItemKind, SnippetString, workspace, SelectionRange } from 'vscode'; import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind, RequestType, TextDocumentPositionParams } from 'vscode-languageclient'; import { EMPTY_ELEMENTS } from './htmlEmptyTagsShared'; import { activateTagClosing } from './tagClosing'; @@ -92,18 +92,18 @@ export function activate(context: ExtensionContext) { context.subscriptions.push(languages.registerSelectionRangeProvider(selector, { async provideSelectionRanges(document: TextDocument, positions: Position[]): Promise { const textDocument = client.code2ProtocolConverter.asTextDocumentIdentifier(document); - return Promise.all(positions.map(async position => { - const rawRanges = await client.sendRequest('$/textDocument/selectionRange', { textDocument, position }); - if (Array.isArray(rawRanges)) { - return rawRanges.map(r => { + const rawResult = await client.sendRequest('$/textDocument/selectionRanges', { textDocument, positions: positions.map(client.code2ProtocolConverter.asPosition) }); + if (Array.isArray(rawResult)) { + return rawResult.map(rawSelectionRanges => { + return rawSelectionRanges.map(selectionRange => { return { - range: client.protocol2CodeConverter.asRange(r), - kind: SelectionRangeKind.Declaration + range: client.protocol2CodeConverter.asRange(selectionRange.range), + kind: selectionRange.kind }; }); - } - return []; - })); + }); + } + return []; } })); }); diff --git a/extensions/html-language-features/package.json b/extensions/html-language-features/package.json index 64fe111d64b..e44dc9f7102 100644 --- a/extensions/html-language-features/package.json +++ b/extensions/html-language-features/package.json @@ -176,7 +176,7 @@ }, "dependencies": { "vscode-extension-telemetry": "0.1.1", - "vscode-languageclient": "^5.1.0", + "vscode-languageclient": "^5.2.1", "vscode-nls": "^4.0.0" }, "devDependencies": { diff --git a/extensions/html-language-features/server/build/filesFillIn.js b/extensions/html-language-features/server/build/filesFillIn.js deleted file mode 100644 index 906617384e0..00000000000 --- a/extensions/html-language-features/server/build/filesFillIn.js +++ /dev/null @@ -1,5 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -module.exports = {}; \ No newline at end of file diff --git a/extensions/html-language-features/server/extension.webpack.config.js b/extensions/html-language-features/server/extension.webpack.config.js index a535ddeae9a..77b86e718b1 100644 --- a/extensions/html-language-features/server/extension.webpack.config.js +++ b/extensions/html-language-features/server/extension.webpack.config.js @@ -9,7 +9,6 @@ const withDefaults = require('../../shared.webpack.config'); const path = require('path'); -var webpack = require('webpack'); module.exports = withDefaults({ context: path.join(__dirname), @@ -22,12 +21,5 @@ module.exports = withDefaults({ }, externals: { 'typescript': 'commonjs typescript' - }, - plugins: [ - new webpack.NormalModuleReplacementPlugin( - /[/\\]vscode-languageserver[/\\]lib[/\\]files\.js/, - require.resolve('./build/filesFillIn') - ), - new webpack.IgnorePlugin(/vertx/) - ], + } }); diff --git a/extensions/html-language-features/server/package.json b/extensions/html-language-features/server/package.json index bfc23ebe0bc..b01c03a3f15 100644 --- a/extensions/html-language-features/server/package.json +++ b/extensions/html-language-features/server/package.json @@ -9,10 +9,10 @@ }, "main": "./out/htmlServerMain", "dependencies": { - "vscode-css-languageservice": "^3.0.13-next.10", - "vscode-html-languageservice": "^2.1.11", - "vscode-languageserver": "^5.1.0", - "vscode-languageserver-types": "^3.13.0", + "vscode-css-languageservice": "^4.0.0-next.3", + "vscode-html-languageservice": "^3.0.0-next.3", + "vscode-languageserver": "^5.3.0-next.2", + "vscode-languageserver-types": "^3.14.0", "vscode-nls": "^4.0.0", "vscode-uri": "^1.0.6" }, diff --git a/extensions/html-language-features/server/src/customData.ts b/extensions/html-language-features/server/src/customData.ts index 673e4a4ab9d..1d550eddf9f 100644 --- a/extensions/html-language-features/server/src/customData.ts +++ b/extensions/html-language-features/server/src/customData.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IHTMLDataProvider, HTMLDataProvider } from 'vscode-html-languageservice'; +import { IHTMLDataProvider, newHTMLDataProvider } from 'vscode-html-languageservice'; import * as fs from 'fs'; export function getDataProviders(dataPaths?: string[]): IHTMLDataProvider[] { @@ -18,7 +18,7 @@ export function getDataProviders(dataPaths?: string[]): IHTMLDataProvider[] { if (fs.existsSync(path)) { const htmlData = JSON.parse(fs.readFileSync(path, 'utf-8')); - providers.push(new HTMLDataProvider(`customProvider${i}`, htmlData)); + providers.push(newHTMLDataProvider(`customProvider${i}`, htmlData)); } } catch (err) { console.log(`Failed to load tag from ${path}`); diff --git a/extensions/html-language-features/server/src/htmlServerMain.ts b/extensions/html-language-features/server/src/htmlServerMain.ts index 43ae7d5070c..7974284bd77 100644 --- a/extensions/html-language-features/server/src/htmlServerMain.ts +++ b/extensions/html-language-features/server/src/htmlServerMain.ts @@ -455,15 +455,15 @@ connection.onFoldingRanges((params, token) => { }, null, `Error while computing folding regions for ${params.textDocument.uri}`, token); }); -connection.onRequest('$/textDocument/selectionRange', async (params, token) => { +connection.onRequest('$/textDocument/selectionRanges', async (params, token) => { return runSafe(() => { const document = documents.get(params.textDocument.uri); - const position: Position = params.position; + const positions: Position[] = params.positions; if (document) { const htmlMode = languageModes.getMode('html'); - if (htmlMode && htmlMode.doSelection) { - return htmlMode.doSelection(document, position); + if (htmlMode && htmlMode.getSelectionRanges) { + return htmlMode.getSelectionRanges(document, positions); } } return Promise.resolve(null); diff --git a/extensions/html-language-features/server/src/modes/htmlMode.ts b/extensions/html-language-features/server/src/modes/htmlMode.ts index 00a690f56eb..09efb996f6e 100644 --- a/extensions/html-language-features/server/src/modes/htmlMode.ts +++ b/extensions/html-language-features/server/src/modes/htmlMode.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { getLanguageModelCache } from '../languageModelCache'; -import { LanguageService as HTMLLanguageService, HTMLDocument, DocumentContext, FormattingOptions, HTMLFormatConfiguration } from 'vscode-html-languageservice'; +import { LanguageService as HTMLLanguageService, HTMLDocument, DocumentContext, FormattingOptions, HTMLFormatConfiguration, SelectionRange } from 'vscode-html-languageservice'; import { TextDocument, Position, Range, CompletionItem, FoldingRange } from 'vscode-languageserver-types'; import { LanguageMode, Workspace } from './languageModes'; import { getPathCompletionParticipant } from './pathCompletion'; @@ -15,8 +15,8 @@ export function getHTMLMode(htmlLanguageService: HTMLLanguageService, workspace: getId() { return 'html'; }, - doSelection(document: TextDocument, position: Position): Range[] { - return htmlLanguageService.getSelectionRanges(document, position); + getSelectionRanges(document: TextDocument, positions: Position[]): SelectionRange[][] { + return htmlLanguageService.getSelectionRanges(document, positions); }, doComplete(document: TextDocument, position: Position, settings = workspace.settings) { let options = settings && settings.html && settings.html.suggest; diff --git a/extensions/html-language-features/server/src/modes/languageModes.ts b/extensions/html-language-features/server/src/modes/languageModes.ts index 048076ed70d..94c0b04a293 100644 --- a/extensions/html-language-features/server/src/modes/languageModes.ts +++ b/extensions/html-language-features/server/src/modes/languageModes.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { getLanguageService as getHTMLLanguageService, DocumentContext, IHTMLDataProvider } from 'vscode-html-languageservice'; +import { getLanguageService as getHTMLLanguageService, DocumentContext, IHTMLDataProvider, SelectionRange } from 'vscode-html-languageservice'; import { CompletionItem, Location, SignatureHelp, Definition, TextEdit, TextDocument, Diagnostic, DocumentLink, Range, Hover, DocumentHighlight, CompletionList, Position, FormattingOptions, SymbolInformation, FoldingRange @@ -31,7 +31,7 @@ export interface Workspace { export interface LanguageMode { getId(): string; - doSelection?: (document: TextDocument, position: Position) => Range[]; + getSelectionRanges?: (document: TextDocument, positions: Position[]) => SelectionRange[][]; doValidation?: (document: TextDocument, settings?: Settings) => Diagnostic[]; doComplete?: (document: TextDocument, position: Position, settings?: Settings) => CompletionList; doResolve?: (document: TextDocument, item: CompletionItem) => CompletionItem; diff --git a/extensions/html-language-features/server/yarn.lock b/extensions/html-language-features/server/yarn.lock index 5173fd2b003..79eeb0196cb 100644 --- a/extensions/html-language-features/server/yarn.lock +++ b/extensions/html-language-features/server/yarn.lock @@ -229,20 +229,20 @@ supports-color@5.4.0: dependencies: has-flag "^3.0.0" -vscode-css-languageservice@^3.0.13-next.10: - version "3.0.13-next.10" - resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-3.0.13-next.10.tgz#f5822e832b06e1e91ec96c528bab83bb07251c35" - integrity sha512-zKwzo3GVhrAllYDM4afL8q1XCHixsI8tP3SyLrWGzp0Nc9P+bbjKQeC26VcaOb0dtkgfpB/vfBPf+4yOs4s/pw== +vscode-css-languageservice@^4.0.0-next.3: + version "4.0.0-next.3" + resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-4.0.0-next.3.tgz#e9529f3b4ddf95c9a3e5dc2a6d701a38280ffa98" + integrity sha512-/xmbWpIQLw+HZ/3LsaE2drHFSNJbM9mZ8bKR5NUiu2ZUr10WbGxX0j/GDZB3LlMmdSHQGgRQ5hTM/Ic2PuBDRw== dependencies: - vscode-languageserver-types "^3.13.0" + vscode-languageserver-types "^3.14.0" vscode-nls "^4.0.0" -vscode-html-languageservice@^2.1.11: - version "2.1.11" - resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-2.1.11.tgz#56fc25cf64793a9eaef2f571a0ecd591eaed26c1" - integrity sha512-wfENgWb7JjEhwsRHXhairumuxAGnFcMUwIut5P7SxGwNrJMDXkgfs6OUBZycQpbaXTkMEvwsKNKFqUQppW7P4g== +vscode-html-languageservice@^3.0.0-next.3: + version "3.0.0-next.3" + resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-3.0.0-next.3.tgz#a0230c57375deb21fadbfa0210a770ca6e06d425" + integrity sha512-vPdZ17JSr8kAAnjNjdiH4jYySaJrXqnbT3OhnGXqc51R3jnwMXV/Jf72ctXptbpiUQM3ifHnfUcxwO+34tw6Lw== dependencies: - vscode-languageserver-types "^3.13.0" + vscode-languageserver-types "^3.14.0" vscode-nls "^4.0.0" vscode-uri "^1.0.6" @@ -251,25 +251,25 @@ vscode-jsonrpc@^4.0.0: resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-4.0.0.tgz#a7bf74ef3254d0a0c272fab15c82128e378b3be9" integrity sha512-perEnXQdQOJMTDFNv+UF3h1Y0z4iSiaN9jIlb0OqIYgosPCZGYh/MCUlkFtV2668PL69lRDO32hmvL2yiidUYg== -vscode-languageserver-protocol@3.13.0: - version "3.13.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.13.0.tgz#710d8e42119bb3affb1416e1e104bd6b4d503595" - integrity sha512-2ZGKwI+P2ovQll2PGAp+2UfJH+FK9eait86VBUdkPd9HRlm8e58aYT9pV/NYanHOcp3pL6x2yTLVCFMcTer0mg== +vscode-languageserver-protocol@3.15.0-next.1: + version "3.15.0-next.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.0-next.1.tgz#1e45e224d7eef8c79b4bed75b9dcb1930d2ab8ed" + integrity sha512-LXF0d9s3vxFBxVQ4aKl/XghdEMAncGt3dh4urIYa9Is43g3MfIQL9fC44YZtP+XXOrI2rpZU8lRNN01U1V6CDg== dependencies: vscode-jsonrpc "^4.0.0" - vscode-languageserver-types "3.13.0" + vscode-languageserver-types "3.14.0" -vscode-languageserver-types@3.13.0, vscode-languageserver-types@^3.13.0: - version "3.13.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.13.0.tgz#b704b024cef059f7b326611c99b9c8753c0a18b4" - integrity sha512-BnJIxS+5+8UWiNKCP7W3g9FlE7fErFw0ofP5BXJe7c2tl0VeWh+nNHFbwAS2vmVC4a5kYxHBjRy0UeOtziemVA== +vscode-languageserver-types@3.14.0, vscode-languageserver-types@^3.14.0: + version "3.14.0" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.14.0.tgz#d3b5952246d30e5241592b6dde8280e03942e743" + integrity sha512-lTmS6AlAlMHOvPQemVwo3CezxBp0sNB95KNPkqp3Nxd5VFEnuG1ByM0zlRWos0zjO3ZWtkvhal0COgiV1xIA4A== -vscode-languageserver@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-5.1.0.tgz#012a28f154cc7a848c443d217894942e4c3eeb39" - integrity sha512-CIsrgx2Y5VHS317g/HwkSTWYBIQmy0DwEyZPmB2pEpVOhYFwVsYpbiJwHIIyLQsQtmRaO4eA2xM8KPjNSdXpBw== +vscode-languageserver@^5.3.0-next.2: + version "5.3.0-next.2" + resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-5.3.0-next.2.tgz#31ce4c34d68b517b400ca9e211e43f8d868b8dcc" + integrity sha512-n5onRw9naMrRHp2jnOn+ZwN1n+tTfzftWLPonjp1FWf/iCZWIlnw2TyF/Hn+SDGhLoVtoghmxhwEQaxEAfLHvw== dependencies: - vscode-languageserver-protocol "3.13.0" + vscode-languageserver-protocol "3.15.0-next.1" vscode-uri "^1.0.6" vscode-nls@^4.0.0: diff --git a/extensions/html-language-features/yarn.lock b/extensions/html-language-features/yarn.lock index 2737f409efa..a0667775d59 100644 --- a/extensions/html-language-features/yarn.lock +++ b/extensions/html-language-features/yarn.lock @@ -45,26 +45,26 @@ vscode-jsonrpc@^4.0.0: resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-4.0.0.tgz#a7bf74ef3254d0a0c272fab15c82128e378b3be9" integrity sha512-perEnXQdQOJMTDFNv+UF3h1Y0z4iSiaN9jIlb0OqIYgosPCZGYh/MCUlkFtV2668PL69lRDO32hmvL2yiidUYg== -vscode-languageclient@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-5.1.0.tgz#650ab0dc9fd0daaade058a8471aaff5bc3f9580e" - integrity sha512-Z95Kps8UqD4o17HE3uCkZuvenOsxHVH46dKmaGVpGixEFZigPaVuVxLM/JWeIY9aRenoC0ZD9CK1O7L4jpffKg== +vscode-languageclient@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-5.2.1.tgz#7cfc83a294c409f58cfa2b910a8cfeaad0397193" + integrity sha512-7jrS/9WnV0ruqPamN1nE7qCxn0phkH5LjSgSp9h6qoJGoeAKzwKz/PF6M+iGA/aklx4GLZg1prddhEPQtuXI1Q== dependencies: semver "^5.5.0" - vscode-languageserver-protocol "3.13.0" + vscode-languageserver-protocol "3.14.1" -vscode-languageserver-protocol@3.13.0: - version "3.13.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.13.0.tgz#710d8e42119bb3affb1416e1e104bd6b4d503595" - integrity sha512-2ZGKwI+P2ovQll2PGAp+2UfJH+FK9eait86VBUdkPd9HRlm8e58aYT9pV/NYanHOcp3pL6x2yTLVCFMcTer0mg== +vscode-languageserver-protocol@3.14.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.14.1.tgz#b8aab6afae2849c84a8983d39a1cf742417afe2f" + integrity sha512-IL66BLb2g20uIKog5Y2dQ0IiigW0XKrvmWiOvc0yXw80z3tMEzEnHjaGAb3ENuU7MnQqgnYJ1Cl2l9RvNgDi4g== dependencies: vscode-jsonrpc "^4.0.0" - vscode-languageserver-types "3.13.0" + vscode-languageserver-types "3.14.0" -vscode-languageserver-types@3.13.0: - version "3.13.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.13.0.tgz#b704b024cef059f7b326611c99b9c8753c0a18b4" - integrity sha512-BnJIxS+5+8UWiNKCP7W3g9FlE7fErFw0ofP5BXJe7c2tl0VeWh+nNHFbwAS2vmVC4a5kYxHBjRy0UeOtziemVA== +vscode-languageserver-types@3.14.0: + version "3.14.0" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.14.0.tgz#d3b5952246d30e5241592b6dde8280e03942e743" + integrity sha512-lTmS6AlAlMHOvPQemVwo3CezxBp0sNB95KNPkqp3Nxd5VFEnuG1ByM0zlRWos0zjO3ZWtkvhal0COgiV1xIA4A== vscode-nls@^4.0.0: version "4.0.0" diff --git a/extensions/json-language-features/client/src/jsonMain.ts b/extensions/json-language-features/client/src/jsonMain.ts index 30b122071b1..ee52e73fc61 100644 --- a/extensions/json-language-features/client/src/jsonMain.ts +++ b/extensions/json-language-features/client/src/jsonMain.ts @@ -6,6 +6,8 @@ import * as path from 'path'; import * as fs from 'fs'; import * as nls from 'vscode-nls'; +import { xhr, XHRResponse, getErrorStatusDescription } from 'request-light'; + const localize = nls.loadMessageBundle(); import { workspace, window, languages, commands, ExtensionContext, extensions, Uri, LanguageConfiguration, Diagnostic, StatusBarAlignment, TextEditor, TextDocument, Position, SelectionRange } from 'vscode'; @@ -93,6 +95,9 @@ export function activate(context: ExtensionContext) { let clientOptions: LanguageClientOptions = { // Register the server for json documents documentSelector, + initializationOptions: { + handledSchemaProtocols: ['file'] // language server only loads file-URI. Fetching schemas with other protocols ('http'...) are made on the client. + }, synchronize: { // Synchronize the setting section 'json' to the server configurationSection: ['json', 'http'], @@ -138,11 +143,20 @@ export function activate(context: ExtensionContext) { // handle content request client.onRequest(VSCodeContentRequest.type, (uriPath: string) => { let uri = Uri.parse(uriPath); - return workspace.openTextDocument(uri).then(doc => { - return doc.getText(); - }, error => { - return Promise.reject(error); - }); + if (uri.scheme !== 'http' && uri.scheme !== 'https') { + return workspace.openTextDocument(uri).then(doc => { + return doc.getText(); + }, error => { + return Promise.reject(error); + }); + } else { + const headers = { 'Accept-Encoding': 'gzip, deflate' }; + return xhr({ url: uriPath, followRedirects: 5, headers }).then(response => { + return response.responseText; + }, (error: XHRResponse) => { + return Promise.reject(error.responseText || getErrorStatusDescription(error.status) || error.toString()); + }); + } }); let handleContentChange = (uri: Uri) => { diff --git a/extensions/json-language-features/extension.webpack.config.js b/extensions/json-language-features/extension.webpack.config.js index 4c2ab99cf25..bf887a3d0d8 100644 --- a/extensions/json-language-features/extension.webpack.config.js +++ b/extensions/json-language-features/extension.webpack.config.js @@ -9,6 +9,7 @@ const withDefaults = require('../shared.webpack.config'); const path = require('path'); +var webpack = require('webpack'); module.exports = withDefaults({ context: path.join(__dirname, 'client'), @@ -18,5 +19,9 @@ module.exports = withDefaults({ output: { filename: 'jsonMain.js', path: path.join(__dirname, 'client', 'dist') - } + }, + plugins: [ + new webpack.IgnorePlugin(/vertx/) // request-light dependendeny + ] + }); diff --git a/extensions/json-language-features/package.json b/extensions/json-language-features/package.json index 47d1f7bf32d..9dc4f87d9f0 100644 --- a/extensions/json-language-features/package.json +++ b/extensions/json-language-features/package.json @@ -102,10 +102,11 @@ }, "dependencies": { "vscode-extension-telemetry": "0.1.1", - "vscode-languageclient": "^5.1.0", - "vscode-nls": "^4.0.0" + "vscode-languageclient": "^5.2.1", + "vscode-nls": "^4.0.0", + "request-light": "^0.2.4" }, "devDependencies": { "@types/node": "^10.12.21" } -} \ No newline at end of file +} diff --git a/extensions/json-language-features/server/build/filesFillIn.js b/extensions/json-language-features/server/build/filesFillIn.js deleted file mode 100644 index 906617384e0..00000000000 --- a/extensions/json-language-features/server/build/filesFillIn.js +++ /dev/null @@ -1,5 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -module.exports = {}; \ No newline at end of file diff --git a/extensions/json-language-features/server/extension.webpack.config.js b/extensions/json-language-features/server/extension.webpack.config.js index b31abe55f25..e6f4c516bc9 100644 --- a/extensions/json-language-features/server/extension.webpack.config.js +++ b/extensions/json-language-features/server/extension.webpack.config.js @@ -21,10 +21,6 @@ module.exports = withDefaults({ path: path.join(__dirname, 'dist') }, plugins: [ - new webpack.NormalModuleReplacementPlugin( - /[/\\]vscode-languageserver[/\\]lib[/\\]files\.js/, - require.resolve('./build/filesFillIn') - ), - new webpack.IgnorePlugin(/vertx/) - ], + new webpack.IgnorePlugin(/vertx/) // request-light dependendeny + ] }); diff --git a/extensions/json-language-features/server/package.json b/extensions/json-language-features/server/package.json index 959af9a9794..fc2323a773e 100644 --- a/extensions/json-language-features/server/package.json +++ b/extensions/json-language-features/server/package.json @@ -12,10 +12,10 @@ }, "main": "./out/jsonServerMain", "dependencies": { - "jsonc-parser": "^2.0.2", + "jsonc-parser": "^2.0.3", "request-light": "^0.2.4", - "vscode-json-languageservice": "^3.3.0-next.4", - "vscode-languageserver": "^5.1.0", + "vscode-json-languageservice": "^3.3.0-next.6", + "vscode-languageserver": "^5.3.0-next.2", "vscode-nls": "^4.0.0", "vscode-uri": "^1.0.6" }, diff --git a/extensions/json-language-features/server/yarn.lock b/extensions/json-language-features/server/yarn.lock index 7a974ff4a2b..bb8489b6d86 100644 --- a/extensions/json-language-features/server/yarn.lock +++ b/extensions/json-language-features/server/yarn.lock @@ -54,10 +54,10 @@ https-proxy-agent@^2.2.1: agent-base "^4.1.0" debug "^3.1.0" -jsonc-parser@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.0.2.tgz#42fcf56d70852a043fadafde51ddb4a85649978d" - integrity sha512-TSU435K5tEKh3g7bam1AFf+uZrISheoDsLlpmAo6wWZYqjsnd09lHYK1Qo+moK4Ikifev1Gdpa69g4NELKnCrQ== +jsonc-parser@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.0.3.tgz#6d4199ccab7f21ff5d2a4225050c54e981fb21a2" + integrity sha512-WJi9y9ABL01C8CxTKxRRQkkSpY/x2bo4Gy0WuiZGrInxQqgxQpvkBCLNcDYcHOSdhx4ODgbFcgAvfL49C+PHgQ== ms@2.0.0: version "2.0.0" @@ -73,13 +73,13 @@ request-light@^0.2.4: https-proxy-agent "^2.2.1" vscode-nls "^4.0.0" -vscode-json-languageservice@^3.3.0-next.4: - version "3.3.0-next.4" - resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.3.0-next.4.tgz#e13b2fd5665b754ee94608a506407cd12b63d64d" - integrity sha512-tD8w2SvwERj3392q34xXKA/i76WWK7YwmP9eoLdKvdfMRwXB7a0MYWPoYZD4HycwvavAlbJ2x18vP4G7+BFA+A== +vscode-json-languageservice@^3.3.0-next.6: + version "3.3.0-next.6" + resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.3.0-next.6.tgz#711f121b44ba443a89f3fb01a01c611f2547079f" + integrity sha512-i1tyLiodWc7y6lR9C4cat+OUSptj8Duk1Ybm1FaMzhNfOTFttSiwrBw1otNb+QwI65VEj7EAEBQHRLeQOWznMw== dependencies: - jsonc-parser "^2.0.2" - vscode-languageserver-types "^3.13.0" + jsonc-parser "^2.0.3" + vscode-languageserver-types "^3.14.0" vscode-nls "^4.0.0" vscode-uri "^1.0.6" @@ -88,25 +88,25 @@ vscode-jsonrpc@^4.0.0: resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-4.0.0.tgz#a7bf74ef3254d0a0c272fab15c82128e378b3be9" integrity sha512-perEnXQdQOJMTDFNv+UF3h1Y0z4iSiaN9jIlb0OqIYgosPCZGYh/MCUlkFtV2668PL69lRDO32hmvL2yiidUYg== -vscode-languageserver-protocol@3.13.0: - version "3.13.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.13.0.tgz#710d8e42119bb3affb1416e1e104bd6b4d503595" - integrity sha512-2ZGKwI+P2ovQll2PGAp+2UfJH+FK9eait86VBUdkPd9HRlm8e58aYT9pV/NYanHOcp3pL6x2yTLVCFMcTer0mg== +vscode-languageserver-protocol@3.15.0-next.1: + version "3.15.0-next.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.0-next.1.tgz#1e45e224d7eef8c79b4bed75b9dcb1930d2ab8ed" + integrity sha512-LXF0d9s3vxFBxVQ4aKl/XghdEMAncGt3dh4urIYa9Is43g3MfIQL9fC44YZtP+XXOrI2rpZU8lRNN01U1V6CDg== dependencies: vscode-jsonrpc "^4.0.0" - vscode-languageserver-types "3.13.0" + vscode-languageserver-types "3.14.0" -vscode-languageserver-types@3.13.0, vscode-languageserver-types@^3.13.0: - version "3.13.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.13.0.tgz#b704b024cef059f7b326611c99b9c8753c0a18b4" - integrity sha512-BnJIxS+5+8UWiNKCP7W3g9FlE7fErFw0ofP5BXJe7c2tl0VeWh+nNHFbwAS2vmVC4a5kYxHBjRy0UeOtziemVA== +vscode-languageserver-types@3.14.0, vscode-languageserver-types@^3.14.0: + version "3.14.0" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.14.0.tgz#d3b5952246d30e5241592b6dde8280e03942e743" + integrity sha512-lTmS6AlAlMHOvPQemVwo3CezxBp0sNB95KNPkqp3Nxd5VFEnuG1ByM0zlRWos0zjO3ZWtkvhal0COgiV1xIA4A== -vscode-languageserver@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-5.1.0.tgz#012a28f154cc7a848c443d217894942e4c3eeb39" - integrity sha512-CIsrgx2Y5VHS317g/HwkSTWYBIQmy0DwEyZPmB2pEpVOhYFwVsYpbiJwHIIyLQsQtmRaO4eA2xM8KPjNSdXpBw== +vscode-languageserver@^5.3.0-next.2: + version "5.3.0-next.2" + resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-5.3.0-next.2.tgz#31ce4c34d68b517b400ca9e211e43f8d868b8dcc" + integrity sha512-n5onRw9naMrRHp2jnOn+ZwN1n+tTfzftWLPonjp1FWf/iCZWIlnw2TyF/Hn+SDGhLoVtoghmxhwEQaxEAfLHvw== dependencies: - vscode-languageserver-protocol "3.13.0" + vscode-languageserver-protocol "3.15.0-next.1" vscode-uri "^1.0.6" vscode-nls@^4.0.0: diff --git a/extensions/json-language-features/yarn.lock b/extensions/json-language-features/yarn.lock index e49fc01bd24..758f96fd511 100644 --- a/extensions/json-language-features/yarn.lock +++ b/extensions/json-language-features/yarn.lock @@ -7,6 +7,13 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.21.tgz#7e8a0c34cf29f4e17a36e9bd0ea72d45ba03908e" integrity sha512-CBgLNk4o3XMnqMc0rhb6lc77IwShMEglz05deDcn2lQxyXEZivfwgYJu7SMha9V5XcrP6qZuevTHV/QrN2vjKQ== +agent-base@4, agent-base@^4.1.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9" + integrity sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg== + dependencies: + es6-promisify "^5.0.0" + applicationinsights@1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.8.tgz#db6e3d983cf9f9405fe1ee5ba30ac6e1914537b5" @@ -16,6 +23,20 @@ applicationinsights@1.0.8: diagnostic-channel-publishers "0.2.1" zone.js "0.7.6" +debug@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== + dependencies: + ms "2.0.0" + +debug@^3.1.0: + version "3.2.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" + integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== + dependencies: + ms "^2.1.1" + diagnostic-channel-publishers@0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-0.2.1.tgz#8e2d607a8b6d79fe880b548bc58cc6beb288c4f3" @@ -28,6 +49,53 @@ diagnostic-channel@0.2.0: dependencies: semver "^5.3.0" +es6-promise@^4.0.3: + version "4.2.6" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.6.tgz#b685edd8258886365ea62b57d30de28fadcd974f" + integrity sha512-aRVgGdnmW2OiySVPUC9e6m+plolMAJKjZnQlCwNSuK5yQ0JN61DZSO1X1Ufd1foqWRAlig0rhduTCHe7sVtK5Q== + +es6-promisify@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" + integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM= + dependencies: + es6-promise "^4.0.3" + +http-proxy-agent@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405" + integrity sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg== + dependencies: + agent-base "4" + debug "3.1.0" + +https-proxy-agent@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz#51552970fa04d723e04c56d04178c3f92592bbc0" + integrity sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ== + dependencies: + agent-base "^4.1.0" + debug "^3.1.0" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +ms@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== + +request-light@^0.2.4: + version "0.2.4" + resolved "https://registry.yarnpkg.com/request-light/-/request-light-0.2.4.tgz#3cea29c126682e6bcadf7915353322eeba01a755" + integrity sha512-pM9Fq5jRnSb+82V7M97rp8FE9/YNeP2L9eckB4Szd7lyeclSIx02aIpPO/6e4m6Dy31+FBN/zkFMTd2HkNO3ow== + dependencies: + http-proxy-agent "^2.1.0" + https-proxy-agent "^2.2.1" + vscode-nls "^4.0.0" + semver@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" @@ -50,26 +118,26 @@ vscode-jsonrpc@^4.0.0: resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-4.0.0.tgz#a7bf74ef3254d0a0c272fab15c82128e378b3be9" integrity sha512-perEnXQdQOJMTDFNv+UF3h1Y0z4iSiaN9jIlb0OqIYgosPCZGYh/MCUlkFtV2668PL69lRDO32hmvL2yiidUYg== -vscode-languageclient@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-5.1.0.tgz#650ab0dc9fd0daaade058a8471aaff5bc3f9580e" - integrity sha512-Z95Kps8UqD4o17HE3uCkZuvenOsxHVH46dKmaGVpGixEFZigPaVuVxLM/JWeIY9aRenoC0ZD9CK1O7L4jpffKg== +vscode-languageclient@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-5.2.1.tgz#7cfc83a294c409f58cfa2b910a8cfeaad0397193" + integrity sha512-7jrS/9WnV0ruqPamN1nE7qCxn0phkH5LjSgSp9h6qoJGoeAKzwKz/PF6M+iGA/aklx4GLZg1prddhEPQtuXI1Q== dependencies: semver "^5.5.0" - vscode-languageserver-protocol "3.13.0" + vscode-languageserver-protocol "3.14.1" -vscode-languageserver-protocol@3.13.0: - version "3.13.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.13.0.tgz#710d8e42119bb3affb1416e1e104bd6b4d503595" - integrity sha512-2ZGKwI+P2ovQll2PGAp+2UfJH+FK9eait86VBUdkPd9HRlm8e58aYT9pV/NYanHOcp3pL6x2yTLVCFMcTer0mg== +vscode-languageserver-protocol@3.14.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.14.1.tgz#b8aab6afae2849c84a8983d39a1cf742417afe2f" + integrity sha512-IL66BLb2g20uIKog5Y2dQ0IiigW0XKrvmWiOvc0yXw80z3tMEzEnHjaGAb3ENuU7MnQqgnYJ1Cl2l9RvNgDi4g== dependencies: vscode-jsonrpc "^4.0.0" - vscode-languageserver-types "3.13.0" + vscode-languageserver-types "3.14.0" -vscode-languageserver-types@3.13.0: - version "3.13.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.13.0.tgz#b704b024cef059f7b326611c99b9c8753c0a18b4" - integrity sha512-BnJIxS+5+8UWiNKCP7W3g9FlE7fErFw0ofP5BXJe7c2tl0VeWh+nNHFbwAS2vmVC4a5kYxHBjRy0UeOtziemVA== +vscode-languageserver-types@3.14.0: + version "3.14.0" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.14.0.tgz#d3b5952246d30e5241592b6dde8280e03942e743" + integrity sha512-lTmS6AlAlMHOvPQemVwo3CezxBp0sNB95KNPkqp3Nxd5VFEnuG1ByM0zlRWos0zjO3ZWtkvhal0COgiV1xIA4A== vscode-nls@^4.0.0: version "4.0.0" diff --git a/extensions/markdown-language-features/package.json b/extensions/markdown-language-features/package.json index 9dd958c29c9..10431185034 100644 --- a/extensions/markdown-language-features/package.json +++ b/extensions/markdown-language-features/package.json @@ -153,6 +153,14 @@ { "command": "markdown.preview.toggleLock", "when": "markdownPreviewFocus" + }, + { + "command": "markdown.preview.refresh", + "when": "editorLangId == markdown" + }, + { + "command": "markdown.preview.refresh", + "when": "markdownPreviewFocus" } ] }, @@ -315,4 +323,4 @@ "webpack": "^4.1.0", "webpack-cli": "^2.0.10" } -} +} \ No newline at end of file diff --git a/extensions/objective-c/test/colorize-results/test_m.json b/extensions/objective-c/test/colorize-results/test_m.json index 9a4926acad5..dc53e9cd713 100644 --- a/extensions/objective-c/test/colorize-results/test_m.json +++ b/extensions/objective-c/test/colorize-results/test_m.json @@ -298,7 +298,7 @@ }, { "c": "void", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.function.objc meta.return-type.objc storage.type.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.function.objc meta.return-type.objc storage.type.built-in.primitive.c", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -639,7 +639,7 @@ }, { "c": "(", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.section.parens.begin.bracket.round.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.section.parens.block punctuation.section.parens.begin.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -650,7 +650,7 @@ }, { "c": "NSDocumentDirectory", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c support.constant.cocoa", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.section.parens.block support.constant.cocoa", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -661,7 +661,7 @@ }, { "c": ",", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.separator.delimiter.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.section.parens.block punctuation.separator.delimiter.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -672,7 +672,7 @@ }, { "c": " ", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.section.parens.block", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -683,7 +683,7 @@ }, { "c": "NSUserDomainMask", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c support.constant.cocoa", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.section.parens.block support.constant.cocoa", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -694,7 +694,7 @@ }, { "c": ",", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.separator.delimiter.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.section.parens.block punctuation.separator.delimiter.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -705,7 +705,7 @@ }, { "c": " ", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.section.parens.block", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -716,7 +716,7 @@ }, { "c": "true", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c constant.language.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.section.parens.block constant.language.c", "r": { "dark_plus": "constant.language: #569CD6", "light_plus": "constant.language: #0000FF", @@ -727,7 +727,7 @@ }, { "c": ")", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.section.parens.end.bracket.round.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.section.parens.block punctuation.section.parens.end.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -738,7 +738,7 @@ }, { "c": "[", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.definition.begin.bracket.square.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c punctuation.definition.begin.bracket.square.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -749,7 +749,7 @@ }, { "c": "0", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c constant.numeric.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c constant.numeric.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -760,7 +760,7 @@ }, { "c": "]", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.definition.end.bracket.square.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c punctuation.definition.end.bracket.square.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -848,7 +848,7 @@ }, { "c": "[", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.definition.begin.bracket.square.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c punctuation.definition.begin.bracket.square.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -859,7 +859,7 @@ }, { "c": "NSOpenPanel", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c support.class.cocoa", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c support.class.cocoa", "r": { "dark_plus": "support.class: #4EC9B0", "light_plus": "support.class: #267F99", @@ -870,7 +870,7 @@ }, { "c": " openPanel", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -881,7 +881,7 @@ }, { "c": "]", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.definition.end.bracket.square.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c punctuation.definition.end.bracket.square.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -914,7 +914,7 @@ }, { "c": "[", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.definition.begin.bracket.square.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c punctuation.definition.begin.bracket.square.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -925,7 +925,7 @@ }, { "c": "panel setAllowedFileTypes:", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -935,8 +935,19 @@ } }, { - "c": "[[", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.definition.begin.bracket.square.c", + "c": "[", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c punctuation.definition.begin.bracket.square.c", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "[", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c meta.bracket.square.access.c punctuation.definition.begin.bracket.square.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -947,7 +958,7 @@ }, { "c": "NSArray", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c support.class.cocoa", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c meta.bracket.square.access.c support.class.cocoa", "r": { "dark_plus": "support.class: #4EC9B0", "light_plus": "support.class: #267F99", @@ -958,7 +969,7 @@ }, { "c": " alloc", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c meta.bracket.square.access.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -969,7 +980,7 @@ }, { "c": "]", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.definition.end.bracket.square.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c meta.bracket.square.access.c punctuation.definition.end.bracket.square.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -980,7 +991,7 @@ }, { "c": " initWithObjects:", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -991,7 +1002,7 @@ }, { "c": "@\"", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c string.quoted.double.objc punctuation.definition.string.begin.objc", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c string.quoted.double.objc punctuation.definition.string.begin.objc", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1002,7 +1013,7 @@ }, { "c": "ipa", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c string.quoted.double.objc", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c string.quoted.double.objc", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1013,7 +1024,7 @@ }, { "c": "\"", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c string.quoted.double.objc punctuation.definition.string.end.objc", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c string.quoted.double.objc punctuation.definition.string.end.objc", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1024,7 +1035,7 @@ }, { "c": ",", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.separator.delimiter.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c punctuation.separator.delimiter.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1035,7 +1046,7 @@ }, { "c": " ", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1046,7 +1057,7 @@ }, { "c": "@\"", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c string.quoted.double.objc punctuation.definition.string.begin.objc", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c string.quoted.double.objc punctuation.definition.string.begin.objc", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1057,7 +1068,7 @@ }, { "c": "xcarchive", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c string.quoted.double.objc", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c string.quoted.double.objc", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1068,7 +1079,7 @@ }, { "c": "\"", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c string.quoted.double.objc punctuation.definition.string.end.objc", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c string.quoted.double.objc punctuation.definition.string.end.objc", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1079,7 +1090,7 @@ }, { "c": ",", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.separator.delimiter.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c punctuation.separator.delimiter.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1090,7 +1101,7 @@ }, { "c": " ", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1101,7 +1112,7 @@ }, { "c": "@\"", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c string.quoted.double.objc punctuation.definition.string.begin.objc", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c string.quoted.double.objc punctuation.definition.string.begin.objc", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1112,7 +1123,7 @@ }, { "c": "app", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c string.quoted.double.objc", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c string.quoted.double.objc", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1123,7 +1134,7 @@ }, { "c": "\"", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c string.quoted.double.objc punctuation.definition.string.end.objc", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c string.quoted.double.objc punctuation.definition.string.end.objc", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1134,7 +1145,7 @@ }, { "c": ",", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.separator.delimiter.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c punctuation.separator.delimiter.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1145,7 +1156,7 @@ }, { "c": " ", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1156,7 +1167,7 @@ }, { "c": "nil", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c constant.language.objc", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c constant.language.objc", "r": { "dark_plus": "constant.language: #569CD6", "light_plus": "constant.language: #0000FF", @@ -1166,8 +1177,19 @@ } }, { - "c": "]]", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.definition.end.bracket.square.c", + "c": "]", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c punctuation.definition.end.bracket.square.c", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "]", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c punctuation.definition.end.bracket.square.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1200,7 +1222,7 @@ }, { "c": "[", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.definition.begin.bracket.square.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c punctuation.definition.begin.bracket.square.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1211,7 +1233,7 @@ }, { "c": "panel beginWithCompletionHandler:", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1222,7 +1244,7 @@ }, { "c": "^", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c keyword.operator.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c keyword.operator.c", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1233,7 +1255,7 @@ }, { "c": "(", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.section.parens.begin.bracket.round.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c punctuation.section.parens.begin.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1244,7 +1266,7 @@ }, { "c": "NSInteger", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c support.type.cocoa.leopard", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c support.type.cocoa.leopard", "r": { "dark_plus": "support.type: #4EC9B0", "light_plus": "support.type: #267F99", @@ -1255,7 +1277,7 @@ }, { "c": " result", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1266,7 +1288,7 @@ }, { "c": ")", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.section.parens.end.bracket.round.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c punctuation.section.parens.end.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1277,7 +1299,7 @@ }, { "c": " ", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1288,7 +1310,7 @@ }, { "c": "{", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.section.block.begin.bracket.curly.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c punctuation.section.block.begin.bracket.curly.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1299,7 +1321,7 @@ }, { "c": " ", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1310,7 +1332,7 @@ }, { "c": "if", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c keyword.control.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c keyword.control.c", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -1321,7 +1343,7 @@ }, { "c": " ", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1332,7 +1354,7 @@ }, { "c": "(", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.section.parens.begin.bracket.round.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c punctuation.section.parens.block punctuation.section.parens.begin.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1343,7 +1365,7 @@ }, { "c": "result ", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c punctuation.section.parens.block", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1354,7 +1376,7 @@ }, { "c": "==", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c keyword.operator.comparison.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c punctuation.section.parens.block keyword.operator.comparison.c", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1365,7 +1387,7 @@ }, { "c": " ", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c punctuation.section.parens.block", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1376,7 +1398,7 @@ }, { "c": "NSFileHandlingPanelOKButton", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c support.constant.cocoa", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c punctuation.section.parens.block support.constant.cocoa", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1387,7 +1409,7 @@ }, { "c": ")", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.section.parens.end.bracket.round.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c punctuation.section.parens.block punctuation.section.parens.end.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1398,7 +1420,7 @@ }, { "c": " ", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1409,7 +1431,7 @@ }, { "c": "[", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.definition.begin.bracket.square.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c punctuation.definition.begin.bracket.square.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1420,29 +1442,29 @@ }, { "c": "self", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c variable.object.access.c variable.object.c", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "variable: #9CDCFE" } }, { "c": ".", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.separator.dot-access.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c variable.object.access.c punctuation.separator.dot-access.c", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "variable: #9CDCFE" } }, { "c": "inputTextField", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c variable.other.member.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c variable.object.access.c variable.other.member.c", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -1453,7 +1475,7 @@ }, { "c": " setStringValue:", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1464,7 +1486,7 @@ }, { "c": "[", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.definition.begin.bracket.square.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c meta.bracket.square.access.c punctuation.definition.begin.bracket.square.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1475,29 +1497,29 @@ }, { "c": "panel", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c meta.bracket.square.access.c variable.object.access.c variable.object.c", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "variable: #9CDCFE" } }, { "c": ".", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.separator.dot-access.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c meta.bracket.square.access.c variable.object.access.c punctuation.separator.dot-access.c", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "variable: #9CDCFE" } }, { "c": "URL", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c variable.other.member.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c meta.bracket.square.access.c variable.object.access.c variable.other.member.c", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -1508,51 +1530,7 @@ }, { "c": " path", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "]]", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.definition.end.bracket.square.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ";", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.terminator.statement.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " ", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "}", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.section.block.end.bracket.curly.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c meta.bracket.square.access.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1563,7 +1541,62 @@ }, { "c": "]", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.definition.end.bracket.square.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c meta.bracket.square.access.c punctuation.definition.end.bracket.square.c", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "]", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c punctuation.definition.end.bracket.square.c", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ";", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c punctuation.terminator.statement.c", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "}", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c punctuation.section.block.end.bracket.curly.c", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "]", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c punctuation.definition.end.bracket.square.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1651,7 +1684,7 @@ }, { "c": "int", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c storage.type.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c storage.type.built-in.primitive.c", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -1728,7 +1761,7 @@ }, { "c": "float", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c storage.type.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c storage.type.built-in.primitive.c", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -2299,8 +2332,19 @@ } }, { - "c": "[[", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.definition.begin.bracket.square.c", + "c": "[", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c punctuation.definition.begin.bracket.square.c", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "[", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c punctuation.definition.begin.bracket.square.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2311,7 +2355,7 @@ }, { "c": "UITapGestureRecognizer alloc", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2322,7 +2366,7 @@ }, { "c": "]", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.definition.end.bracket.square.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c punctuation.definition.end.bracket.square.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2333,7 +2377,7 @@ }, { "c": " initWithTarget:self action:", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2344,7 +2388,7 @@ }, { "c": "@", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.selector.objc storage.type.objc punctuation.definition.storage.type.objc", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.selector.objc storage.type.objc punctuation.definition.storage.type.objc", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -2355,7 +2399,7 @@ }, { "c": "selector", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.selector.objc storage.type.objc", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.selector.objc storage.type.objc", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -2366,7 +2410,7 @@ }, { "c": "(", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.selector.objc punctuation.definition.storage.type.objc", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.selector.objc punctuation.definition.storage.type.objc", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2377,7 +2421,7 @@ }, { "c": "handleTap:", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.selector.objc meta.selector.method-name.objc support.function.any-method.name-of-parameter.objc", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.selector.objc meta.selector.method-name.objc support.function.any-method.name-of-parameter.objc", "r": { "dark_plus": "support.function: #DCDCAA", "light_plus": "support.function: #795E26", @@ -2388,7 +2432,7 @@ }, { "c": ")", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.selector.objc punctuation.definition.storage.type.objc", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.selector.objc punctuation.definition.storage.type.objc", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2399,7 +2443,7 @@ }, { "c": "]", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.definition.end.bracket.square.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c punctuation.definition.end.bracket.square.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2498,7 +2542,7 @@ }, { "c": "[", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.definition.begin.bracket.square.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c punctuation.definition.begin.bracket.square.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2509,7 +2553,7 @@ }, { "c": "NSMutableArray", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c support.class.cocoa", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c support.class.cocoa", "r": { "dark_plus": "support.class: #4EC9B0", "light_plus": "support.class: #267F99", @@ -2520,7 +2564,7 @@ }, { "c": " array", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2531,7 +2575,7 @@ }, { "c": "]", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.definition.end.bracket.square.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c punctuation.definition.end.bracket.square.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2564,7 +2608,7 @@ }, { "c": "[", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.definition.begin.bracket.square.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c punctuation.definition.begin.bracket.square.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2575,7 +2619,7 @@ }, { "c": "gestureRecognizers addObject:tapGesture", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2586,7 +2630,7 @@ }, { "c": "]", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.definition.end.bracket.square.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c punctuation.definition.end.bracket.square.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2619,7 +2663,7 @@ }, { "c": "[", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.definition.begin.bracket.square.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c punctuation.definition.begin.bracket.square.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2629,8 +2673,8 @@ } }, { - "c": "gestureRecognizers addObjectsFromArray:scnView", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", + "c": "gestureRecognizers addObjectsFromArray:", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2639,20 +2683,31 @@ "hc_black": "default: #FFFFFF" } }, + { + "c": "scnView", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c variable.object.access.c variable.object.c", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, { "c": ".", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.separator.dot-access.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c variable.object.access.c punctuation.separator.dot-access.c", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "variable: #9CDCFE" } }, { "c": "gestureRecognizers", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c variable.other.member.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c variable.object.access.c variable.other.member.c", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -2663,7 +2718,7 @@ }, { "c": "]", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.definition.end.bracket.square.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c punctuation.definition.end.bracket.square.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2684,7 +2739,7 @@ } }, { - "c": " scnView", + "c": " ", "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", "r": { "dark_plus": "default: #D4D4D4", @@ -2695,19 +2750,30 @@ } }, { - "c": ".", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.separator.dot-access.c", + "c": "scnView", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c variable.object.access.c variable.object.c", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ".", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c variable.object.access.c punctuation.separator.dot-access.c", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" } }, { "c": "gestureRecognizers", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c variable.other.member.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c variable.object.access.c variable.other.member.c", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", diff --git a/extensions/package.json b/extensions/package.json index 94c17380115..88394d29642 100644 --- a/extensions/package.json +++ b/extensions/package.json @@ -3,7 +3,7 @@ "version": "0.0.1", "description": "Dependencies shared by all extensions", "dependencies": { - "typescript": "3.3.3333" + "typescript": "3.3.3" }, "scripts": { "postinstall": "node ./postinstall" diff --git a/extensions/python/package.json b/extensions/python/package.json index c81942af764..53ca25bd88f 100644 --- a/extensions/python/package.json +++ b/extensions/python/package.json @@ -10,7 +10,7 @@ "contributes": { "languages": [{ "id": "python", - "extensions": [ ".py", ".rpy", ".pyw", ".cpy", ".gyp", ".gypi", ".snakefile", ".smk", ".pyi"], + "extensions": [ ".py", ".rpy", ".pyw", ".cpy", ".gyp", ".gypi", ".snakefile", ".smk", ".pyi", ".ipy"], "aliases": [ "Python", "py" ], "firstLine": "^#!\\s*/.*\\bpython[0-9.-]*\\b", "configuration": "./language-configuration.json" diff --git a/extensions/shared.webpack.config.js b/extensions/shared.webpack.config.js index 2916167b057..8e2288c1e61 100644 --- a/extensions/shared.webpack.config.js +++ b/extensions/shared.webpack.config.js @@ -51,8 +51,6 @@ module.exports = function withDefaults(/**@type WebpackConfig*/extConfig) { }, externals: { 'vscode': 'commonjs vscode', // ignored because it doesn't exist - - // "vscode-extension-telemetry": 'commonjs vscode-extension-telemetry', // commonly used }, output: { // all output goes into `dist`. diff --git a/extensions/typescript-language-features/src/features/bufferSyncSupport.ts b/extensions/typescript-language-features/src/features/bufferSyncSupport.ts index 32c0b020e6b..422fee00f03 100644 --- a/extensions/typescript-language-features/src/features/bufferSyncSupport.ts +++ b/extensions/typescript-language-features/src/features/bufferSyncSupport.ts @@ -13,11 +13,17 @@ import * as languageModeIds from '../utils/languageModeIds'; import { ResourceMap } from '../utils/resourceMap'; import * as typeConverters from '../utils/typeConverters'; -enum BufferKind { +const enum BufferKind { TypeScript = 1, JavaScript = 2, } +const enum BufferState { + Initial = 1, + Open = 2, + Closed = 2, +} + function mode2ScriptKind(mode: string): 'TS' | 'TSX' | 'JS' | 'JSX' | undefined { switch (mode) { case languageModeIds.typescript: return 'TS'; @@ -30,6 +36,8 @@ function mode2ScriptKind(mode: string): 'TS' | 'TSX' | 'JS' | 'JSX' | undefined class SyncedBuffer { + private state = BufferState.Initial; + constructor( private readonly document: vscode.TextDocument, public readonly filepath: string, @@ -63,6 +71,7 @@ class SyncedBuffer { } this.client.executeWithoutWaitingForResponse('open', args); + this.state = BufferState.Open; } public get resource(): vscode.Uri { @@ -91,9 +100,14 @@ class SyncedBuffer { file: this.filepath }; this.client.executeWithoutWaitingForResponse('close', args); + this.state = BufferState.Closed; } public onContentChanged(events: vscode.TextDocumentContentChangeEvent[]): void { + if (this.state !== BufferState.Open) { + console.error(`Unexpected buffer state: ${this.state}`); + } + for (const { range, text } of events) { const args: Proto.ChangeRequestArgs = { insertString: text, diff --git a/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts b/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts index 890a30fcc06..68e3dd484ab 100644 --- a/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts +++ b/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts @@ -217,47 +217,16 @@ export default class TypeScriptServiceClientHost extends Disposable { return; } - (this.findLanguage(this.client.toResource(body.configFile))).then(language => { + this.findLanguage(this.client.toResource(body.configFile)).then(language => { if (!language) { return; } - if (body.diagnostics.length === 0) { - language.configFileDiagnosticsReceived(this.client.toResource(body.configFile), []); - } else if (body.diagnostics.length >= 1) { - vscode.workspace.openTextDocument(vscode.Uri.file(body.configFile)).then((document) => { - let curly: [number, number, number] | undefined = undefined; - let nonCurly: [number, number, number] | undefined = undefined; - let diagnostic: vscode.Diagnostic; - for (let index = 0; index < document.lineCount; index++) { - const line = document.lineAt(index); - const text = line.text; - const firstNonWhitespaceCharacterIndex = line.firstNonWhitespaceCharacterIndex; - if (firstNonWhitespaceCharacterIndex < text.length) { - if (text.charAt(firstNonWhitespaceCharacterIndex) === '{') { - curly = [index, firstNonWhitespaceCharacterIndex, firstNonWhitespaceCharacterIndex + 1]; - break; - } else { - const matches = /\s*([^\s]*)(?:\s*|$)/.exec(text.substr(firstNonWhitespaceCharacterIndex)); - if (matches && matches.length >= 1) { - nonCurly = [index, firstNonWhitespaceCharacterIndex, firstNonWhitespaceCharacterIndex + matches[1].length]; - } - } - } - } - const match = curly || nonCurly; - if (match) { - diagnostic = new vscode.Diagnostic(new vscode.Range(match[0], match[1], match[0], match[2]), body.diagnostics[0].text); - } else { - diagnostic = new vscode.Diagnostic(new vscode.Range(0, 0, 0, 0), body.diagnostics[0].text); - } - if (diagnostic) { - diagnostic.source = language.diagnosticSource; - language.configFileDiagnosticsReceived(this.client.toResource(body.configFile), [diagnostic]); - } - }, _error => { - language.configFileDiagnosticsReceived(this.client.toResource(body.configFile), [new vscode.Diagnostic(new vscode.Range(0, 0, 0, 0), body.diagnostics[0].text)]); - }); - } + + language.configFileDiagnosticsReceived(this.client.toResource(body.configFile), body.diagnostics.map(tsDiag => { + const diagnostic = new vscode.Diagnostic(typeConverters.Range.fromTextSpan(tsDiag), body.diagnostics[0].text); + diagnostic.source = language.diagnosticSource; + return diagnostic; + })); }); } diff --git a/extensions/typescript-language-features/src/utils/typeConverters.ts b/extensions/typescript-language-features/src/utils/typeConverters.ts index 36a20544fee..279853cff21 100644 --- a/extensions/typescript-language-features/src/utils/typeConverters.ts +++ b/extensions/typescript-language-features/src/utils/typeConverters.ts @@ -63,16 +63,17 @@ export namespace WorkspaceEdit { edits: Iterable ): vscode.WorkspaceEdit { return withFileCodeEdits(new vscode.WorkspaceEdit(), client, edits); - } + export function withFileCodeEdits( workspaceEdit: vscode.WorkspaceEdit, client: ITypeScriptServiceClient, edits: Iterable ): vscode.WorkspaceEdit { for (const edit of edits) { + const resource = client.toResource(edit.fileName); for (const textChange of edit.textChanges) { - workspaceEdit.replace(client.toResource(edit.fileName), + workspaceEdit.replace(resource, Range.fromTextSpan(textChange), textChange.newText); } diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/commands.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/commands.test.ts index c007e2168f0..c26666ed671 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/commands.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/commands.test.ts @@ -74,23 +74,6 @@ suite('commands namespace tests', () => { }); - test('api-command: vscode.previewHtml', async function () { - - let registration = workspace.registerTextDocumentContentProvider('speciale', { - provideTextDocumentContent(uri) { - return `content of URI ${uri.toString()}`; - } - }); - - let virtualDocumentUri = Uri.parse('speciale://authority/path'); - let title = 'A title'; - - const success = await commands.executeCommand('vscode.previewHtml', virtualDocumentUri, ViewColumn.Three, title); - assert.ok(success); - registration.dispose(); - - }); - test('api-command: vscode.diff', function () { let registration = workspace.registerTextDocumentContentProvider('sc', { diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts index 1deca1ad443..a1e85fc4daf 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts @@ -488,7 +488,21 @@ suite('workspace-namespace', () => { }); test('findFiles', () => { - return vscode.workspace.findFiles('*.png').then((res) => { + return vscode.workspace.findFiles('**/*.png').then((res) => { + assert.equal(res.length, 2); + assert.equal(basename(vscode.workspace.asRelativePath(res[0])), 'image.png'); + }); + }); + + test('findFiles - exclude', () => { + return vscode.workspace.findFiles('**/*.png').then((res) => { + assert.equal(res.length, 2); + assert.equal(basename(vscode.workspace.asRelativePath(res[0])), 'image.png'); + }); + }); + + test('findFiles, exclude', () => { + return vscode.workspace.findFiles('**/*.png', '**/sub/**').then((res) => { assert.equal(res.length, 1); assert.equal(basename(vscode.workspace.asRelativePath(res[0])), 'image.png'); }); diff --git a/extensions/yarn.lock b/extensions/yarn.lock index 568fe29f5bf..abf61b0ecfa 100644 --- a/extensions/yarn.lock +++ b/extensions/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -typescript@3.3.3333: - version "3.3.3333" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.3.3333.tgz#171b2c5af66c59e9431199117a3bcadc66fdcfd6" - integrity sha512-JjSKsAfuHBE/fB2oZ8NxtRTk5iGcg6hkYXMnZ3Wc+b2RSqejEqTaem11mHASMnFilHrax3sLK0GDzcJrekZYLw== +typescript@3.3.3: + version "3.3.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.3.3.tgz#f1657fc7daa27e1a8930758ace9ae8da31403221" + integrity sha512-Y21Xqe54TBVp+VDSNbuDYdGw0BpoR/Q6wo/+35M8PAU0vipahnyduJWirxxdxjsAkS7hue53x2zp8gz7F05u0A== diff --git a/package.json b/package.json index d392e69ab0c..8eddb0f8231 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "code-oss-dev", - "version": "1.32.0", + "version": "1.33.0", "distro": "d7aadf23b016949c24a43083a0b3deecb8aa81fa", "author": { "name": "Microsoft Corporation" @@ -28,7 +28,7 @@ }, "dependencies": { "applicationinsights": "1.0.8", - "gc-signals": "^0.0.1", + "gc-signals": "^0.0.2", "getmac": "1.4.1", "graceful-fs": "4.1.11", "http-proxy-agent": "^2.1.0", @@ -97,7 +97,7 @@ "gulp-rename": "^1.2.0", "gulp-replace": "^0.5.4", "gulp-shell": "^0.6.5", - "gulp-tsb": "2.0.6", + "gulp-tsb": "2.0.7", "gulp-tslint": "^8.1.3", "gulp-uglify": "^3.0.0", "gulp-vinyl-zip": "^2.1.2", diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index 315d356b476..9ececdd3e50 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -14,13 +14,23 @@ "./vs/workbench/browser/parts/quickinput/**/*", "./vs/workbench/electron-browser/actions/**/*", "./vs/workbench/contrib/emmet/**/*", - "./vs/workbench/contrib/execution/**/*", + "./vs/workbench/contrib/externalTerminal/**/*", + "./vs/workbench/contrib/scm/**/*.ts", "./vs/workbench/contrib/snippets/**/*.ts", + "./vs/workbench/contrib/outline/**/*.ts", + "./vs/workbench/contrib/performance/**/*.ts", "./vs/workbench/contrib/welcome/**/*.ts", "./vs/workbench/contrib/issue/**/*", + "./vs/workbench/contrib/splash/**/*.ts", + "./vs/workbench/contrib/tasks/**/*.ts", "./vs/workbench/services/commands/**/*", "./vs/workbench/services/files/node/watcher/**/*", - "./vs/workbench/services/themes/**/*.ts" + "./vs/workbench/services/themes/**/*.ts", + "./vs/workbench/services/bulkEdit/**/*.ts", + "./vs/workbench/services/progress/**/*.ts", + "./vs/workbench/services/preferences/**/*.ts", + "./vs/workbench/services/timer/**/*.ts", + "./vs/workbench/contrib/webview/**/*.ts" ], "files": [ "./vs/monaco.d.ts", @@ -28,6 +38,7 @@ "./vs/nls.mock.ts", "./vs/vscode.d.ts", "./vs/vscode.proposed.d.ts", + "./vs/workbench/api/browser/viewsExtensionPoint.ts", "./vs/workbench/api/common/configurationExtensionPoint.ts", "./vs/workbench/api/common/jsonValidationExtensionPoint.ts", "./vs/workbench/api/common/menusExtensionPoint.ts", @@ -45,6 +56,7 @@ "./vs/workbench/api/electron-browser/mainThreadErrors.ts", "./vs/workbench/api/electron-browser/mainThreadFileSystem.ts", "./vs/workbench/api/electron-browser/mainThreadFileSystemEventService.ts", + "./vs/workbench/api/electron-browser/mainThreadHeapService.ts", "./vs/workbench/api/electron-browser/mainThreadLanguages.ts", "./vs/workbench/api/electron-browser/mainThreadLogService.ts", "./vs/workbench/api/electron-browser/mainThreadMessageService.ts", @@ -52,6 +64,7 @@ "./vs/workbench/api/electron-browser/mainThreadProgress.ts", "./vs/workbench/api/electron-browser/mainThreadQuickOpen.ts", "./vs/workbench/api/electron-browser/mainThreadSCM.ts", + "./vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts", "./vs/workbench/api/electron-browser/mainThreadSearch.ts", "./vs/workbench/api/electron-browser/mainThreadStatusBar.ts", "./vs/workbench/api/electron-browser/mainThreadStorage.ts", @@ -61,50 +74,87 @@ "./vs/workbench/api/electron-browser/mainThreadUrls.ts", "./vs/workbench/api/electron-browser/mainThreadWindow.ts", "./vs/workbench/api/electron-browser/mainThreadWorkspace.ts", + "./vs/workbench/api/node/apiCommands.ts", "./vs/workbench/api/node/extHost.protocol.ts", + "./vs/workbench/api/node/extHostCLIServer.ts", "./vs/workbench/api/node/extHostClipboard.ts", + "./vs/workbench/api/node/extHostCommands.ts", "./vs/workbench/api/node/extHostConfiguration.ts", "./vs/workbench/api/node/extHostDecorations.ts", + "./vs/workbench/api/node/extHostDiagnostics.ts", "./vs/workbench/api/node/extHostDialogs.ts", + "./vs/workbench/api/node/extHostDocumentContentProviders.ts", "./vs/workbench/api/node/extHostDocumentData.ts", + "./vs/workbench/api/node/extHostDocumentSaveParticipant.ts", + "./vs/workbench/api/node/extHostDocuments.ts", + "./vs/workbench/api/node/extHostDocumentsAndEditors.ts", "./vs/workbench/api/node/extHostExtensionActivator.ts", + "./vs/workbench/api/node/extHostFileSystemEventService.ts", "./vs/workbench/api/node/extHostHeapService.ts", + "./vs/workbench/api/node/extHostLanguages.ts", "./vs/workbench/api/node/extHostLogService.ts", "./vs/workbench/api/node/extHostMessageService.ts", "./vs/workbench/api/node/extHostOutputService.ts", + "./vs/workbench/api/node/extHostProgress.ts", + "./vs/workbench/api/node/extHostQuickOpen.ts", + "./vs/workbench/api/node/extHostSCM.ts", "./vs/workbench/api/node/extHostSearch.fileIndex.ts", "./vs/workbench/api/node/extHostSearch.ts", "./vs/workbench/api/node/extHostStorage.ts", + "./vs/workbench/api/node/extHostTextEditor.ts", + "./vs/workbench/api/node/extHostTextEditors.ts", + "./vs/workbench/api/node/extHostTypeConverters.ts", "./vs/workbench/api/node/extHostTypes.ts", "./vs/workbench/api/node/extHostUrls.ts", + "./vs/workbench/api/node/extHostWebview.ts", "./vs/workbench/api/node/extHostWindow.ts", "./vs/workbench/api/node/extHostWorkspace.ts", "./vs/workbench/api/shared/editor.ts", "./vs/workbench/api/shared/tasks.ts", "./vs/workbench/browser/actions.ts", "./vs/workbench/browser/actions/layoutActions.ts", + "./vs/workbench/browser/actions/listCommands.ts", "./vs/workbench/browser/actions/navigationActions.ts", "./vs/workbench/browser/actions/workspaceActions.ts", "./vs/workbench/browser/actions/workspaceCommands.ts", "./vs/workbench/browser/composite.ts", "./vs/workbench/browser/contextkeys.ts", + "./vs/workbench/browser/dnd.ts", "./vs/workbench/browser/editor.ts", + "./vs/workbench/browser/labels.ts", "./vs/workbench/browser/panel.ts", "./vs/workbench/browser/part.ts", + "./vs/workbench/browser/parts/activitybar/activitybarActions.ts", + "./vs/workbench/browser/parts/activitybar/activitybarPart.ts", + "./vs/workbench/browser/parts/compositeBar.ts", + "./vs/workbench/browser/parts/compositeBarActions.ts", "./vs/workbench/browser/parts/compositePart.ts", "./vs/workbench/browser/parts/editor/baseEditor.ts", "./vs/workbench/browser/parts/editor/binaryDiffEditor.ts", "./vs/workbench/browser/parts/editor/binaryEditor.ts", "./vs/workbench/browser/parts/editor/breadcrumbs.ts", + "./vs/workbench/browser/parts/editor/breadcrumbsControl.ts", "./vs/workbench/browser/parts/editor/breadcrumbsModel.ts", + "./vs/workbench/browser/parts/editor/breadcrumbsPicker.ts", "./vs/workbench/browser/parts/editor/editor.ts", + "./vs/workbench/browser/parts/editor/editorActions.ts", + "./vs/workbench/browser/parts/editor/editorCommands.ts", "./vs/workbench/browser/parts/editor/editorControl.ts", + "./vs/workbench/browser/parts/editor/editorDropTarget.ts", + "./vs/workbench/browser/parts/editor/editorGroupView.ts", "./vs/workbench/browser/parts/editor/editorPicker.ts", "./vs/workbench/browser/parts/editor/editorWidgets.ts", + "./vs/workbench/browser/parts/editor/noTabsTitleControl.ts", "./vs/workbench/browser/parts/editor/rangeDecorations.ts", "./vs/workbench/browser/parts/editor/resourceViewer.ts", "./vs/workbench/browser/parts/editor/sideBySideEditor.ts", + "./vs/workbench/browser/parts/editor/tabsTitleControl.ts", + "./vs/workbench/browser/parts/editor/textDiffEditor.ts", "./vs/workbench/browser/parts/editor/textEditor.ts", + "./vs/workbench/browser/parts/editor/textResourceEditor.ts", + "./vs/workbench/browser/parts/editor/titleControl.ts", + "./vs/workbench/browser/parts/panel/panelActions.ts", + "./vs/workbench/browser/parts/panel/panelPart.ts", "./vs/workbench/browser/parts/quickinput/quickInputBox.ts", "./vs/workbench/browser/parts/quickinput/quickInputList.ts", "./vs/workbench/browser/parts/quickinput/quickInputUtils.ts", @@ -114,6 +164,9 @@ "./vs/workbench/browser/parts/sidebar/sidebarPart.ts", "./vs/workbench/browser/parts/statusbar/statusbar.ts", "./vs/workbench/browser/parts/statusbar/statusbarPart.ts", + "./vs/workbench/browser/parts/titlebar/menubarControl.ts", + "./vs/workbench/browser/parts/titlebar/titlebarPart.ts", + "./vs/workbench/browser/parts/views/customView.ts", "./vs/workbench/browser/parts/views/panelViewlet.ts", "./vs/workbench/browser/parts/views/views.ts", "./vs/workbench/browser/parts/views/viewsViewlet.ts", @@ -130,8 +183,15 @@ "./vs/workbench/common/editor.ts", "./vs/workbench/common/editor/binaryEditorModel.ts", "./vs/workbench/common/editor/dataUriEditorInput.ts", + "./vs/workbench/common/editor/diffEditorInput.ts", "./vs/workbench/common/editor/diffEditorModel.ts", "./vs/workbench/common/editor/editorGroup.ts", + "./vs/workbench/common/editor/resourceEditorInput.ts", + "./vs/workbench/common/editor/resourceEditorModel.ts", + "./vs/workbench/common/editor/textDiffEditorModel.ts", + "./vs/workbench/common/editor/textEditorModel.ts", + "./vs/workbench/common/editor/untitledEditorInput.ts", + "./vs/workbench/common/editor/untitledEditorModel.ts", "./vs/workbench/common/memento.ts", "./vs/workbench/common/notifications.ts", "./vs/workbench/common/panel.ts", @@ -139,6 +199,8 @@ "./vs/workbench/common/theme.ts", "./vs/workbench/common/viewlet.ts", "./vs/workbench/common/views.ts", + "./vs/workbench/contrib/backup/common/backup.contribution.ts", + "./vs/workbench/contrib/backup/common/backupModelTracker.ts", "./vs/workbench/contrib/backup/common/backupRestorer.ts", "./vs/workbench/contrib/cli/node/cli.contribution.ts", "./vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.ts", @@ -193,20 +255,33 @@ "./vs/workbench/contrib/extensions/common/extensions.ts", "./vs/workbench/contrib/extensions/common/extensionsFileTemplate.ts", "./vs/workbench/contrib/extensions/common/extensionsInput.ts", + "./vs/workbench/contrib/extensions/common/extensionsUtils.ts", + "./vs/workbench/contrib/extensions/electron-browser/extensionEditor.ts", + "./vs/workbench/contrib/extensions/electron-browser/extensionProfileService.ts", "./vs/workbench/contrib/extensions/electron-browser/extensionTipsService.ts", + "./vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts", "./vs/workbench/contrib/extensions/electron-browser/extensionsActions.ts", "./vs/workbench/contrib/extensions/electron-browser/extensionsActivationProgress.ts", + "./vs/workbench/contrib/extensions/electron-browser/extensionsAutoProfiler.ts", "./vs/workbench/contrib/extensions/electron-browser/extensionsList.ts", - "./vs/workbench/contrib/extensions/electron-browser/extensionsUtils.ts", + "./vs/workbench/contrib/extensions/electron-browser/extensionsViewlet.ts", + "./vs/workbench/contrib/extensions/electron-browser/extensionsViews.ts", "./vs/workbench/contrib/extensions/electron-browser/extensionsWidgets.ts", + "./vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts", "./vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsInput.ts", "./vs/workbench/contrib/extensions/node/extensionsWorkbenchService.ts", "./vs/workbench/contrib/extensions/test/common/extensionQuery.test.ts", "./vs/workbench/contrib/feedback/electron-browser/feedback.contribution.ts", "./vs/workbench/contrib/feedback/electron-browser/feedback.ts", "./vs/workbench/contrib/feedback/electron-browser/feedbackStatusbarItem.ts", + "./vs/workbench/contrib/files/browser/editors/binaryFileEditor.ts", + "./vs/workbench/contrib/files/browser/editors/fileEditorTracker.ts", + "./vs/workbench/contrib/files/browser/editors/textFileEditor.ts", "./vs/workbench/contrib/files/browser/files.ts", + "./vs/workbench/contrib/files/browser/views/emptyView.ts", "./vs/workbench/contrib/files/browser/views/explorerDecorationsProvider.ts", + "./vs/workbench/contrib/files/common/dirtyFilesTracker.ts", + "./vs/workbench/contrib/files/common/editors/fileEditorInput.ts", "./vs/workbench/contrib/files/common/explorerModel.ts", "./vs/workbench/contrib/files/common/explorerService.ts", "./vs/workbench/contrib/files/common/files.ts", @@ -218,20 +293,33 @@ "./vs/workbench/contrib/logs/common/logs.contribution.ts", "./vs/workbench/contrib/logs/common/logsActions.ts", "./vs/workbench/contrib/markers/browser/constants.ts", + "./vs/workbench/contrib/markers/browser/markers.contribution.ts", "./vs/workbench/contrib/markers/browser/markers.ts", "./vs/workbench/contrib/markers/browser/markersFileDecorations.ts", "./vs/workbench/contrib/markers/browser/markersFilterOptions.ts", "./vs/workbench/contrib/markers/browser/markersModel.ts", + "./vs/workbench/contrib/markers/browser/markersPanel.ts", "./vs/workbench/contrib/markers/browser/markersPanelActions.ts", + "./vs/workbench/contrib/markers/browser/markersTreeViewer.ts", "./vs/workbench/contrib/markers/browser/messages.ts", "./vs/workbench/contrib/markers/test/electron-browser/markersModel.test.ts", + "./vs/workbench/contrib/output/browser/logViewer.ts", + "./vs/workbench/contrib/output/browser/outputActions.ts", + "./vs/workbench/contrib/output/browser/outputPanel.ts", "./vs/workbench/contrib/output/common/output.ts", "./vs/workbench/contrib/output/common/outputLinkComputer.ts", "./vs/workbench/contrib/output/common/outputLinkProvider.ts", + "./vs/workbench/contrib/output/electron-browser/output.contribution.ts", + "./vs/workbench/contrib/output/electron-browser/outputServices.ts", "./vs/workbench/contrib/output/node/outputAppender.ts", - "./vs/workbench/contrib/performance/electron-browser/startupTimings.ts", + "./vs/workbench/contrib/preferences/browser/preferencesActions.ts", + "./vs/workbench/contrib/preferences/browser/preferencesWidgets.ts", + "./vs/workbench/contrib/preferences/browser/settingsLayout.ts", "./vs/workbench/contrib/preferences/browser/settingsWidgets.ts", + "./vs/workbench/contrib/preferences/common/preferences.ts", + "./vs/workbench/contrib/preferences/common/preferencesContribution.ts", "./vs/workbench/contrib/preferences/common/smartSnippetInserter.ts", + "./vs/workbench/contrib/preferences/electron-browser/preferencesSearch.ts", "./vs/workbench/contrib/preferences/test/common/smartSnippetInserter.test.ts", "./vs/workbench/contrib/quickopen/browser/commandsHandler.ts", "./vs/workbench/contrib/quickopen/browser/gotoLineHandler.ts", @@ -240,12 +328,12 @@ "./vs/workbench/contrib/quickopen/browser/quickopen.contribution.ts", "./vs/workbench/contrib/quickopen/browser/viewPickerHandler.ts", "./vs/workbench/contrib/relauncher/electron-browser/relauncher.contribution.ts", + "./vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts", + "./vs/workbench/contrib/scm/browser/scmActivity.ts", + "./vs/workbench/contrib/scm/browser/scmMenus.ts", + "./vs/workbench/contrib/scm/browser/scmUtil.ts", "./vs/workbench/contrib/scm/common/scm.ts", "./vs/workbench/contrib/scm/common/scmService.ts", - "./vs/workbench/contrib/scm/electron-browser/dirtydiffDecorator.ts", - "./vs/workbench/contrib/scm/electron-browser/scmActivity.ts", - "./vs/workbench/contrib/scm/electron-browser/scmMenus.ts", - "./vs/workbench/contrib/scm/electron-browser/scmUtil.ts", "./vs/workbench/contrib/search/browser/openAnythingHandler.ts", "./vs/workbench/contrib/search/browser/openFileHandler.ts", "./vs/workbench/contrib/search/browser/openSymbolHandler.ts", @@ -262,71 +350,61 @@ "./vs/workbench/contrib/search/test/browser/openFileHandler.test.ts", "./vs/workbench/contrib/search/test/common/searchModel.test.ts", "./vs/workbench/contrib/search/test/common/searchResult.test.ts", - "./vs/workbench/contrib/splash/electron-browser/partsSplash.contribution.ts", "./vs/workbench/contrib/stats/node/stats.contribution.ts", "./vs/workbench/contrib/stats/node/workspaceStats.ts", "./vs/workbench/contrib/stats/test/workspaceStats.test.ts", "./vs/workbench/contrib/surveys/electron-browser/languageSurveys.contribution.ts", "./vs/workbench/contrib/surveys/electron-browser/nps.contribution.ts", - "./vs/workbench/contrib/tasks/browser/quickOpen.ts", - "./vs/workbench/contrib/tasks/browser/taskQuickOpen.ts", - "./vs/workbench/contrib/tasks/common/problemCollectors.ts", - "./vs/workbench/contrib/tasks/common/problemMatcher.ts", - "./vs/workbench/contrib/tasks/common/taskDefinitionRegistry.ts", - "./vs/workbench/contrib/tasks/common/taskService.ts", - "./vs/workbench/contrib/tasks/common/taskSystem.ts", - "./vs/workbench/contrib/tasks/common/taskTemplates.ts", - "./vs/workbench/contrib/tasks/common/tasks.ts", - "./vs/workbench/contrib/tasks/electron-browser/jsonSchemaCommon.ts", - "./vs/workbench/contrib/tasks/electron-browser/jsonSchema_v1.ts", - "./vs/workbench/contrib/tasks/electron-browser/jsonSchema_v2.ts", - "./vs/workbench/contrib/tasks/electron-browser/runAutomaticTasks.ts", - "./vs/workbench/contrib/tasks/electron-browser/terminalTaskSystem.ts", - "./vs/workbench/contrib/tasks/node/processRunnerDetector.ts", - "./vs/workbench/contrib/tasks/node/processTaskSystem.ts", - "./vs/workbench/contrib/tasks/node/taskConfiguration.ts", - "./vs/workbench/contrib/tasks/node/tasks.ts", - "./vs/workbench/contrib/tasks/test/common/problemMatcher.test.ts", - "./vs/workbench/contrib/tasks/test/electron-browser/configuration.test.ts", + "./vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts", + "./vs/workbench/contrib/terminal/browser/terminal.contribution.ts", + "./vs/workbench/contrib/terminal/browser/terminal.ts", + "./vs/workbench/contrib/terminal/browser/terminalActions.ts", + "./vs/workbench/contrib/terminal/browser/terminalCommandTracker.ts", + "./vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts", "./vs/workbench/contrib/terminal/browser/terminalFindWidget.ts", + "./vs/workbench/contrib/terminal/browser/terminalInstance.ts", + "./vs/workbench/contrib/terminal/browser/terminalLinkHandler.ts", + "./vs/workbench/contrib/terminal/browser/terminalPanel.ts", + "./vs/workbench/contrib/terminal/browser/terminalProcessManager.ts", "./vs/workbench/contrib/terminal/browser/terminalQuickOpen.ts", + "./vs/workbench/contrib/terminal/browser/terminalService.ts", "./vs/workbench/contrib/terminal/browser/terminalTab.ts", "./vs/workbench/contrib/terminal/browser/terminalWidgetManager.ts", "./vs/workbench/contrib/terminal/common/terminal.ts", "./vs/workbench/contrib/terminal/common/terminalColorRegistry.ts", "./vs/workbench/contrib/terminal/common/terminalCommands.ts", + "./vs/workbench/contrib/terminal/common/terminalEnvironment.ts", "./vs/workbench/contrib/terminal/common/terminalMenu.ts", + "./vs/workbench/contrib/terminal/common/terminalProcessExtHostProxy.ts", "./vs/workbench/contrib/terminal/common/terminalService.ts", - "./vs/workbench/contrib/terminal/electron-browser/terminalActions.ts", - "./vs/workbench/contrib/terminal/electron-browser/terminalConfigHelper.ts", - "./vs/workbench/contrib/terminal/electron-browser/terminalInstance.ts", - "./vs/workbench/contrib/terminal/electron-browser/terminalLinkHandler.ts", - "./vs/workbench/contrib/terminal/electron-browser/terminalProcessManager.ts", + "./vs/workbench/contrib/terminal/electron-browser/terminal.contribution.ts", + "./vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts", + "./vs/workbench/contrib/terminal/electron-browser/terminalService.ts", "./vs/workbench/contrib/terminal/node/terminal.ts", - "./vs/workbench/contrib/terminal/node/terminalCommandTracker.ts", - "./vs/workbench/contrib/terminal/node/terminalEnvironment.ts", "./vs/workbench/contrib/terminal/node/terminalProcess.ts", - "./vs/workbench/contrib/terminal/node/terminalProcessExtHostProxy.ts", "./vs/workbench/contrib/terminal/node/windowsShellHelper.ts", "./vs/workbench/contrib/terminal/test/electron-browser/terminalColorRegistry.test.ts", + "./vs/workbench/contrib/terminal/test/electron-browser/terminalCommandTracker.test.ts", "./vs/workbench/contrib/terminal/test/electron-browser/terminalConfigHelper.test.ts", "./vs/workbench/contrib/terminal/test/electron-browser/terminalLinkHandler.test.ts", - "./vs/workbench/contrib/terminal/test/node/terminalCommandTracker.test.ts", "./vs/workbench/contrib/terminal/test/node/terminalEnvironment.test.ts", "./vs/workbench/contrib/themes/browser/themes.contribution.ts", "./vs/workbench/contrib/themes/test/electron-browser/themes.test.contribution.ts", + "./vs/workbench/contrib/update/electron-browser/releaseNotesEditor.ts", + "./vs/workbench/contrib/update/electron-browser/update.contribution.ts", + "./vs/workbench/contrib/update/electron-browser/update.ts", "./vs/workbench/contrib/url/common/url.contribution.ts", - "./vs/workbench/contrib/webview/electron-browser/webviewProtocols.ts", "./vs/workbench/electron-browser/window.ts", + "./vs/workbench/services/activity/browser/activityService.ts", "./vs/workbench/services/activity/common/activity.ts", "./vs/workbench/services/backup/common/backup.ts", "./vs/workbench/services/backup/node/backupFileService.ts", "./vs/workbench/services/broadcast/electron-browser/broadcastService.ts", - "./vs/workbench/services/bulkEdit/browser/bulkEditService.ts", "./vs/workbench/services/configuration/common/configuration.ts", "./vs/workbench/services/configuration/common/configurationModels.ts", "./vs/workbench/services/configuration/common/jsonEditing.ts", "./vs/workbench/services/configuration/node/configuration.ts", + "./vs/workbench/services/configuration/node/configurationEditingService.ts", "./vs/workbench/services/configuration/node/jsonEditingService.ts", "./vs/workbench/services/configuration/test/common/configurationModels.test.ts", "./vs/workbench/services/configurationResolver/common/configurationResolver.ts", @@ -340,7 +418,6 @@ "./vs/workbench/services/editor/common/editorGroupsService.ts", "./vs/workbench/services/editor/common/editorService.ts", "./vs/workbench/services/extensionManagement/node/multiExtensionManagement.ts", - "./vs/workbench/services/extensions/common/extensionHostProtocol.ts", "./vs/workbench/services/extensions/common/extensions.ts", "./vs/workbench/services/extensions/common/extensionsRegistry.ts", "./vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts", @@ -348,6 +425,7 @@ "./vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts", "./vs/workbench/services/extensions/electron-browser/inactiveExtensionUrlHandler.ts", "./vs/workbench/services/extensions/node/extensionDescriptionRegistry.ts", + "./vs/workbench/services/extensions/node/extensionHostProtocol.ts", "./vs/workbench/services/extensions/node/extensionManagementServerService.ts", "./vs/workbench/services/extensions/node/extensionPoints.ts", "./vs/workbench/services/extensions/node/lazyPromise.ts", @@ -355,6 +433,8 @@ "./vs/workbench/services/extensions/node/rpcProtocol.ts", "./vs/workbench/services/extensions/test/node/rpcProtocol.test.ts", "./vs/workbench/services/files/node/encoding.ts", + "./vs/workbench/services/files/node/remoteFileService.ts", + "./vs/workbench/services/files/node/fileService.ts", "./vs/workbench/services/files/node/streams.ts", "./vs/workbench/services/files/test/electron-browser/utils.ts", "./vs/workbench/services/files/test/electron-browser/watcher.test.ts", @@ -382,8 +462,6 @@ "./vs/workbench/services/part/common/partService.ts", "./vs/workbench/services/preferences/common/keybindingsEditorModel.ts", "./vs/workbench/services/preferences/test/common/keybindingsEditorModel.test.ts", - "./vs/workbench/services/progress/browser/progressService.ts", - "./vs/workbench/services/progress/test/progressService.test.ts", "./vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl.ts", "./vs/workbench/services/remote/node/remoteAgentEnvironmentChannel.ts", "./vs/workbench/services/remote/node/remoteAgentService.ts", @@ -414,10 +492,14 @@ "./vs/workbench/services/textMate/common/textMateService.ts", "./vs/workbench/services/textMate/electron-browser/TMGrammars.ts", "./vs/workbench/services/textMate/electron-browser/textMateService.ts", + "./vs/workbench/services/textfile/common/textFileEditorModel.ts", + "./vs/workbench/services/textfile/common/textFileEditorModelManager.ts", "./vs/workbench/services/textfile/common/textfiles.ts", "./vs/workbench/services/textfile/node/textResourcePropertiesService.ts", + "./vs/workbench/services/textmodelResolver/common/textModelResolverService.ts", "./vs/workbench/services/timer/electron-browser/timerService.ts", "./vs/workbench/services/title/common/titleService.ts", + "./vs/workbench/services/untitled/common/untitledEditorService.ts", "./vs/workbench/services/viewlet/browser/viewlet.ts", "./vs/workbench/services/workspace/common/workspaceEditing.ts", "./vs/workbench/test/browser/actionRegistry.test.ts", @@ -440,4 +522,4 @@ "./typings/require-monaco.d.ts", "./vs/workbench/contrib/comments/electron-browser/commentThreadWidget.ts" ] -} \ No newline at end of file +} diff --git a/src/vs/base/browser/browser.ts b/src/vs/base/browser/browser.ts index c09210608f3..16414ef34d3 100644 --- a/src/vs/base/browser/browser.ts +++ b/src/vs/base/browser/browser.ts @@ -34,7 +34,7 @@ class WindowManager { } // --- Zoom Factor - private _zoomFactor: number = 0; + private _zoomFactor: number = 1; public getZoomFactor(): number { return this._zoomFactor; diff --git a/src/vs/base/browser/dnd.ts b/src/vs/base/browser/dnd.ts index d7e6571b6f9..6ba70bd072a 100644 --- a/src/vs/base/browser/dnd.ts +++ b/src/vs/base/browser/dnd.ts @@ -16,7 +16,9 @@ export class DelayedDragHandler extends Disposable { constructor(container: HTMLElement, callback: () => void) { super(); - this._register(addDisposableListener(container, 'dragover', () => { + this._register(addDisposableListener(container, 'dragover', e => { + e.preventDefault(); // needed so that the drop event fires (https://stackoverflow.com/questions/21339924/drop-event-not-firing-in-chrome) + if (!this.timeout) { this.timeout = setTimeout(() => { callback(); diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index 2ac95d2b9c6..b74f87d67a3 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -615,7 +615,7 @@ export interface IDomNodePagePosition { height: number; } -export function size(element: HTMLElement, width: number, height: number): void { +export function size(element: HTMLElement, width: number | null, height: number | null): void { if (typeof width === 'number') { element.style.width = `${width}px`; } diff --git a/src/vs/base/browser/ui/list/list.ts b/src/vs/base/browser/ui/list/list.ts index b4a4aa4821f..42097113808 100644 --- a/src/vs/base/browser/ui/list/list.ts +++ b/src/vs/base/browser/ui/list/list.ts @@ -16,8 +16,8 @@ export interface IListVirtualDelegate { export interface IListRenderer { templateId: string; renderTemplate(container: HTMLElement): TTemplateData; - renderElement(element: T, index: number, templateData: TTemplateData): void; - disposeElement?(element: T, index: number, templateData: TTemplateData): void; + renderElement(element: T, index: number, templateData: TTemplateData, dynamicHeightProbing?: boolean): void; + disposeElement?(element: T, index: number, templateData: TTemplateData, dynamicHeightProbing?: boolean): void; disposeTemplate(templateData: TTemplateData): void; } @@ -55,7 +55,7 @@ export interface IListContextMenuEvent { browserEvent: UIEvent; element: T | undefined; index: number | undefined; - anchor: HTMLElement | { x: number; y: number; } | undefined; + anchor: HTMLElement | { x: number; y: number; }; } export interface IIdentityProvider { diff --git a/src/vs/base/browser/ui/list/listPaging.ts b/src/vs/base/browser/ui/list/listPaging.ts index c35537fa7ea..3d1fe415426 100644 --- a/src/vs/base/browser/ui/list/listPaging.ts +++ b/src/vs/base/browser/ui/list/listPaging.ts @@ -35,7 +35,7 @@ class PagedRenderer implements IListRenderer { } } }; } - renderElement(index: number, _: number, data: ITemplateData): void { + renderElement(index: number, _: number, data: ITemplateData, dynamicHeightProbing?: boolean): void { if (data.disposable) { data.disposable.dispose(); } @@ -47,7 +47,7 @@ class PagedRenderer implements IListRenderer implements IListRenderer cts.cancel() }; this.renderer.renderPlaceholder(index, data.data); - promise.then(entry => this.renderer.renderElement(entry, index, data.data!)); + promise.then(entry => this.renderer.renderElement(entry, index, data.data!, dynamicHeightProbing)); } disposeTemplate(data: ITemplateData): void { diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index f5ab01d4155..a4f93a958c7 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -445,6 +445,15 @@ export class ListView implements ISpliceable, IDisposable { get firstVisibleIndex(): number { const range = this.getRenderRange(this.lastRenderTop, this.lastRenderHeight); + const firstElTop = this.rangeMap.positionAt(range.start); + const nextElTop = this.rangeMap.positionAt(range.start + 1); + if (nextElTop !== -1) { + const firstElMidpoint = (nextElTop - firstElTop) / 2 + firstElTop; + if (firstElMidpoint < this.scrollTop) { + return range.start + 1; + } + } + return range.start; } @@ -786,6 +795,8 @@ export class ListView implements ISpliceable, IDisposable { } private onDragOver(event: IListDragEvent): boolean { + event.browserEvent.preventDefault(); // needed so that the drop event fires (https://stackoverflow.com/questions/21339924/drop-event-not-firing-in-chrome) + this.onDragLeaveTimeout.dispose(); if (StaticDND.CurrentDragAndDropData && StaticDND.CurrentDragAndDropData.getData() === 'vscode-ui') { @@ -1079,14 +1090,20 @@ export class ListView implements ISpliceable, IDisposable { } const size = item.size; - const renderer = this.renderers.get(item.templateId); const row = this.cache.alloc(item.templateId); row.domNode!.style.height = ''; this.rowsContainer.appendChild(row.domNode!); + + const renderer = this.renderers.get(item.templateId); if (renderer) { - renderer.renderElement(item.element, index, row.templateData); + renderer.renderElement(item.element, index, row.templateData, true); + + if (renderer.disposeElement) { + renderer.disposeElement(item.element, index, row.templateData, true); + } } + item.size = row.domNode!.offsetHeight; item.lastDynamicHeightWidth = this.renderWidth; this.rowsContainer.removeChild(row.domNode!); diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 8aa79431056..197fcb2ab98 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -976,20 +976,20 @@ class PipelineRenderer implements IListRenderer { return this.renderers.map(r => r.renderTemplate(container)); } - renderElement(element: T, index: number, templateData: any[]): void { + renderElement(element: T, index: number, templateData: any[], dynamicHeightProbing?: boolean): void { let i = 0; for (const renderer of this.renderers) { - renderer.renderElement(element, index, templateData[i++]); + renderer.renderElement(element, index, templateData[i++], dynamicHeightProbing); } } - disposeElement(element: T, index: number, templateData: any[]): void { + disposeElement(element: T, index: number, templateData: any[], dynamicHeightProbing?: boolean): void { let i = 0; for (const renderer of this.renderers) { if (renderer.disposeElement) { - renderer.disposeElement(element, index, templateData[i]); + renderer.disposeElement(element, index, templateData[i], dynamicHeightProbing); } i += 1; @@ -1127,13 +1127,7 @@ export class List implements ISpliceable, IDisposable { .map(e => new StandardKeyboardEvent(e)) .filter(e => this.didJustPressContextMenuKey = e.keyCode === KeyCode.ContextMenu || (e.shiftKey && e.keyCode === KeyCode.F10)) .filter(e => { e.preventDefault(); e.stopPropagation(); return false; }) - .map(event => { - const index = this.getFocus()[0]; - const element = this.view.element(index); - const anchor = this.view.domElement(index) || undefined; - return { index, element, anchor, browserEvent: event.browserEvent }; - }) - .event; + .event as Event; const fromKeyup = Event.chain(domEvent(this.view.domNode, 'keyup')) .filter(() => { @@ -1141,14 +1135,13 @@ export class List implements ISpliceable, IDisposable { this.didJustPressContextMenuKey = false; return didJustPressContextMenuKey; }) - .filter(() => this.getFocus().length > 0) + .filter(() => this.getFocus().length > 0 && !!this.view.domElement(this.getFocus()[0])) .map(browserEvent => { const index = this.getFocus()[0]; const element = this.view.element(index); - const anchor = this.view.domElement(index) || undefined; + const anchor = this.view.domElement(index) as HTMLElement; return { index, element, anchor, browserEvent }; }) - .filter(({ anchor }) => !!anchor) .event; const fromMouse = Event.chain(this.view.onContextMenu) diff --git a/src/vs/base/browser/ui/menu/menu.ts b/src/vs/base/browser/ui/menu/menu.ts index c2b6a7ea36d..26d89db30a2 100644 --- a/src/vs/base/browser/ui/menu/menu.ts +++ b/src/vs/base/browser/ui/menu/menu.ts @@ -8,7 +8,7 @@ import * as nls from 'vs/nls'; import * as strings from 'vs/base/common/strings'; import { IActionRunner, IAction, Action } from 'vs/base/common/actions'; import { ActionBar, IActionItemProvider, ActionsOrientation, Separator, ActionItem, IActionItemOptions, BaseActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; -import { ResolvedKeybinding, KeyCode, KeyCodeUtils } from 'vs/base/common/keyCodes'; +import { ResolvedKeybinding, KeyCode } from 'vs/base/common/keyCodes'; import { addClass, EventType, EventHelper, EventLike, removeTabIndexAndUpdateFocus, isAncestor, hasClass, addDisposableListener, removeClass, append, $, addClasses, removeClasses } from 'vs/base/browser/dom'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { RunOnceScheduler } from 'vs/base/common/async'; @@ -20,8 +20,22 @@ import { Event, Emitter } from 'vs/base/common/event'; import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; import { isLinux } from 'vs/base/common/platform'; -export const MENU_MNEMONIC_REGEX: RegExp = /\(&(\w)\)|(?>; + private mnemonics: Map>; private menuDisposables: IDisposable[]; private scrollableElement: DomScrollableElement; private menuElement: HTMLElement; @@ -93,7 +107,7 @@ export class Menu extends ActionBar { if (options.enableMnemonics) { this.menuDisposables.push(addDisposableListener(menuElement, EventType.KEY_DOWN, (e) => { - const key = KeyCodeUtils.fromString(e.key); + const key = e.key.toLocaleLowerCase(); if (this.mnemonics.has(key)) { EventHelper.stop(e, true); const actions = this.mnemonics.get(key)!; @@ -175,7 +189,7 @@ export class Menu extends ActionBar { parent: this }; - this.mnemonics = new Map>(); + this.mnemonics = new Map>(); this.push(actions, { icon: true, label: true, isMenu: true }); @@ -349,7 +363,7 @@ class MenuActionItem extends BaseActionItem { private label: HTMLElement; private check: HTMLElement; - private mnemonic: KeyCode; + private mnemonic: string; private cssClass: string; protected menuStyle: IMenuStyles; @@ -368,7 +382,7 @@ class MenuActionItem extends BaseActionItem { if (label) { let matches = MENU_MNEMONIC_REGEX.exec(label); if (matches) { - this.mnemonic = KeyCodeUtils.fromString((!!matches[1] ? matches[1] : matches[2]).toLocaleLowerCase()); + this.mnemonic = (!!matches[1] ? matches[1] : matches[2]).toLocaleLowerCase(); } } } @@ -522,7 +536,7 @@ class MenuActionItem extends BaseActionItem { } } - getMnemonic(): KeyCode { + getMnemonic(): string { return this.mnemonic; } diff --git a/src/vs/base/browser/ui/menu/menubar.ts b/src/vs/base/browser/ui/menu/menubar.ts index 02e64243b56..a4283053ab0 100644 --- a/src/vs/base/browser/ui/menu/menubar.ts +++ b/src/vs/base/browser/ui/menu/menubar.ts @@ -14,7 +14,7 @@ import { cleanMnemonic, IMenuOptions, Menu, MENU_ESCAPED_MNEMONIC_REGEX, MENU_MN import { ActionRunner, IAction, IActionRunner } from 'vs/base/common/actions'; import { RunOnceScheduler } from 'vs/base/common/async'; import { Event, Emitter } from 'vs/base/common/event'; -import { KeyCode, KeyCodeUtils, ResolvedKeybinding } from 'vs/base/common/keyCodes'; +import { KeyCode, ResolvedKeybinding } from 'vs/base/common/keyCodes'; import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; const $ = DOM.$; @@ -22,7 +22,7 @@ const $ = DOM.$; export interface IMenuBarOptions { enableMnemonics?: boolean; visibility?: string; - getKeybinding?: (action: IAction) => ResolvedKeybinding; + getKeybinding?: (action: IAction) => ResolvedKeybinding | undefined; alwaysOnMnemonics?: boolean; } @@ -70,7 +70,7 @@ export class MenuBar extends Disposable { private openedViaKeyboard: boolean; private awaitingAltRelease: boolean; private ignoreNextMouseUp: boolean; - private mnemonics: Map; + private mnemonics: Map; private updatePending: boolean; private _focusState: MenubarState; @@ -89,7 +89,7 @@ export class MenuBar extends Disposable { this.container.attributes['role'] = 'menubar'; this.menuCache = []; - this.mnemonics = new Map(); + this.mnemonics = new Map(); this._focusState = MenubarState.VISIBLE; @@ -110,7 +110,7 @@ export class MenuBar extends Disposable { this._register(DOM.addDisposableListener(this.container, DOM.EventType.KEY_DOWN, (e) => { let event = new StandardKeyboardEvent(e as KeyboardEvent); let eventHandled = true; - const key = !!e.key ? KeyCodeUtils.fromString(e.key) : KeyCode.Unknown; + const key = !!e.key ? e.key.toLocaleLowerCase() : ''; if (event.equals(KeyCode.LeftArrow)) { this.focusPrevious(); @@ -162,7 +162,7 @@ export class MenuBar extends Disposable { return; } - const key = KeyCodeUtils.fromString(e.key); + const key = e.key.toLocaleLowerCase(); if (!this.mnemonics.has(key)) { return; } @@ -505,7 +505,7 @@ export class MenuBar extends Disposable { } private registerMnemonic(menuIndex: number, mnemonic: string): void { - this.mnemonics.set(KeyCodeUtils.fromString(mnemonic), menuIndex); + this.mnemonics.set(mnemonic.toLocaleLowerCase(), menuIndex); } private hideMenubar(): void { @@ -954,6 +954,16 @@ class ModifierKeyEmitter extends Emitter { this._keyStatus.lastKeyPressed = undefined; })); + this._subscriptions.push(domEvent(document.body, 'mouseup', true)(e => { + this._keyStatus.lastKeyPressed = undefined; + })); + + this._subscriptions.push(domEvent(document.body, 'mousemove', true)(e => { + if (e.buttons) { + this._keyStatus.lastKeyPressed = undefined; + } + })); + this._subscriptions.push(domEvent(window, 'blur')(e => { this._keyStatus.lastKeyPressed = undefined; this._keyStatus.lastKeyReleased = undefined; diff --git a/src/vs/base/browser/ui/splitview/panelview.ts b/src/vs/base/browser/ui/splitview/panelview.ts index fb9897920a5..e83b324542d 100644 --- a/src/vs/base/browser/ui/splitview/panelview.ts +++ b/src/vs/base/browser/ui/splitview/panelview.ts @@ -172,8 +172,9 @@ export abstract class Panel implements IView { this.renderHeader(this.header); const focusTracker = trackFocus(this.header); - focusTracker.onDidFocus(() => addClass(this.header, 'focused')); - focusTracker.onDidBlur(() => removeClass(this.header, 'focused')); + this.disposables.push(focusTracker); + focusTracker.onDidFocus(() => addClass(this.header, 'focused'), null, this.disposables); + focusTracker.onDidBlur(() => removeClass(this.header, 'focused'), null, this.disposables); this.updateHeader(); diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 9edbb3174bb..52f31490996 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -233,23 +233,28 @@ class TreeRenderer implements IListRenderer, index: number, templateData: ITreeListTemplateData): void { - this.renderedNodes.set(node, templateData); - this.renderedElements.set(node.element, node); + renderElement(node: ITreeNode, index: number, templateData: ITreeListTemplateData, dynamicHeightProbing?: boolean): void { + if (!dynamicHeightProbing) { + this.renderedNodes.set(node, templateData); + this.renderedElements.set(node.element, node); + } const indent = TreeRenderer.DefaultIndent + (node.depth - 1) * this.indent; templateData.twistie.style.marginLeft = `${indent}px`; this.update(node, templateData); - this.renderer.renderElement(node, index, templateData.templateData); + this.renderer.renderElement(node, index, templateData.templateData, dynamicHeightProbing); } - disposeElement(node: ITreeNode, index: number, templateData: ITreeListTemplateData): void { + disposeElement(node: ITreeNode, index: number, templateData: ITreeListTemplateData, dynamicHeightProbing?: boolean): void { if (this.renderer.disposeElement) { - this.renderer.disposeElement(node, index, templateData.templateData); + this.renderer.disposeElement(node, index, templateData.templateData, dynamicHeightProbing); + } + + if (!dynamicHeightProbing) { + this.renderedNodes.delete(node); + this.renderedElements.delete(node.element); } - this.renderedNodes.delete(node); - this.renderedElements.delete(node.element); } disposeTemplate(templateData: ITreeListTemplateData): void { @@ -598,6 +603,8 @@ class TypeFilterController implements IDisposable { }; const onDragOver = (event: DragEvent) => { + event.preventDefault(); // needed so that the drop event fires (https://stackoverflow.com/questions/21339924/drop-event-not-firing-in-chrome) + const x = event.screenX - left; if (event.dataTransfer) { event.dataTransfer.dropEffect = 'none'; diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index d8b97395fbf..21b5b7fdef7 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -17,24 +17,35 @@ import { ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView'; import { isPromiseCanceledError, onUnexpectedError } from 'vs/base/common/errors'; import { toggleClass } from 'vs/base/browser/dom'; -const enum AsyncDataTreeNodeState { - Uninitialized = 'uninitialized', - Loaded = 'loaded', - Loading = 'loading' -} - interface IAsyncDataTreeNode { element: TInput | T; readonly parent: IAsyncDataTreeNode | null; readonly children: IAsyncDataTreeNode[]; readonly id?: string | null; - state: AsyncDataTreeNodeState; + loading: boolean; hasChildren: boolean; - needsRefresh: boolean; + stale: boolean; slow: boolean; disposed: boolean; } +interface IAsyncDataTreeNodeRequiredProps extends Partial> { + readonly element: TInput | T; + readonly parent: IAsyncDataTreeNode | null; + readonly hasChildren: boolean; +} + +function createAsyncDataTreeNode(props: IAsyncDataTreeNodeRequiredProps): IAsyncDataTreeNode { + return { + ...props, + children: [], + loading: false, + stale: true, + disposed: false, + slow: false + }; +} + function isAncestor(ancestor: IAsyncDataTreeNode, descendant: IAsyncDataTreeNode): boolean { if (!descendant.parent) { return false; @@ -87,8 +98,8 @@ class DataTreeRenderer implements ITreeRe return { templateData }; } - renderElement(node: ITreeNode, TFilterData>, index: number, templateData: IDataTreeListTemplateData): void { - this.renderer.renderElement(new AsyncDataTreeNodeWrapper(node), index, templateData.templateData); + renderElement(node: ITreeNode, TFilterData>, index: number, templateData: IDataTreeListTemplateData, dynamicHeightProbing?: boolean): void { + this.renderer.renderElement(new AsyncDataTreeNodeWrapper(node), index, templateData.templateData, dynamicHeightProbing); } renderTwistie(element: IAsyncDataTreeNode, twistieElement: HTMLElement): boolean { @@ -96,9 +107,9 @@ class DataTreeRenderer implements ITreeRe return false; } - disposeElement(node: ITreeNode, TFilterData>, index: number, templateData: IDataTreeListTemplateData): void { + disposeElement(node: ITreeNode, TFilterData>, index: number, templateData: IDataTreeListTemplateData, dynamicHeightProbing?: boolean): void { if (this.renderer.disposeElement) { - this.renderer.disposeElement(new AsyncDataTreeNodeWrapper(node), index, templateData.templateData); + this.renderer.disposeElement(new AsyncDataTreeNodeWrapper(node), index, templateData.templateData, dynamicHeightProbing); } } @@ -236,7 +247,7 @@ function asTreeElement(node: IAsyncDataTreeNode, viewState return { element: node, - children: Iterator.map(Iterator.fromArray(node.children), child => asTreeElement(child, viewStateContext)), + children: node.hasChildren ? Iterator.map(Iterator.fromArray(node.children), child => asTreeElement(child, viewStateContext)) : [], collapsible: node.hasChildren, collapsed }; @@ -287,6 +298,7 @@ export class AsyncDataTree implements IDisposable get onDidChangeSelection(): Event> { return Event.map(this.tree.onDidChangeSelection, asTreeEvent); } get onDidOpen(): Event> { return Event.map(this.tree.onDidOpen, asTreeEvent); } + get onKeyDown(): Event { return this.tree.onKeyDown; } get onMouseClick(): Event> { return Event.map(this.tree.onMouseClick, asTreeMouseEvent); } get onMouseDblClick(): Event> { return Event.map(this.tree.onMouseDblClick, asTreeMouseEvent); } get onContextMenu(): Event> { return Event.map(this.tree.onContextMenu, asTreeContextMenuEvent); } @@ -317,16 +329,11 @@ export class AsyncDataTree implements IDisposable this.tree = new ObjectTree(container, objectTreeDelegate, objectTreeRenderers, objectTreeOptions); - this.root = { + this.root = createAsyncDataTreeNode({ element: undefined!, parent: null, - children: [], - state: AsyncDataTreeNodeState.Uninitialized, - hasChildren: true, - needsRefresh: false, - disposed: false, - slow: false - }; + hasChildren: true + }); if (this.identityProvider) { this.root = { @@ -425,7 +432,7 @@ export class AsyncDataTree implements IDisposable throw new Error('Tree input not set'); } - if (this.root.state === AsyncDataTreeNodeState.Loading) { + if (this.root.loading) { await this.subTreeRefreshPromises.get(this.root)!; await Event.toPromise(this._onDidRender.event); } @@ -476,20 +483,20 @@ export class AsyncDataTree implements IDisposable throw new Error('Tree input not set'); } - if (this.root.state === AsyncDataTreeNodeState.Loading) { + if (this.root.loading) { await this.subTreeRefreshPromises.get(this.root)!; await Event.toPromise(this._onDidRender.event); } const node = this.getDataNode(element); - if (node !== this.root && node.state !== AsyncDataTreeNodeState.Loading && !this.tree.isCollapsed(node)) { + if (node !== this.root && !node.loading && !this.tree.isCollapsed(node)) { return false; } const result = this.tree.expand(node === this.root ? null : node, recursive); - if (node.state === AsyncDataTreeNodeState.Loading) { + if (node.loading) { await this.subTreeRefreshPromises.get(node)!; await Event.toPromise(this._onDidRender.event); } @@ -650,39 +657,19 @@ export class AsyncDataTree implements IDisposable } private async doRefreshSubTree(node: IAsyncDataTreeNode, recursive: boolean, viewStateContext?: IAsyncDataTreeViewStateContext): Promise { - node.state = AsyncDataTreeNodeState.Loading; + node.loading = true; try { - await this.doRefreshNode(node, recursive, viewStateContext); + const childrenToRefresh = await this.doRefreshNode(node, recursive, viewStateContext); + node.stale = false; - if (recursive) { - const childrenToRefresh = node.children - .filter(child => { - if (child.needsRefresh) { - child.needsRefresh = false; - return true; - } - - // TODO@joao: is this still needed? - if (child.hasChildren && child.state === AsyncDataTreeNodeState.Loaded) { - return true; - } - - if (!viewStateContext || !viewStateContext.viewState.expanded || !child.id) { - return false; - } - - return viewStateContext.viewState.expanded.indexOf(child.id) > -1; - }); - - await Promise.all(childrenToRefresh.map(child => this.doRefreshSubTree(child, recursive, viewStateContext))); - } + await Promise.all(childrenToRefresh.map(child => this.doRefreshSubTree(child, recursive, viewStateContext))); } finally { - node.state = AsyncDataTreeNodeState.Loaded; + node.loading = false; } } - private async doRefreshNode(node: IAsyncDataTreeNode, recursive: boolean, viewStateContext?: IAsyncDataTreeViewStateContext): Promise { + private async doRefreshNode(node: IAsyncDataTreeNode, recursive: boolean, viewStateContext?: IAsyncDataTreeViewStateContext): Promise[]> { node.hasChildren = !!this.dataSource.hasChildren(node.element!); let childrenPromise: Promise; @@ -703,16 +690,14 @@ export class AsyncDataTree implements IDisposable try { const children = await childrenPromise; - this.setChildren(node, children, recursive, viewStateContext); + return this.setChildren(node, children, recursive, viewStateContext); } catch (err) { - node.needsRefresh = true; - if (node !== this.root) { this.tree.collapse(node === this.root ? null : node); } if (isPromiseCanceledError(err)) { - return; + return []; } throw err; @@ -747,7 +732,7 @@ export class AsyncDataTree implements IDisposable } private _onDidChangeCollapseState({ node, deep }: ICollapseStateChangeEvent, any>): void { - if (!node.collapsed && (node.element.state === AsyncDataTreeNodeState.Uninitialized || node.element.needsRefresh)) { + if (!node.collapsed && node.element.stale) { if (deep) { this.collapse(node.element.element as T); } else { @@ -757,7 +742,12 @@ export class AsyncDataTree implements IDisposable } } - private setChildren(node: IAsyncDataTreeNode, childrenElements: T[], recursive: boolean, viewStateContext?: IAsyncDataTreeViewStateContext): void { + private setChildren(node: IAsyncDataTreeNode, childrenElements: T[], recursive: boolean, viewStateContext?: IAsyncDataTreeViewStateContext): IAsyncDataTreeNode[] { + // perf: if the node was and still is a leaf, avoid all this hassle + if (node.children.length === 0 && childrenElements.length === 0) { + return []; + } + let nodeChildren: Map> | undefined; if (this.identityProvider) { @@ -768,67 +758,57 @@ export class AsyncDataTree implements IDisposable } } + let childrenToRefresh: IAsyncDataTreeNode[] = []; + const children = childrenElements.map>(element => { if (!this.identityProvider) { - const hasChildren = !!this.dataSource.hasChildren(element); - - return { + return createAsyncDataTreeNode({ element, parent: node, - children: [], - state: AsyncDataTreeNodeState.Uninitialized, - hasChildren, - needsRefresh: false, - disposed: false, - slow: false - }; + hasChildren: !!this.dataSource.hasChildren(element), + }); } const id = this.identityProvider.getId(element).toString(); const asyncDataTreeNode = nodeChildren!.get(id); - if (!asyncDataTreeNode) { - const childAsyncDataTreeNode: IAsyncDataTreeNode = { - element, - parent: node, - children: [], - id, - state: AsyncDataTreeNodeState.Uninitialized, - hasChildren: !!this.dataSource.hasChildren(element), - needsRefresh: false, - disposed: false, - slow: false - }; + if (asyncDataTreeNode) { + asyncDataTreeNode.element = element; + asyncDataTreeNode.stale = asyncDataTreeNode.stale || recursive; + asyncDataTreeNode.hasChildren = !!this.dataSource.hasChildren(element); - if (viewStateContext && viewStateContext.viewState.focus && viewStateContext.viewState.focus.indexOf(id) > -1) { - viewStateContext.focus.push(childAsyncDataTreeNode); + if (recursive && !this.tree.isCollapsed(asyncDataTreeNode)) { + childrenToRefresh.push(asyncDataTreeNode); } - if (viewStateContext && viewStateContext.viewState.selection && viewStateContext.viewState.selection.indexOf(id) > -1) { - viewStateContext.selection.push(childAsyncDataTreeNode); - } - - return childAsyncDataTreeNode; + return asyncDataTreeNode; } - asyncDataTreeNode.element = element; + const childAsyncDataTreeNode = createAsyncDataTreeNode({ + element, + parent: node, + id, + hasChildren: !!this.dataSource.hasChildren(element) + }); - const hasChildren = this.dataSource.hasChildren(asyncDataTreeNode.element); - - if (asyncDataTreeNode.state === AsyncDataTreeNodeState.Loaded || (asyncDataTreeNode.state !== AsyncDataTreeNodeState.Uninitialized && asyncDataTreeNode.hasChildren !== !!hasChildren)) { - asyncDataTreeNode.needsRefresh = true; + if (viewStateContext && viewStateContext.viewState.focus && viewStateContext.viewState.focus.indexOf(id) > -1) { + viewStateContext.focus.push(childAsyncDataTreeNode); } - asyncDataTreeNode.hasChildren = hasChildren; - return asyncDataTreeNode; + if (viewStateContext && viewStateContext.viewState.selection && viewStateContext.viewState.selection.indexOf(id) > -1) { + viewStateContext.selection.push(childAsyncDataTreeNode); + } + + if (viewStateContext && viewStateContext.viewState.expanded && viewStateContext.viewState.expanded.indexOf(id) > -1) { + childrenToRefresh.push(childAsyncDataTreeNode); + } + + return childAsyncDataTreeNode; }); - // perf: if the node was and still is a leaf, avoid all these expensive no-ops - if (node.children.length === 0 && childrenElements.length === 0) { - return; - } - node.children.splice(0, node.children.length, ...children); + + return childrenToRefresh; } private render(node: IAsyncDataTreeNode, viewStateContext?: IAsyncDataTreeViewStateContext): void { diff --git a/src/vs/base/common/arrays.ts b/src/vs/base/common/arrays.ts index ce2f41cf633..4b6e32dc042 100644 --- a/src/vs/base/common/arrays.ts +++ b/src/vs/base/common/arrays.ts @@ -51,8 +51,8 @@ export function binarySearch(array: ReadonlyArray, key: T, comparator: (op high = array.length - 1; while (low <= high) { - let mid = ((low + high) / 2) | 0; - let comp = comparator(array[mid], key); + const mid = ((low + high) / 2) | 0; + const comp = comparator(array[mid], key); if (comp < 0) { low = mid + 1; } else if (comp > 0) { @@ -75,7 +75,7 @@ export function findFirstInSorted(array: ReadonlyArray, p: (x: T) => boole return 0; // no children } while (low < high) { - let mid = Math.floor((low + high) / 2); + const mid = Math.floor((low + high) / 2); if (p(array[mid])) { high = mid; } else { @@ -122,7 +122,7 @@ function _sort(a: T[], compare: Compare, lo: number, hi: number, aux: T[]) if (hi <= lo) { return; } - let mid = lo + ((hi - lo) / 2) | 0; + const mid = lo + ((hi - lo) / 2) | 0; _sort(a, compare, lo, mid, aux); _sort(a, compare, mid + 1, hi, aux); if (compare(a[mid], a[mid + 1]) <= 0) { @@ -502,8 +502,8 @@ export function shuffle(array: T[], _seed?: number): void { } for (let i = array.length - 1; i > 0; i -= 1) { - let j = Math.floor(rand() * (i + 1)); - let temp = array[i]; + const j = Math.floor(rand() * (i + 1)); + const temp = array[i]; array[i] = array[j]; array[j] = temp; } diff --git a/src/vs/base/common/async.ts b/src/vs/base/common/async.ts index 080a2c7a273..8cc8a36ffcd 100644 --- a/src/vs/base/common/async.ts +++ b/src/vs/base/common/async.ts @@ -52,7 +52,7 @@ export function createCancelablePromise(callback: (token: CancellationToken) export function asPromise(callback: () => T | Thenable): Promise { return new Promise((resolve, reject) => { - let item = callback(); + const item = callback(); if (isThenable(item)) { item.then(resolve, reject); } else { @@ -688,12 +688,12 @@ declare function cancelIdleCallback(handle: number): void; (function () { if (typeof requestIdleCallback !== 'function' || typeof cancelIdleCallback !== 'function') { - let dummyIdle: IdleDeadline = Object.freeze({ + const dummyIdle: IdleDeadline = Object.freeze({ didTimeout: true, timeRemaining() { return 15; } }); runWhenIdle = (runner) => { - let handle = setTimeout(() => runner(dummyIdle)); + const handle = setTimeout(() => runner(dummyIdle)); let disposed = false; return { dispose() { @@ -707,7 +707,7 @@ declare function cancelIdleCallback(handle: number): void; }; } else { runWhenIdle = (runner, timeout?) => { - let handle: number = requestIdleCallback(runner, typeof timeout === 'number' ? { timeout } : undefined); + const handle: number = requestIdleCallback(runner, typeof timeout === 'number' ? { timeout } : undefined); let disposed = false; return { dispose() { diff --git a/src/vs/base/common/cancellation.ts b/src/vs/base/common/cancellation.ts index 39f672c9751..8f6b0b96f74 100644 --- a/src/vs/base/common/cancellation.ts +++ b/src/vs/base/common/cancellation.ts @@ -16,7 +16,7 @@ export interface CancellationToken { } const shortcutEvent = Object.freeze(function (callback, context?): IDisposable { - let handle = setTimeout(callback.bind(context), 0); + const handle = setTimeout(callback.bind(context), 0); return { dispose() { clearTimeout(handle); } }; } as Event); diff --git a/src/vs/base/common/color.ts b/src/vs/base/common/color.ts index f63e7aa86f1..3be7a1128a6 100644 --- a/src/vs/base/common/color.ts +++ b/src/vs/base/common/color.ts @@ -387,7 +387,7 @@ export class Color { const thisA = this.rgba.a; const colorA = rgba.a; - let a = thisA + colorA * (1 - thisA); + const a = thisA + colorA * (1 - thisA); if (a < 1e-6) { return Color.transparent; } diff --git a/src/vs/base/common/comparers.ts b/src/vs/base/common/comparers.ts index f5b9b26edd6..adf5859c794 100644 --- a/src/vs/base/common/comparers.ts +++ b/src/vs/base/common/comparers.ts @@ -144,8 +144,8 @@ export function comparePaths(one: string, other: string, caseSensitive = false): } export function compareAnything(one: string, other: string, lookFor: string): number { - let elementAName = one.toLowerCase(); - let elementBName = other.toLowerCase(); + const elementAName = one.toLowerCase(); + const elementBName = other.toLowerCase(); // Sort prefix matches over non prefix matches const prefixCompare = compareByPrefix(one, other, lookFor); @@ -154,14 +154,14 @@ export function compareAnything(one: string, other: string, lookFor: string): nu } // Sort suffix matches over non suffix matches - let elementASuffixMatch = strings.endsWith(elementAName, lookFor); - let elementBSuffixMatch = strings.endsWith(elementBName, lookFor); + const elementASuffixMatch = strings.endsWith(elementAName, lookFor); + const elementBSuffixMatch = strings.endsWith(elementBName, lookFor); if (elementASuffixMatch !== elementBSuffixMatch) { return elementASuffixMatch ? -1 : 1; } // Understand file names - let r = compareFileNames(elementAName, elementBName); + const r = compareFileNames(elementAName, elementBName); if (r !== 0) { return r; } @@ -171,12 +171,12 @@ export function compareAnything(one: string, other: string, lookFor: string): nu } export function compareByPrefix(one: string, other: string, lookFor: string): number { - let elementAName = one.toLowerCase(); - let elementBName = other.toLowerCase(); + const elementAName = one.toLowerCase(); + const elementBName = other.toLowerCase(); // Sort prefix matches over non prefix matches - let elementAPrefixMatch = strings.startsWith(elementAName, lookFor); - let elementBPrefixMatch = strings.startsWith(elementBName, lookFor); + const elementAPrefixMatch = strings.startsWith(elementAName, lookFor); + const elementBPrefixMatch = strings.startsWith(elementBName, lookFor); if (elementAPrefixMatch !== elementBPrefixMatch) { return elementAPrefixMatch ? -1 : 1; } diff --git a/src/vs/base/common/errors.ts b/src/vs/base/common/errors.ts index 15e2eb22b8b..da6f814332f 100644 --- a/src/vs/base/common/errors.ts +++ b/src/vs/base/common/errors.ts @@ -102,7 +102,7 @@ export function transformErrorForSerialization(error: any): any; export function transformErrorForSerialization(error: any): any { if (error instanceof Error) { let { name, message } = error; - let stack: string = (error).stacktrace || (error).stack; + const stack: string = (error).stacktrace || (error).stack; return { $isError: true, name, @@ -146,7 +146,7 @@ export function isPromiseCanceledError(error: any): boolean { * Returns an error that signals cancellation. */ export function canceled(): Error { - let error = new Error(canceledName); + const error = new Error(canceledName); error.name = error.message; return error; } diff --git a/src/vs/base/common/event.ts b/src/vs/base/common/event.ts index 1a5596aa6b7..159c6e708d9 100644 --- a/src/vs/base/common/event.ts +++ b/src/vs/base/common/event.ts @@ -152,7 +152,7 @@ export namespace Event { clearTimeout(handle); handle = setTimeout(() => { - let _output = output; + const _output = output; output = undefined; handle = undefined; if (!leading || numDebouncedCalls > 1) { @@ -190,7 +190,7 @@ export namespace Event { let cache: T; return filter(event, value => { - let shouldEmit = firstCall || value !== cache; + const shouldEmit = firstCall || value !== cache; firstCall = false; cache = value; return shouldEmit; @@ -389,7 +389,7 @@ export interface EmitterOptions { let _globalLeakWarningThreshold = -1; export function setGlobalLeakWarningThreshold(n: number): IDisposable { - let oldValue = _globalLeakWarningThreshold; + const oldValue = _globalLeakWarningThreshold; _globalLeakWarningThreshold = n; return { dispose() { @@ -428,8 +428,8 @@ class LeakageMonitor { if (!this._stacks) { this._stacks = new Map(); } - let stack = new Error().stack!.split('\n').slice(3).join('\n'); - let count = (this._stacks.get(stack) || 0); + const stack = new Error().stack!.split('\n').slice(3).join('\n'); + const count = (this._stacks.get(stack) || 0); this._stacks.set(stack, count + 1); this._warnCountdown -= 1; @@ -453,7 +453,7 @@ class LeakageMonitor { } return () => { - let count = (this._stacks!.get(stack) || 0); + const count = (this._stacks!.get(stack) || 0); this._stacks!.set(stack, count - 1); }; } @@ -627,7 +627,7 @@ export class AsyncEmitter extends Emitter { } for (let iter = this._listeners.iterator(), e = iter.next(); !e.done; e = iter.next()) { - let thenables: Promise[] = []; + const thenables: Promise[] = []; this._asyncDeliveryQueue.push([e.value, eventFn(thenables, typeof e.value === 'function' ? e.value : e.value[0]), thenables]); } diff --git a/src/vs/base/common/extpath.ts b/src/vs/base/common/extpath.ts index eaf31396842..984a1c8afd1 100644 --- a/src/vs/base/common/extpath.ts +++ b/src/vs/base/common/extpath.ts @@ -6,7 +6,7 @@ import { isWindows } from 'vs/base/common/platform'; import { startsWithIgnoreCase, equalsIgnoreCase } from 'vs/base/common/strings'; import { CharCode } from 'vs/base/common/charCode'; -import { sep } from 'vs/base/common/path'; +import { sep, posix } from 'vs/base/common/path'; function isPathSeparator(code: number) { return code === CharCode.Slash || code === CharCode.Backslash; @@ -18,7 +18,7 @@ function isPathSeparator(code: number) { * Using it on a Linux or MaxOS path might change it. */ export function toSlashes(osPath: string) { - return osPath.replace(/[\\/]/g, '/'); + return osPath.replace(/[\\/]/g, posix.sep); } /** @@ -26,13 +26,13 @@ export function toSlashes(osPath: string) { * `getRoot('files:///files/path') === files:///`, * or `getRoot('\\server\shares\path') === \\server\shares\` */ -export function getRoot(path: string, sep: string = '/'): string { +export function getRoot(path: string, sep: string = posix.sep): string { if (!path) { return ''; } - let len = path.length; + const len = path.length; const firstLetter = path.charCodeAt(0); if (isPathSeparator(firstLetter)) { if (isPathSeparator(path.charCodeAt(1))) { @@ -40,7 +40,7 @@ export function getRoot(path: string, sep: string = '/'): string { // ^^^^^^^^^^^^^^^^^^^ if (!isPathSeparator(path.charCodeAt(2))) { let pos = 3; - let start = pos; + const start = pos; for (; pos < len; pos++) { if (isPathSeparator(path.charCodeAt(pos))) { break; @@ -121,7 +121,7 @@ export function isUNC(path: string): boolean { return false; } let pos = 2; - let start = pos; + const start = pos; for (; pos < path.length; pos++) { code = path.charCodeAt(pos); if (code === CharCode.Backslash) { diff --git a/src/vs/base/common/filters.ts b/src/vs/base/common/filters.ts index e7674b93648..ef1df908741 100644 --- a/src/vs/base/common/filters.ts +++ b/src/vs/base/common/filters.ts @@ -28,7 +28,7 @@ export interface IMatch { export function or(...filter: IFilter[]): IFilter { return function (word: string, wordToMatchAgainst: string): IMatch[] | null { for (let i = 0, len = filter.length; i < len; i++) { - let match = filter[i](word, wordToMatchAgainst); + const match = filter[i](word, wordToMatchAgainst); if (match) { return match; } @@ -64,7 +64,7 @@ function _matchesPrefix(ignoreCase: boolean, word: string, wordToMatchAgainst: s // Contiguous Substring export function matchesContiguousSubString(word: string, wordToMatchAgainst: string): IMatch[] | null { - let index = wordToMatchAgainst.toLowerCase().indexOf(word.toLowerCase()); + const index = wordToMatchAgainst.toLowerCase().indexOf(word.toLowerCase()); if (index === -1) { return null; } @@ -136,7 +136,7 @@ function join(head: IMatch, tail: IMatch[]): IMatch[] { function nextAnchor(camelCaseWord: string, start: number): number { for (let i = start; i < camelCaseWord.length; i++) { - let c = camelCaseWord.charCodeAt(i); + const c = camelCaseWord.charCodeAt(i); if (isUpper(c) || isNumber(c) || (i > 0 && !isAlphanumeric(camelCaseWord.charCodeAt(i - 1)))) { return i; } @@ -184,10 +184,10 @@ function analyzeCamelCaseWord(word: string): ICamelCaseAnalysis { if (isNumber(code)) { numeric++; } } - let upperPercent = upper / word.length; - let lowerPercent = lower / word.length; - let alphaPercent = alpha / word.length; - let numericPercent = numeric / word.length; + const upperPercent = upper / word.length; + const lowerPercent = lower / word.length; + const alphaPercent = alpha / word.length; + const numericPercent = numeric / word.length; return { upperPercent, lowerPercent, alphaPercent, numericPercent }; } @@ -307,7 +307,7 @@ function _matchesWords(word: string, target: string, i: number, j: number, conti function nextWord(word: string, start: number): number { for (let i = start; i < word.length; i++) { - let c = word.charCodeAt(i); + const c = word.charCodeAt(i); if (isWhitespace(c) || (i > 0 && isWhitespace(word.charCodeAt(i - 1)))) { return i; } @@ -334,7 +334,7 @@ export function matchesFuzzy(word: string, wordToMatchAgainst: string, enableSep } // RegExp Filter - let match = regexp.exec(wordToMatchAgainst); + const match = regexp.exec(wordToMatchAgainst); if (match) { return [{ start: match.index, end: match.index + match[0].length }]; } @@ -348,7 +348,7 @@ export function matchesFuzzy(word: string, wordToMatchAgainst: string, enableSep * powerfull than `matchesFuzzy` */ export function matchesFuzzy2(pattern: string, word: string): IMatch[] | null { - let score = fuzzyScore(pattern, pattern.toLowerCase(), 0, word, word.toLowerCase(), 0, true); + const score = fuzzyScore(pattern, pattern.toLowerCase(), 0, word, word.toLowerCase(), 0, true); return score ? createMatches(score) : null; } @@ -404,7 +404,7 @@ function initTable() { row.push(-i); } for (let i = 0; i <= _maxLen; i++) { - let thisRow = row.slice(0); + const thisRow = row.slice(0); thisRow[0] = -i; table.push(thisRow); } @@ -566,9 +566,9 @@ export function fuzzyScore(pattern: string, patternLow: string, patternPos: numb _scores[patternPos][wordPos] = score; - let diag = _table[patternPos - 1][wordPos - 1] + (score > 1 ? 1 : score); - let top = _table[patternPos - 1][wordPos] + -1; - let left = _table[patternPos][wordPos - 1] + -1; + const diag = _table[patternPos - 1][wordPos - 1] + (score > 1 ? 1 : score); + const top = _table[patternPos - 1][wordPos] + -1; + const left = _table[patternPos][wordPos - 1] + -1; if (left >= top) { // left or diag @@ -635,8 +635,8 @@ function _findAllMatches2(patternPos: number, wordPos: number, total: number, ma while (patternPos > _patternStartPos && wordPos > 0) { - let score = _scores[patternPos][wordPos]; - let arrow = _arrows[patternPos][wordPos]; + const score = _scores[patternPos][wordPos]; + const arrow = _arrows[patternPos][wordPos]; if (arrow === Arrow.Left) { // left -> no match, skip a word character @@ -733,11 +733,11 @@ function fuzzyScoreWithPermutations(pattern: string, lowPattern: string, pattern // permutations of the pattern to find a better match. The // permutations only swap neighbouring characters, e.g // `cnoso` becomes `conso`, `cnsoo`, `cnoos`. - let tries = Math.min(7, pattern.length - 1); + const tries = Math.min(7, pattern.length - 1); for (let movingPatternPos = patternPos + 1; movingPatternPos < tries; movingPatternPos++) { - let newPattern = nextTypoPermutation(pattern, movingPatternPos); + const newPattern = nextTypoPermutation(pattern, movingPatternPos); if (newPattern) { - let candidate = fuzzyScore(newPattern, newPattern.toLowerCase(), patternPos, word, lowWord, wordPos, firstMatchCanBeWeak); + const candidate = fuzzyScore(newPattern, newPattern.toLowerCase(), patternPos, word, lowWord, wordPos, firstMatchCanBeWeak); if (candidate) { candidate[0] -= 3; // permutation penalty if (!top || candidate[0] > top[0]) { @@ -757,8 +757,8 @@ function nextTypoPermutation(pattern: string, patternPos: number): string | unde return undefined; } - let swap1 = pattern[patternPos]; - let swap2 = pattern[patternPos + 1]; + const swap1 = pattern[patternPos]; + const swap2 = pattern[patternPos + 1]; if (swap1 === swap2) { return undefined; diff --git a/src/vs/base/common/glob.ts b/src/vs/base/common/glob.ts index a7569150a48..0cecf47d7d4 100644 --- a/src/vs/base/common/glob.ts +++ b/src/vs/base/common/glob.ts @@ -53,7 +53,7 @@ export function splitGlobAware(pattern: string, splitChar: string): string[] { return []; } - let segments: string[] = []; + const segments: string[] = []; let inBraces = false; let inBrackets = false; @@ -102,7 +102,7 @@ function parseRegExp(pattern: string): string { let regEx = ''; // Split up into segments for each slash found - let segments = splitGlobAware(pattern, GLOB_SPLIT); + const segments = splitGlobAware(pattern, GLOB_SPLIT); // Special case where we only have globstars if (segments.every(s => s === GLOBSTAR)) { @@ -179,10 +179,10 @@ function parseRegExp(pattern: string): string { continue; case '}': - let choices = splitGlobAware(braceVal, ','); + const choices = splitGlobAware(braceVal, ','); // Converts {foo,bar} => [foo|bar] - let braceRegExp = `(?:${choices.map(c => parseRegExp(c)).join('|')})`; + const braceRegExp = `(?:${choices.map(c => parseRegExp(c)).join('|')})`; regEx += braceRegExp; diff --git a/src/vs/base/common/history.ts b/src/vs/base/common/history.ts index 95551eb1a7a..19dada92a3e 100644 --- a/src/vs/base/common/history.ts +++ b/src/vs/base/common/history.ts @@ -66,7 +66,7 @@ export class HistoryNavigator implements INavigator { } private _reduceToLimit() { - let data = this._elements; + const data = this._elements; if (data.length > this._limit) { this._initialize(data.slice(data.length - this._limit)); } diff --git a/src/vs/base/common/json.ts b/src/vs/base/common/json.ts index e0cf131ad89..b3a62ede9ff 100644 --- a/src/vs/base/common/json.ts +++ b/src/vs/base/common/json.ts @@ -209,7 +209,7 @@ export function createScanner(text: string, ignoreTrivia: boolean = false): JSON let digits = 0; let value = 0; while (digits < count) { - let ch = text.charCodeAt(pos); + const ch = text.charCodeAt(pos); if (ch >= CharacterCodes._0 && ch <= CharacterCodes._9) { value = value * 16 + ch - CharacterCodes._0; } @@ -240,7 +240,7 @@ export function createScanner(text: string, ignoreTrivia: boolean = false): JSON } function scanNumber(): string { - let start = pos; + const start = pos; if (text.charCodeAt(pos) === CharacterCodes._0) { pos++; } else { @@ -331,7 +331,7 @@ export function createScanner(text: string, ignoreTrivia: boolean = false): JSON result += '\t'; break; case CharacterCodes.u: - let ch = scanHexDigits(4); + const ch = scanHexDigits(4); if (ch >= 0) { result += String.fromCharCode(ch); } else { @@ -424,7 +424,7 @@ export function createScanner(text: string, ignoreTrivia: boolean = false): JSON // comments case CharacterCodes.slash: - let start = pos - 1; + const start = pos - 1; // Single-line comment if (text.charCodeAt(pos + 1) === CharacterCodes.slash) { pos += 2; @@ -444,10 +444,10 @@ export function createScanner(text: string, ignoreTrivia: boolean = false): JSON if (text.charCodeAt(pos + 1) === CharacterCodes.asterisk) { pos += 2; - let safeLength = len - 1; // For lookahead. + const safeLength = len - 1; // For lookahead. let commentClosed = false; while (pos < safeLength) { - let ch = text.charCodeAt(pos); + const ch = text.charCodeAt(pos); if (ch === CharacterCodes.asterisk && text.charCodeAt(pos + 1) === CharacterCodes.slash) { pos += 2; @@ -720,8 +720,8 @@ interface NodeImpl extends Node { * For a given offset, evaluate the location in the JSON document. Each segment in the location path is either a property name or an array index. */ export function getLocation(text: string, position: number): Location { - let segments: Segment[] = []; // strings or numbers - let earlyReturnException = new Object(); + const segments: Segment[] = []; // strings or numbers + const earlyReturnException = new Object(); let previousNode: NodeImpl | undefined = undefined; const previousNodeInst: NodeImpl = { value: {}, @@ -800,7 +800,7 @@ export function getLocation(text: string, position: number): Location { isAtPropertyKey = false; previousNode = undefined; } else if (sep === ',') { - let last = segments[segments.length - 1]; + const last = segments[segments.length - 1]; if (typeof last === 'number') { segments[segments.length - 1] = last + 1; } else { @@ -843,7 +843,7 @@ export function getLocation(text: string, position: number): Location { export function parse(text: string, errors: ParseError[] = [], options: ParseOptions = ParseOptions.DEFAULT): any { let currentProperty: string | null = null; let currentParent: any = []; - let previousParents: any[] = []; + const previousParents: any[] = []; function onValue(value: any) { if (Array.isArray(currentParent)) { @@ -853,9 +853,9 @@ export function parse(text: string, errors: ParseError[] = [], options: ParseOpt } } - let visitor: JSONVisitor = { + const visitor: JSONVisitor = { onObjectBegin: () => { - let object = {}; + const object = {}; onValue(object); previousParents.push(currentParent); currentParent = object; @@ -868,7 +868,7 @@ export function parse(text: string, errors: ParseError[] = [], options: ParseOpt currentParent = previousParents.pop(); }, onArrayBegin: () => { - let array: any[] = []; + const array: any[] = []; onValue(array); previousParents.push(currentParent); currentParent = array; @@ -905,7 +905,7 @@ export function parseTree(text: string, errors: ParseError[] = [], options: Pars return valueNode; } - let visitor: JSONVisitor = { + const visitor: JSONVisitor = { onObjectBegin: (offset: number) => { currentParent = onValue({ type: 'object', offset, length: -1, parent: currentParent, children: [] }); }, @@ -945,7 +945,7 @@ export function parseTree(text: string, errors: ParseError[] = [], options: Pars }; visit(text, visitor, options); - let result = currentParent.children![0]; + const result = currentParent.children![0]; if (result) { delete result.parent; } @@ -977,7 +977,7 @@ export function findNodeAtLocation(root: Node, path: JSONPath): Node | undefined return undefined; } } else { - let index = segment; + const index = segment; if (node.type !== 'array' || index < 0 || !Array.isArray(node.children) || index >= node.children.length) { return undefined; } @@ -994,12 +994,12 @@ export function getNodePath(node: Node): JSONPath { if (!node.parent || !node.parent.children) { return []; } - let path = getNodePath(node.parent); + const path = getNodePath(node.parent); if (node.parent.type === 'property') { - let key = node.parent.children[0].value; + const key = node.parent.children[0].value; path.push(key); } else if (node.parent.type === 'array') { - let index = node.parent.children.indexOf(node); + const index = node.parent.children.indexOf(node); if (index !== -1) { path.push(index); } @@ -1015,9 +1015,9 @@ export function getNodeValue(node: Node): any { case 'array': return node.children!.map(getNodeValue); case 'object': - let obj = Object.create(null); + const obj = Object.create(null); for (let prop of node.children!) { - let valueNode = prop.children![1]; + const valueNode = prop.children![1]; if (valueNode) { obj[prop.children![0].value] = getNodeValue(valueNode); } @@ -1043,10 +1043,10 @@ export function contains(node: Node, offset: number, includeRightBound = false): */ export function findNodeAtOffset(node: Node, offset: number, includeRightBound = false): Node | undefined { if (contains(node, offset, includeRightBound)) { - let children = node.children; + const children = node.children; if (Array.isArray(children)) { for (let i = 0; i < children.length && children[i].offset <= offset; i++) { - let item = findNodeAtOffset(children[i], offset, includeRightBound); + const item = findNodeAtOffset(children[i], offset, includeRightBound); if (item) { return item; } @@ -1064,7 +1064,7 @@ export function findNodeAtOffset(node: Node, offset: number, includeRightBound = */ export function visit(text: string, visitor: JSONVisitor, options: ParseOptions = ParseOptions.DEFAULT): any { - let _scanner = createScanner(text, false); + const _scanner = createScanner(text, false); function toNoArgVisit(visitFunction?: (offset: number, length: number) => void): () => void { return visitFunction ? () => visitFunction(_scanner.getTokenOffset(), _scanner.getTokenLength()) : () => true; @@ -1073,7 +1073,7 @@ export function visit(text: string, visitor: JSONVisitor, options: ParseOptions return visitFunction ? (arg: T) => visitFunction(arg, _scanner.getTokenOffset(), _scanner.getTokenLength()) : () => true; } - let onObjectBegin = toNoArgVisit(visitor.onObjectBegin), + const onObjectBegin = toNoArgVisit(visitor.onObjectBegin), onObjectProperty = toOneArgVisit(visitor.onObjectProperty), onObjectEnd = toNoArgVisit(visitor.onObjectEnd), onArrayBegin = toNoArgVisit(visitor.onArrayBegin), @@ -1083,11 +1083,11 @@ export function visit(text: string, visitor: JSONVisitor, options: ParseOptions onComment = toNoArgVisit(visitor.onComment), onError = toOneArgVisit(visitor.onError); - let disallowComments = options && options.disallowComments; - let allowTrailingComma = options && options.allowTrailingComma; + const disallowComments = options && options.disallowComments; + const allowTrailingComma = options && options.allowTrailingComma; function scanNext(): SyntaxKind { while (true) { - let token = _scanner.scan(); + const token = _scanner.scan(); switch (_scanner.getTokenError()) { case ScanError.InvalidUnicode: handleError(ParseErrorCode.InvalidUnicode); @@ -1148,7 +1148,7 @@ export function visit(text: string, visitor: JSONVisitor, options: ParseOptions } function parseString(isValue: boolean): boolean { - let value = _scanner.getTokenValue(); + const value = _scanner.getTokenValue(); if (isValue) { onLiteralValue(value); } else { diff --git a/src/vs/base/common/jsonEdit.ts b/src/vs/base/common/jsonEdit.ts index 7675ea16049..4a99bee9acd 100644 --- a/src/vs/base/common/jsonEdit.ts +++ b/src/vs/base/common/jsonEdit.ts @@ -12,9 +12,9 @@ export function removeProperty(text: string, path: JSONPath, formattingOptions: } export function setProperty(text: string, originalPath: JSONPath, value: any, formattingOptions: FormattingOptions, getInsertionIndex?: (properties: string[]) => number): Edit[] { - let path = originalPath.slice(); - let errors: ParseError[] = []; - let root = parseTree(text, errors); + const path = originalPath.slice(); + const errors: ParseError[] = []; + const root = parseTree(text, errors); let parent: Node | undefined = undefined; let lastSegment: Segment | undefined = undefined; @@ -39,24 +39,24 @@ export function setProperty(text: string, originalPath: JSONPath, value: any, fo } return withFormatting(text, { offset: root ? root.offset : 0, length: root ? root.length : 0, content: JSON.stringify(value) }, formattingOptions); } else if (parent.type === 'object' && typeof lastSegment === 'string' && Array.isArray(parent.children)) { - let existing = findNodeAtLocation(parent, [lastSegment]); + const existing = findNodeAtLocation(parent, [lastSegment]); if (existing !== undefined) { if (value === undefined) { // delete if (!existing.parent) { throw new Error('Malformed AST'); } - let propertyIndex = parent.children.indexOf(existing.parent); + const propertyIndex = parent.children.indexOf(existing.parent); let removeBegin: number; let removeEnd = existing.parent.offset + existing.parent.length; if (propertyIndex > 0) { // remove the comma of the previous node - let previous = parent.children[propertyIndex - 1]; + const previous = parent.children[propertyIndex - 1]; removeBegin = previous.offset + previous.length; } else { removeBegin = parent.offset + 1; if (parent.children.length > 1) { // remove the comma of the next node - let next = parent.children[1]; + const next = parent.children[1]; removeEnd = next.offset; } } @@ -69,11 +69,11 @@ export function setProperty(text: string, originalPath: JSONPath, value: any, fo if (value === undefined) { // delete return []; // property does not exist, nothing to do } - let newProperty = `${JSON.stringify(lastSegment)}: ${JSON.stringify(value)}`; - let index = getInsertionIndex ? getInsertionIndex(parent.children.map(p => p.children![0].value)) : parent.children.length; + const newProperty = `${JSON.stringify(lastSegment)}: ${JSON.stringify(value)}`; + const index = getInsertionIndex ? getInsertionIndex(parent.children.map(p => p.children![0].value)) : parent.children.length; let edit: Edit; if (index > 0) { - let previous = parent.children[index - 1]; + const previous = parent.children[index - 1]; edit = { offset: previous.offset + previous.length, length: 0, content: ',' + newProperty }; } else if (parent.children.length === 0) { edit = { offset: parent.offset + 1, length: 0, content: newProperty }; @@ -83,32 +83,32 @@ export function setProperty(text: string, originalPath: JSONPath, value: any, fo return withFormatting(text, edit, formattingOptions); } } else if (parent.type === 'array' && typeof lastSegment === 'number' && Array.isArray(parent.children)) { - let insertIndex = lastSegment; + const insertIndex = lastSegment; if (insertIndex === -1) { // Insert - let newProperty = `${JSON.stringify(value)}`; + const newProperty = `${JSON.stringify(value)}`; let edit: Edit; if (parent.children.length === 0) { edit = { offset: parent.offset + 1, length: 0, content: newProperty }; } else { - let previous = parent.children[parent.children.length - 1]; + const previous = parent.children[parent.children.length - 1]; edit = { offset: previous.offset + previous.length, length: 0, content: ',' + newProperty }; } return withFormatting(text, edit, formattingOptions); } else { if (value === undefined && parent.children.length >= 0) { //Removal - let removalIndex = lastSegment; - let toRemove = parent.children[removalIndex]; + const removalIndex = lastSegment; + const toRemove = parent.children[removalIndex]; let edit: Edit; if (parent.children.length === 1) { // only item edit = { offset: parent.offset + 1, length: parent.length - 2, content: '' }; } else if (parent.children.length - 1 === removalIndex) { // last item - let previous = parent.children[removalIndex - 1]; - let offset = previous.offset + previous.length; - let parentEndOffset = parent.offset + parent.length; + const previous = parent.children[removalIndex - 1]; + const offset = previous.offset + previous.length; + const parentEndOffset = parent.offset + parent.length; edit = { offset, length: parentEndOffset - 2 - offset, content: '' }; } else { edit = { offset: toRemove.offset, length: parent.children[removalIndex + 1].offset - toRemove.offset, content: '' }; @@ -139,18 +139,18 @@ function withFormatting(text: string, edit: Edit, formattingOptions: FormattingO } } - let edits = format(newText, { offset: begin, length: end - begin }, formattingOptions); + const edits = format(newText, { offset: begin, length: end - begin }, formattingOptions); // apply the formatting edits and track the begin and end offsets of the changes for (let i = edits.length - 1; i >= 0; i--) { - let edit = edits[i]; + const edit = edits[i]; newText = applyEdit(newText, edit); begin = Math.min(begin, edit.offset); end = Math.max(end, edit.offset + edit.length); end += edit.content.length - edit.length; } // create a single edit with all changes - let editLength = text.length - (newText.length - end) - begin; + const editLength = text.length - (newText.length - end) - begin; return [{ offset: begin, length: editLength, content: newText.substring(begin, end) }]; } diff --git a/src/vs/base/common/jsonFormatter.ts b/src/vs/base/common/jsonFormatter.ts index df2a97ee5cd..a25c493c05d 100644 --- a/src/vs/base/common/jsonFormatter.ts +++ b/src/vs/base/common/jsonFormatter.ts @@ -80,7 +80,7 @@ export function format(documentText: string, range: Range | undefined, options: rangeStart = 0; rangeEnd = documentText.length; } - let eol = getEOL(options, documentText); + const eol = getEOL(options, documentText); let lineBreak = false; let indentLevel = 0; @@ -91,7 +91,7 @@ export function format(documentText: string, range: Range | undefined, options: indentValue = '\t'; } - let scanner = createScanner(formatText, false); + const scanner = createScanner(formatText, false); let hasError = false; function newLineAndIndent(): string { @@ -107,7 +107,7 @@ export function format(documentText: string, range: Range | undefined, options: hasError = token === SyntaxKind.Unknown || scanner.getTokenError() !== ScanError.None; return token; } - let editOperations: Edit[] = []; + const editOperations: Edit[] = []; function addEdit(text: string, startOffset: number, endOffset: number) { if (!hasError && startOffset < rangeEnd && endOffset > rangeStart && documentText.substring(startOffset, endOffset) !== text) { editOperations.push({ offset: startOffset, length: endOffset - startOffset, content: text }); @@ -117,8 +117,8 @@ export function format(documentText: string, range: Range | undefined, options: let firstToken = scanNext(); if (firstToken !== SyntaxKind.EOF) { - let firstTokenStart = scanner.getTokenOffset() + formatTextStart; - let initialIndent = repeat(indentValue, initialIndentLevel); + const firstTokenStart = scanner.getTokenOffset() + formatTextStart; + const initialIndent = repeat(indentValue, initialIndentLevel); addEdit(initialIndent, formatTextStart, firstTokenStart); } @@ -129,7 +129,7 @@ export function format(documentText: string, range: Range | undefined, options: let replaceContent = ''; while (!lineBreak && (secondToken === SyntaxKind.LineCommentTrivia || secondToken === SyntaxKind.BlockCommentTrivia)) { // comments on the same line: keep them on the same line, but ignore them otherwise - let commentTokenStart = scanner.getTokenOffset() + formatTextStart; + const commentTokenStart = scanner.getTokenOffset() + formatTextStart; addEdit(' ', firstTokenEnd, commentTokenStart); firstTokenEnd = scanner.getTokenOffset() + scanner.getTokenLength() + formatTextStart; replaceContent = secondToken === SyntaxKind.LineCommentTrivia ? newLineAndIndent() : ''; @@ -195,7 +195,7 @@ export function format(documentText: string, range: Range | undefined, options: } } - let secondTokenStart = scanner.getTokenOffset() + formatTextStart; + const secondTokenStart = scanner.getTokenOffset() + formatTextStart; addEdit(replaceContent, firstTokenEnd, secondTokenStart); firstToken = secondToken; } @@ -213,9 +213,9 @@ function repeat(s: string, count: number): string { function computeIndentLevel(content: string, options: FormattingOptions): number { let i = 0; let nChars = 0; - let tabSize = options.tabSize || 4; + const tabSize = options.tabSize || 4; while (i < content.length) { - let ch = content.charAt(i); + const ch = content.charAt(i); if (ch === ' ') { nChars++; } else if (ch === '\t') { @@ -230,7 +230,7 @@ function computeIndentLevel(content: string, options: FormattingOptions): number function getEOL(options: FormattingOptions, text: string): string { for (let i = 0; i < text.length; i++) { - let ch = text.charAt(i); + const ch = text.charAt(i); if (ch === '\r') { if (i + 1 < text.length && text.charAt(i + 1) === '\n') { return '\r\n'; diff --git a/src/vs/base/common/keyCodes.ts b/src/vs/base/common/keyCodes.ts index b2d75920a4c..ec08bea7e91 100644 --- a/src/vs/base/common/keyCodes.ts +++ b/src/vs/base/common/keyCodes.ts @@ -407,7 +407,7 @@ export const enum KeyMod { } export function KeyChord(firstPart: number, secondPart: number): number { - let chordPart = ((secondPart & 0x0000FFFF) << 16) >>> 0; + const chordPart = ((secondPart & 0x0000FFFF) << 16) >>> 0; return (firstPart | chordPart) >>> 0; } @@ -466,10 +466,10 @@ export class SimpleKeybinding { } public getHashCode(): string { - let ctrl = this.ctrlKey ? '1' : '0'; - let shift = this.shiftKey ? '1' : '0'; - let alt = this.altKey ? '1' : '0'; - let meta = this.metaKey ? '1' : '0'; + const ctrl = this.ctrlKey ? '1' : '0'; + const shift = this.shiftKey ? '1' : '0'; + const alt = this.altKey ? '1' : '0'; + const meta = this.metaKey ? '1' : '0'; return `${ctrl}${shift}${alt}${meta}${this.keyCode}`; } diff --git a/src/vs/base/common/keybindingLabels.ts b/src/vs/base/common/keybindingLabels.ts index a1df76ab4fb..671816830bd 100644 --- a/src/vs/base/common/keybindingLabels.ts +++ b/src/vs/base/common/keybindingLabels.ts @@ -41,7 +41,7 @@ export class ModifierLabelProvider { return null; } - let result: string[] = []; + const result: string[] = []; for (let i = 0, len = parts.length; i < len; i++) { const part = parts[i]; const keyLabel = keyLabelProvider(part); @@ -162,7 +162,7 @@ function _simpleAsString(modifiers: Modifiers, key: string, labels: ModifierLabe return ''; } - let result: string[] = []; + const result: string[] = []; // translate modifier keys: Ctrl-Shift-Alt-Meta if (modifiers.ctrlKey) { diff --git a/src/vs/base/common/keybindingParser.ts b/src/vs/base/common/keybindingParser.ts index 68025d9131b..f7e120acc53 100644 --- a/src/vs/base/common/keybindingParser.ts +++ b/src/vs/base/common/keybindingParser.ts @@ -85,7 +85,7 @@ export class KeybindingParser { return null; } - let parts: SimpleKeybinding[] = []; + const parts: SimpleKeybinding[] = []; let part: SimpleKeybinding; do { @@ -112,7 +112,7 @@ export class KeybindingParser { return []; } - let parts: (SimpleKeybinding | ScanCodeBinding)[] = []; + const parts: (SimpleKeybinding | ScanCodeBinding)[] = []; let part: SimpleKeybinding | ScanCodeBinding; while (input.length > 0) { diff --git a/src/vs/base/common/labels.ts b/src/vs/base/common/labels.ts index dc55a8db24a..aea4b726f2f 100644 --- a/src/vs/base/common/labels.ts +++ b/src/vs/base/common/labels.ts @@ -283,7 +283,7 @@ interface ISegment { * @param value string to which templating is applied * @param values the values of the templates to use */ -export function template(template: string, values: { [key: string]: string | ISeparator } = Object.create(null)): string { +export function template(template: string, values: { [key: string]: string | ISeparator | null } = Object.create(null)): string { const segments: ISegment[] = []; let inVariable = false; diff --git a/src/vs/base/common/linkedList.ts b/src/vs/base/common/linkedList.ts index 5673ae64808..0eb16c2faa3 100644 --- a/src/vs/base/common/linkedList.ts +++ b/src/vs/base/common/linkedList.ts @@ -97,7 +97,7 @@ export class LinkedList { } if (candidate.prev && candidate.next) { // middle - let anchor = candidate.prev; + const anchor = candidate.prev; anchor.next = candidate.next; candidate.next.prev = anchor; @@ -144,7 +144,7 @@ export class LinkedList { } toArray(): E[] { - let result: E[] = []; + const result: E[] = []; for (let node = this._first; node instanceof Node; node = node.next) { result.push(node.element); } diff --git a/src/vs/base/common/map.ts b/src/vs/base/common/map.ts index c1ed1d4aa4a..5320086b024 100644 --- a/src/vs/base/common/map.ts +++ b/src/vs/base/common/map.ts @@ -100,8 +100,8 @@ export class StringIterator implements IKeyIterator { } cmp(a: string): number { - let aCode = a.charCodeAt(0); - let thisCode = this._value.charCodeAt(this._pos); + const aCode = a.charCodeAt(0); + const thisCode = this._value.charCodeAt(this._pos); return aCode - thisCode; } @@ -149,11 +149,11 @@ export class PathIterator implements IKeyIterator { cmp(a: string): number { let aPos = 0; - let aLen = a.length; + const aLen = a.length; let thisPos = this._from; while (aPos < aLen && thisPos < this._to) { - let cmp = a.charCodeAt(aPos) - this._value.charCodeAt(thisPos); + const cmp = a.charCodeAt(aPos) - this._value.charCodeAt(thisPos); if (cmp !== 0) { return cmp; } @@ -210,7 +210,7 @@ export class TernarySearchTree { } set(key: string, element: E): E | undefined { - let iter = this._iter.reset(key); + const iter = this._iter.reset(key); let node: TernarySearchTreeNode; if (!this._root) { @@ -220,7 +220,7 @@ export class TernarySearchTree { node = this._root; while (true) { - let val = iter.cmp(node.segment); + const val = iter.cmp(node.segment); if (val > 0) { // left if (!node.left) { @@ -256,10 +256,10 @@ export class TernarySearchTree { } get(key: string): E | undefined { - let iter = this._iter.reset(key); + const iter = this._iter.reset(key); let node = this._root; while (node) { - let val = iter.cmp(node.segment); + const val = iter.cmp(node.segment); if (val > 0) { // left node = node.left; @@ -279,13 +279,13 @@ export class TernarySearchTree { delete(key: string): void { - let iter = this._iter.reset(key); - let stack: [-1 | 0 | 1, TernarySearchTreeNode][] = []; + const iter = this._iter.reset(key); + const stack: [-1 | 0 | 1, TernarySearchTreeNode][] = []; let node = this._root; // find and unset node while (node) { - let val = iter.cmp(node.segment); + const val = iter.cmp(node.segment); if (val > 0) { // left stack.push([1, node]); @@ -319,11 +319,11 @@ export class TernarySearchTree { } findSubstr(key: string): E | undefined { - let iter = this._iter.reset(key); + const iter = this._iter.reset(key); let node = this._root; let candidate: E | undefined = undefined; while (node) { - let val = iter.cmp(node.segment); + const val = iter.cmp(node.segment); if (val > 0) { // left node = node.left; @@ -343,10 +343,10 @@ export class TernarySearchTree { } findSuperstr(key: string): Iterator | undefined { - let iter = this._iter.reset(key); + const iter = this._iter.reset(key); let node = this._root; while (node) { - let val = iter.cmp(node.segment); + const val = iter.cmp(node.segment); if (val > 0) { // left node = node.left; @@ -373,7 +373,7 @@ export class TernarySearchTree { let res: { done: false; value: E; }; let idx: number; let data: E[]; - let next = (): IteratorResult => { + const next = (): IteratorResult => { if (!data) { // lazy till first invocation data = []; @@ -610,7 +610,7 @@ export class LinkedMap { } values(): V[] { - let result: V[] = []; + const result: V[] = []; let current = this._head; while (current) { result.push(current.value); @@ -620,7 +620,7 @@ export class LinkedMap { } keys(): K[] { - let result: K[] = []; + const result: K[] = []; let current = this._head; while (current) { result.push(current.key); @@ -631,14 +631,14 @@ export class LinkedMap { /* VS Code / Monaco editor runs on es5 which has no Symbol.iterator keys(): IterableIterator { - let current = this._head; - let iterator: IterableIterator = { + const current = this._head; + const iterator: IterableIterator = { [Symbol.iterator]() { return iterator; }, next():IteratorResult { if (current) { - let result = { value: current.key, done: false }; + const result = { value: current.key, done: false }; current = current.next; return result; } else { @@ -650,14 +650,14 @@ export class LinkedMap { } values(): IterableIterator { - let current = this._head; - let iterator: IterableIterator = { + const current = this._head; + const iterator: IterableIterator = { [Symbol.iterator]() { return iterator; }, next():IteratorResult { if (current) { - let result = { value: current.value, done: false }; + const result = { value: current.value, done: false }; current = current.next; return result; } else { diff --git a/src/vs/base/common/mime.ts b/src/vs/base/common/mime.ts index 10ab55362b1..de19c7c03f8 100644 --- a/src/vs/base/common/mime.ts +++ b/src/vs/base/common/mime.ts @@ -230,7 +230,7 @@ export function isUnspecific(mime: string[] | string): boolean { * 2. Otherwise, if there are other extensions, suggest the first one. * 3. Otherwise, suggest the prefix. */ -export function suggestFilename(langId: string, prefix: string): string { +export function suggestFilename(langId: string | null, prefix: string): string { const extensions = registeredAssociations .filter(assoc => !assoc.userConfigured && assoc.extension && assoc.id === langId) .map(assoc => assoc.extension); diff --git a/src/vs/base/common/objects.ts b/src/vs/base/common/objects.ts index ef5e8b59de4..34e9a7862c9 100644 --- a/src/vs/base/common/objects.ts +++ b/src/vs/base/common/objects.ts @@ -30,11 +30,11 @@ export function deepFreeze(obj: T): T { } const stack: any[] = [obj]; while (stack.length > 0) { - let obj = stack.shift(); + const obj = stack.shift(); Object.freeze(obj); for (const key in obj) { if (_hasOwnProperty.call(obj, key)) { - let prop = obj[key]; + const prop = obj[key]; if (typeof prop === 'object' && !Object.isFrozen(prop)) { stack.push(prop); } diff --git a/src/vs/base/common/parsers.ts b/src/vs/base/common/parsers.ts index da9010662fa..4ea4e95373d 100644 --- a/src/vs/base/common/parsers.ts +++ b/src/vs/base/common/parsers.ts @@ -81,8 +81,8 @@ export abstract class Parser { protected static merge(destination: T, source: T, overwrite: boolean): void { Object.keys(source).forEach((key: string) => { - let destValue = destination[key]; - let sourceValue = source[key]; + const destValue = destination[key]; + const sourceValue = source[key]; if (Types.isUndefined(sourceValue)) { return; } diff --git a/src/vs/base/common/path.ts b/src/vs/base/common/path.ts index 53bd495fd8d..640c033ae01 100644 --- a/src/vs/base/common/path.ts +++ b/src/vs/base/common/path.ts @@ -6,28 +6,30 @@ // NOTE: VSCode's copy of nodejs path library to be usable in common (non-node) namespace // Copied from: https://github.com/nodejs/node/tree/43dd49c9782848c25e5b03448c8a0f923f13c158 -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. +/** + * Copyright Joyent, Inc. and other Node contributors. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit + * persons to whom the Software is furnished to do so, subject to the + * following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + */ -import { isWindows } from 'vs/base/common/platform'; +import * as process from 'vs/base/common/process'; const CHAR_UPPERCASE_A = 65;/* A */ const CHAR_LOWERCASE_A = 97; /* a */ @@ -39,19 +41,6 @@ const CHAR_BACKWARD_SLASH = 92; /* \ */ const CHAR_COLON = 58; /* : */ const CHAR_QUESTION_MARK = 63; /* ? */ -interface IProcess { - cwd(): string; - platform: string; - env: object; -} - -declare let process: IProcess; -const safeProcess: IProcess = (typeof process === 'undefined') ? { - cwd() { return '/'; }, - env: {}, - get platform() { return isWindows ? 'win32' : 'posix'; } -} : process; - class ErrorInvalidArgType extends Error { code: 'ERR_INVALID_ARG_TYPE'; constructor(name: string, expected: string, actual: string) { @@ -205,12 +194,7 @@ interface IPath { posix: IPath | null; } -interface IExportedPath extends IPath { - win32: IPath; - posix: IPath; -} - -const win32: IPath = { +export const win32: IPath = { // path.resolve([from ...], to) resolve(...pathSegments: string[]): string { let resolvedDevice = ''; @@ -222,14 +206,14 @@ const win32: IPath = { if (i >= 0) { path = pathSegments[i]; } else if (!resolvedDevice) { - path = safeProcess.cwd(); + path = process.cwd(); } else { // Windows has the concept of drive-specific current working // directories. If we've resolved a drive letter but not yet an // absolute path, get cwd for that drive, or the process cwd if // the drive cwd is not available. We're sure the device is not // a UNC path at this points, because UNC paths are always absolute. - path = safeProcess.env['=' + resolvedDevice] || safeProcess.cwd(); + path = process.env['=' + resolvedDevice] || process.cwd(); // Verify that a cwd was found and that it actually points // to our drive. If not, default to the drive's root. @@ -247,7 +231,7 @@ const win32: IPath = { continue; } - let len = path.length; + const len = path.length; let rootEnd = 0; let device = ''; let isAbsolute = false; @@ -519,7 +503,7 @@ const win32: IPath = { let joined; let firstPart; for (let i = 0; i < paths.length; ++i) { - let arg = paths[i]; + const arg = paths[i]; validateString(arg, 'path'); if (arg.length > 0) { if (joined === undefined) { @@ -598,8 +582,8 @@ const win32: IPath = { return ''; } - let fromOrig = win32.resolve(from); - let toOrig = win32.resolve(to); + const fromOrig = win32.resolve(from); + const toOrig = win32.resolve(to); if (fromOrig === toOrig) { return ''; @@ -626,7 +610,7 @@ const win32: IPath = { break; } } - let fromLen = (fromEnd - fromStart); + const fromLen = (fromEnd - fromStart); // Trim any leading backslashes let toStart = 0; @@ -642,10 +626,10 @@ const win32: IPath = { break; } } - let toLen = (toEnd - toStart); + const toLen = (toEnd - toStart); // Compare paths to find the longest common path from root - let length = (fromLen < toLen ? fromLen : toLen); + const length = (fromLen < toLen ? fromLen : toLen); let lastCommonSep = -1; let i = 0; for (; i <= length; ++i) { @@ -674,8 +658,8 @@ const win32: IPath = { } break; } - let fromCode = from.charCodeAt(fromStart + i); - let toCode = to.charCodeAt(toStart + i); + const fromCode = from.charCodeAt(fromStart + i); + const toCode = to.charCodeAt(toStart + i); if (fromCode !== toCode) { break; } @@ -1031,12 +1015,12 @@ const win32: IPath = { parse(path) { validateString(path, 'path'); - let ret = { root: '', dir: '', base: '', ext: '', name: '' }; + const ret = { root: '', dir: '', base: '', ext: '', name: '' }; if (path.length === 0) { return ret; } - let len = path.length; + const len = path.length; let rootEnd = 0; let code = path.charCodeAt(0); @@ -1199,7 +1183,7 @@ const win32: IPath = { posix: null }; -const posix: IPath = { +export const posix: IPath = { // path.resolve([from ...], to) resolve(...pathSegments: string[]): string { let resolvedPath = ''; @@ -1211,7 +1195,7 @@ const posix: IPath = { path = pathSegments[i]; } else { - path = safeProcess.cwd(); + path = process.cwd(); } validateString(path, 'path'); @@ -1284,7 +1268,7 @@ const posix: IPath = { } let joined; for (let i = 0; i < paths.length; ++i) { - let arg = arguments[i]; + const arg = arguments[i]; validateString(arg, 'path'); if (arg.length > 0) { if (joined === undefined) { @@ -1323,8 +1307,8 @@ const posix: IPath = { break; } } - let fromEnd = from.length; - let fromLen = (fromEnd - fromStart); + const fromEnd = from.length; + const fromLen = (fromEnd - fromStart); // Trim any leading backslashes let toStart = 1; @@ -1333,11 +1317,11 @@ const posix: IPath = { break; } } - let toEnd = to.length; - let toLen = (toEnd - toStart); + const toEnd = to.length; + const toLen = (toEnd - toStart); // Compare paths to find the longest common path from root - let length = (fromLen < toLen ? fromLen : toLen); + const length = (fromLen < toLen ? fromLen : toLen); let lastCommonSep = -1; let i = 0; for (; i <= length; ++i) { @@ -1365,8 +1349,8 @@ const posix: IPath = { } break; } - let fromCode = from.charCodeAt(fromStart + i); - let toCode = to.charCodeAt(toStart + i); + const fromCode = from.charCodeAt(fromStart + i); + const toCode = to.charCodeAt(toStart + i); if (fromCode !== toCode) { break; } @@ -1584,11 +1568,11 @@ const posix: IPath = { parse(path: string): ParsedPath { validateString(path, 'path'); - let ret = { root: '', dir: '', base: '', ext: '', name: '' }; + const ret = { root: '', dir: '', base: '', ext: '', name: '' }; if (path.length === 0) { return ret; } - let isAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH; + const isAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH; let start; if (isAbsolute) { ret.root = '/'; @@ -1685,5 +1669,16 @@ const posix: IPath = { posix.win32 = win32.win32 = win32; posix.posix = win32.posix = posix; -const impl = (safeProcess.platform === 'win32' ? win32 : posix) as IExportedPath; -export = impl; +export const normalize = (process.platform === 'win32' ? win32.normalize : posix.normalize); +export const isAbsolute = (process.platform === 'win32' ? win32.isAbsolute : posix.isAbsolute); +export const join = (process.platform === 'win32' ? win32.join : posix.join); +export const resolve = (process.platform === 'win32' ? win32.resolve : posix.resolve); +export const relative = (process.platform === 'win32' ? win32.relative : posix.relative); +export const dirname = (process.platform === 'win32' ? win32.dirname : posix.dirname); +export const basename = (process.platform === 'win32' ? win32.basename : posix.basename); +export const extname = (process.platform === 'win32' ? win32.extname : posix.extname); +export const format = (process.platform === 'win32' ? win32.format : posix.format); +export const parse = (process.platform === 'win32' ? win32.parse : posix.parse); +export const toNamespacedPath = (process.platform === 'win32' ? win32.toNamespacedPath : posix.toNamespacedPath); +export const sep = (process.platform === 'win32' ? win32.sep : posix.sep); +export const delimiter = (process.platform === 'win32' ? win32.delimiter : posix.delimiter); diff --git a/src/vs/base/common/platform.ts b/src/vs/base/common/platform.ts index 6755ebaa983..66178dacef8 100644 --- a/src/vs/base/common/platform.ts +++ b/src/vs/base/common/platform.ts @@ -34,15 +34,15 @@ interface INodeProcess { }; type?: string; } -declare let process: INodeProcess; -declare let global: any; +declare const process: INodeProcess; +declare const global: any; interface INavigator { userAgent: string; language: string; } -declare let navigator: INavigator; -declare let self: any; +declare const navigator: INavigator; +declare const self: any; const isElectronRenderer = (typeof process !== 'undefined' && typeof process.versions !== 'undefined' && typeof process.versions.electron !== 'undefined' && process.type === 'renderer'); diff --git a/src/vs/base/common/process.ts b/src/vs/base/common/process.ts new file mode 100644 index 00000000000..a8447d58eea --- /dev/null +++ b/src/vs/base/common/process.ts @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { isWindows, isMacintosh, setImmediate } from 'vs/base/common/platform'; + +interface IProcess { + platform: string; + env: object; + + cwd(): string; + nextTick(callback: (...args: any[]) => void): number; +} + +declare const process: IProcess; +const safeProcess: IProcess = (typeof process === 'undefined') ? { + cwd(): string { return '/'; }, + env: Object.create(null), + get platform(): string { return isWindows ? 'win32' : isMacintosh ? 'darwin' : 'linux'; }, + nextTick(callback: (...args: any[]) => void): number { return setImmediate(callback); } +} : process; + +export const cwd = safeProcess.cwd; +export const env = safeProcess.env; +export const platform = safeProcess.platform; +export const nextTick = safeProcess.nextTick; diff --git a/src/vs/base/common/processes.ts b/src/vs/base/common/processes.ts index 7eef74d894f..b42d8e25605 100644 --- a/src/vs/base/common/processes.ts +++ b/src/vs/base/common/processes.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { IProcessEnvironment } from 'vs/base/common/platform'; + /** * Options to be passed to the external program or shell. */ @@ -84,3 +86,30 @@ export const enum TerminateResponseCode { AccessDenied = 2, ProcessNotFound = 3, } + +/** + * Sanitizes a VS Code process environment by removing all Electron/VS Code-related values. + */ +export function sanitizeProcessEnvironment(env: IProcessEnvironment, ...preserve: string[]): void { + const set = preserve.reduce((set, key) => { + set[key] = true; + return set; + }, {} as Record); + const keysToRemove = [ + /^ELECTRON_.+$/, + /^GOOGLE_API_KEY$/, + /^VSCODE_.+$/, + /^SNAP(|_.*)$/ + ]; + const envKeys = Object.keys(env); + envKeys + .filter(key => !set[key]) + .forEach(envKey => { + for (let i = 0; i < keysToRemove.length; i++) { + if (envKey.search(keysToRemove[i]) !== -1) { + delete env[envKey]; + break; + } + } + }); +} diff --git a/src/vs/base/common/resources.ts b/src/vs/base/common/resources.ts index 607aff7911a..60a58363f18 100644 --- a/src/vs/base/common/resources.ts +++ b/src/vs/base/common/resources.ts @@ -181,7 +181,7 @@ export function hasTrailingPathSeparator(resource: URI): boolean { const fsp = originalFSPath(resource); return fsp.length > extpath.getRoot(fsp).length && fsp[fsp.length - 1] === paths.sep; } else { - let p = resource.path; + const p = resource.path; return p.length > 1 && p.charCodeAt(p.length - 1) === CharCode.Slash; // ignore the slash at offset 0 } } @@ -249,21 +249,6 @@ export function distinctParents(items: T[], resourceAccessor: (item: T) => UR return distinctParents; } -/** - * Tests whether the given URL is a file URI created by `URI.parse` instead of `URI.file`. - * Such URI have no scheme or scheme that consist of a single letter (windows drive letter) - * @param candidate The URI to test - * @returns A corrected, real file URI if the input seems to be malformed. - * Undefined is returned if the input URI looks fine. - */ -export function isMalformedFileUri(candidate: URI): URI | undefined { - if (!candidate.scheme || isWindows && candidate.scheme.match(/^[a-zA-Z]$/)) { - return URI.file((candidate.scheme ? candidate.scheme + ':' : '') + candidate.path); - } - return undefined; -} - - /** * Data URI related helpers. */ @@ -324,4 +309,4 @@ export class ResourceGlobMatcher { } return !!this.globalExpression(resource.path); } -} \ No newline at end of file +} diff --git a/src/vs/base/common/scrollable.ts b/src/vs/base/common/scrollable.ts index f33897a9f3b..1ecdd316ba8 100644 --- a/src/vs/base/common/scrollable.ts +++ b/src/vs/base/common/scrollable.ts @@ -117,13 +117,13 @@ export class ScrollState implements IScrollDimensions, IScrollPosition { } public createScrollEvent(previous: ScrollState): ScrollEvent { - let widthChanged = (this.width !== previous.width); - let scrollWidthChanged = (this.scrollWidth !== previous.scrollWidth); - let scrollLeftChanged = (this.scrollLeft !== previous.scrollLeft); + const widthChanged = (this.width !== previous.width); + const scrollWidthChanged = (this.scrollWidth !== previous.scrollWidth); + const scrollLeftChanged = (this.scrollLeft !== previous.scrollLeft); - let heightChanged = (this.height !== previous.height); - let scrollHeightChanged = (this.scrollHeight !== previous.scrollHeight); - let scrollTopChanged = (this.scrollTop !== previous.scrollTop); + const heightChanged = (this.height !== previous.height); + const scrollHeightChanged = (this.scrollHeight !== previous.scrollHeight); + const scrollTopChanged = (this.scrollTop !== previous.scrollTop); return { width: this.width, diff --git a/src/vs/base/common/strings.ts b/src/vs/base/common/strings.ts index 72a0c0785b5..37c57c26f1c 100644 --- a/src/vs/base/common/strings.ts +++ b/src/vs/base/common/strings.ts @@ -21,8 +21,8 @@ export function isFalsyOrWhitespace(str: string | undefined): boolean { * @returns the provided number with the given number of preceding zeros. */ export function pad(n: number, l: number, char: string = '0'): string { - let str = '' + n; - let r = [str]; + const str = '' + n; + const r = [str]; for (let i = str.length; i < l; i++) { r.push(char); @@ -44,7 +44,7 @@ export function format(value: string, ...args: any[]): string { return value; } return value.replace(_formatRegexp, function (match, group) { - let idx = parseInt(group, 10); + const idx = parseInt(group, 10); return isNaN(idx) || idx < 0 || idx >= args.length ? match : args[idx]; @@ -79,7 +79,7 @@ export function escapeRegExpCharacters(value: string): string { * @param needle the thing to trim (default is a blank) */ export function trim(haystack: string, needle: string = ' '): string { - let trimmed = ltrim(haystack, needle); + const trimmed = ltrim(haystack, needle); return rtrim(trimmed, needle); } @@ -93,7 +93,7 @@ export function ltrim(haystack: string, needle: string): string { return haystack; } - let needleLen = needle.length; + const needleLen = needle.length; if (needleLen === 0 || haystack.length === 0) { return haystack; } @@ -116,7 +116,7 @@ export function rtrim(haystack: string, needle: string): string { return haystack; } - let needleLen = needle.length, + const needleLen = needle.length, haystackLen = haystack.length; if (needleLen === 0 || haystackLen === 0) { @@ -173,7 +173,7 @@ export function startsWith(haystack: string, needle: string): boolean { * Determines if haystack ends with needle. */ export function endsWith(haystack: string, needle: string): boolean { - let diff = haystack.length - needle.length; + const diff = haystack.length - needle.length; if (diff > 0) { return haystack.indexOf(needle, diff) === diff; } else if (diff === 0) { @@ -232,7 +232,7 @@ export function regExpLeadsToEndlessLoop(regexp: RegExp): boolean { // We check against an empty string. If the regular expression doesn't advance // (e.g. ends in an endless loop) it will match an empty string. - let match = regexp.exec(''); + const match = regexp.exec(''); return !!(match && regexp.lastIndex === 0); } @@ -253,7 +253,7 @@ export function regExpFlags(regexp: RegExp): string { */ export function firstNonWhitespaceIndex(str: string): number { for (let i = 0, len = str.length; i < len; i++) { - let chCode = str.charCodeAt(i); + const chCode = str.charCodeAt(i); if (chCode !== CharCode.Space && chCode !== CharCode.Tab) { return i; } @@ -267,7 +267,7 @@ export function firstNonWhitespaceIndex(str: string): number { */ export function getLeadingWhitespace(str: string, start: number = 0, end: number = str.length): string { for (let i = start; i < end; i++) { - let chCode = str.charCodeAt(i); + const chCode = str.charCodeAt(i); if (chCode !== CharCode.Space && chCode !== CharCode.Tab) { return str.substring(start, i); } @@ -281,7 +281,7 @@ export function getLeadingWhitespace(str: string, start: number = 0, end: number */ export function lastNonWhitespaceIndex(str: string, startIndex: number = str.length - 1): number { for (let i = startIndex; i >= 0; i--) { - let chCode = str.charCodeAt(i); + const chCode = str.charCodeAt(i); if (chCode !== CharCode.Space && chCode !== CharCode.Tab) { return i; } @@ -380,7 +380,7 @@ function doEqualsIgnoreCase(a: string, b: string, stopAt = a.length): boolean { // a-z A-Z if (isAsciiLetter(codeA) && isAsciiLetter(codeB)) { - let diff = Math.abs(codeA - codeB); + const diff = Math.abs(codeA - codeB); if (diff !== 0 && diff !== 32) { return false; } @@ -431,8 +431,8 @@ export function commonSuffixLength(a: string, b: string): number { let i: number, len = Math.min(a.length, b.length); - let aLastIndex = a.length - 1; - let bLastIndex = b.length - 1; + const aLastIndex = a.length - 1; + const bLastIndex = b.length - 1; for (i = 0; i < len; i++) { if (a.charCodeAt(aLastIndex - i) !== b.charCodeAt(bLastIndex - i)) { @@ -459,7 +459,7 @@ function substrEquals(a: string, aStart: number, aEnd: number, b: string, bStart * For instance `overlap("foobar", "arr, I'm a pirate") === 2`. */ export function overlap(a: string, b: string): number { - let aEnd = a.length; + const aEnd = a.length; let bEnd = b.length; let aStart = aEnd - bEnd; @@ -486,9 +486,9 @@ export function overlap(a: string, b: string): number { // Code points U+0000 to U+D7FF and U+E000 to U+FFFF are represented on a single character // Code points U+10000 to U+10FFFF are represented on two consecutive characters //export function getUnicodePoint(str:string, index:number, len:number):number { -// let chrCode = str.charCodeAt(index); +// const chrCode = str.charCodeAt(index); // if (0xD800 <= chrCode && chrCode <= 0xDBFF && index + 1 < len) { -// let nextChrCode = str.charCodeAt(index + 1); +// const nextChrCode = str.charCodeAt(index + 1); // if (0xDC00 <= nextChrCode && nextChrCode <= 0xDFFF) { // return (chrCode - 0xD800) << 10 + (nextChrCode - 0xDC00) + 0x10000; // } @@ -685,7 +685,7 @@ export function fuzzyContains(target: string, query: string): boolean { let index = 0; let lastIndexOf = -1; while (index < queryLen) { - let indexOf = targetLower.indexOf(query[index], lastIndexOf + 1); + const indexOf = targetLower.indexOf(query[index], lastIndexOf + 1); if (indexOf < 0) { return false; } diff --git a/src/vs/base/common/types.ts b/src/vs/base/common/types.ts index 9dbae8ce1e2..d2a261fb029 100644 --- a/src/vs/base/common/types.ts +++ b/src/vs/base/common/types.ts @@ -168,7 +168,7 @@ export function create(ctor: Function, ...args: any[]): any { if (isNativeClass(ctor)) { return new (ctor as any)(...args); } else { - let obj = Object.create(ctor.prototype); + const obj = Object.create(ctor.prototype); ctor.apply(obj, args); return obj; } diff --git a/src/vs/base/common/uri.ts b/src/vs/base/common/uri.ts index 29524b9d7f0..79560531ae8 100644 --- a/src/vs/base/common/uri.ts +++ b/src/vs/base/common/uri.ts @@ -56,6 +56,21 @@ function _validateUri(ret: URI, _strict?: boolean): void { } } +// for a while we allowed uris *without* schemes and this is the migration +// for them, e.g. an uri without scheme and without strict-mode warns and falls +// back to the file-scheme. that should cause the least carnage and still be a +// clear warning +function _schemeFix(scheme: string, _strict: boolean): string { + if (_strict || _throwOnMissingSchema) { + return scheme || _empty; + } + if (!scheme) { + console.trace('BAD uri lacks scheme, falling back to file-scheme.'); + scheme = 'file'; + } + return scheme; +} + // implements a bit of https://tools.ietf.org/html/rfc3986#section-5 function _referenceResolution(scheme: string, path: string): string { @@ -154,7 +169,7 @@ export class URI implements UriComponents { /** * @internal */ - protected constructor(schemeOrData: string | UriComponents, authority?: string, path?: string, query?: string, fragment?: string, _strict?: boolean) { + protected constructor(schemeOrData: string | UriComponents, authority?: string, path?: string, query?: string, fragment?: string, _strict: boolean = false) { if (typeof schemeOrData === 'object') { this.scheme = schemeOrData.scheme || _empty; @@ -166,7 +181,7 @@ export class URI implements UriComponents { // that creates uri components. // _validateUri(this); } else { - this.scheme = schemeOrData || _empty; + this.scheme = _schemeFix(schemeOrData, _strict); this.authority = authority || _empty; this.path = _referenceResolution(this.scheme, path || _empty); this.query = query || _empty; @@ -314,7 +329,7 @@ export class URI implements UriComponents { // check for authority as used in UNC shares // or use the path as given if (path[0] === _slash && path[1] === _slash) { - let idx = path.indexOf(_slash, 2); + const idx = path.indexOf(_slash, 2); if (idx === -1) { authority = path.substring(2); path = _slash; @@ -364,7 +379,7 @@ export class URI implements UriComponents { } else if (data instanceof URI) { return data; } else { - let result = new _URI(data); + const result = new _URI(data); result._fsPath = (data).fsPath; result._formatted = (data).external; return result; @@ -473,7 +488,7 @@ function encodeURIComponentFast(uriComponent: string, allowSlash: boolean): stri let nativeEncodePos = -1; for (let pos = 0; pos < uriComponent.length; pos++) { - let code = uriComponent.charCodeAt(pos); + const code = uriComponent.charCodeAt(pos); // unreserved characters: https://tools.ietf.org/html/rfc3986#section-2.3 if ( @@ -503,7 +518,7 @@ function encodeURIComponentFast(uriComponent: string, allowSlash: boolean): stri } // check with default table first - let escaped = encodeTable[code]; + const escaped = encodeTable[code]; if (escaped !== undefined) { // check if we are delaying native encode @@ -532,7 +547,7 @@ function encodeURIComponentFast(uriComponent: string, allowSlash: boolean): stri function encodeURIComponentMinimal(path: string): string { let res: string | undefined = undefined; for (let pos = 0; pos < path.length; pos++) { - let code = path.charCodeAt(pos); + const code = path.charCodeAt(pos); if (code === CharCode.Hash || code === CharCode.QuestionMark) { if (res === undefined) { res = path.substr(0, pos); @@ -622,12 +637,12 @@ function _asFormatted(uri: URI, skipEncoding: boolean): string { if (path) { // lower-case windows drive letters in /C:/fff or C:/fff if (path.length >= 3 && path.charCodeAt(0) === CharCode.Slash && path.charCodeAt(2) === CharCode.Colon) { - let code = path.charCodeAt(1); + const code = path.charCodeAt(1); if (code >= CharCode.A && code <= CharCode.Z) { path = `/${String.fromCharCode(code + 32)}:${path.substr(3)}`; // "/c:".length === 3 } } else if (path.length >= 2 && path.charCodeAt(1) === CharCode.Colon) { - let code = path.charCodeAt(0); + const code = path.charCodeAt(0); if (code >= CharCode.A && code <= CharCode.Z) { path = `${String.fromCharCode(code + 32)}:${path.substr(2)}`; // "/c:".length === 3 } diff --git a/src/vs/base/node/decoder.ts b/src/vs/base/node/decoder.ts index 5ef4abf19f7..76741a370e4 100644 --- a/src/vs/base/node/decoder.ts +++ b/src/vs/base/node/decoder.ts @@ -24,8 +24,8 @@ export class LineDecoder { } public write(buffer: Buffer): string[] { - let result: string[] = []; - let value = this.remaining + const result: string[] = []; + const value = this.remaining ? this.remaining + this.stringDecoder.write(buffer) : this.stringDecoder.write(buffer); @@ -41,7 +41,7 @@ export class LineDecoder { result.push(value.substring(start, idx)); idx++; if (idx < value.length) { - let lastChar = ch; + const lastChar = ch; ch = value.charCodeAt(idx); if ((lastChar === CharCode.CarriageReturn && ch === CharCode.LineFeed) || (lastChar === CharCode.LineFeed && ch === CharCode.CarriageReturn)) { idx++; diff --git a/src/vs/base/node/extfs.ts b/src/vs/base/node/extfs.ts index 866ba70ddc9..a42ed5d8f95 100644 --- a/src/vs/base/node/extfs.ts +++ b/src/vs/base/node/extfs.ts @@ -219,7 +219,7 @@ export function del(path: string, tmpFolder: string, callback: (error: Error | n } function rmRecursive(path: string, callback: (error: Error | null) => void): void { - if (path === '\\' || path === '/') { + if (path === paths.win32.sep || path === paths.posix.sep) { return callback(new Error('Will not delete root!')); } @@ -277,6 +277,10 @@ function rmRecursive(path: string, callback: (error: Error | null) => void): voi } export function delSync(path: string): void { + if (path === paths.win32.sep || path === paths.posix.sep) { + throw new Error('Will not delete root!'); + } + try { const stat = fs.lstatSync(path); if (stat.isDirectory() && !stat.isSymbolicLink()) { diff --git a/src/vs/base/node/flow.ts b/src/vs/base/node/flow.ts index c1232c2993b..a97727ae88d 100644 --- a/src/vs/base/node/flow.ts +++ b/src/vs/base/node/flow.ts @@ -10,8 +10,8 @@ import * as assert from 'assert'; * 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 | 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); + const results = new Array(list.length); + const errors = new Array(list.length); let didErrorOccur = false; let doneCount = 0; @@ -68,9 +68,9 @@ export function loop(param: any, fn: (item: any, callback: (error: Error | nu // Expect the param to be an array and loop over it else { - let results: E[] = []; + const results: E[] = []; - let looper: (i: number) => void = function (i: number): void { + const looper: (i: number) => void = function (i: number): void { // Still work to do if (i < param.length) { @@ -126,11 +126,11 @@ function Sequence(sequences: { (...param: any[]): void; }[]): void { }); // Execute in Loop - let errorHandler = sequences.splice(0, 1)[0]; //Remove error handler + const errorHandler = sequences.splice(0, 1)[0]; //Remove error handler let sequenceResult: any = null; loop(sequences, (sequence, clb) => { - let sequenceFunction = function (error: any, result: any): void { + const sequenceFunction = function (error: any, result: any): void { // A method might only send a boolean value as return value (e.g. fs.exists), support this case gracefully if (error === true || error === false) { diff --git a/src/vs/base/node/ports.ts b/src/vs/base/node/ports.ts index a077f020fa9..d0334628e47 100644 --- a/src/vs/base/node/ports.ts +++ b/src/vs/base/node/ports.ts @@ -9,8 +9,8 @@ import * as net from 'net'; * @returns Returns a random port between 1025 and 65535. */ export function randomPort(): number { - let min = 1025; - let max = 65535; + const min = 1025; + const max = 65535; return min + Math.floor((max - min) * Math.random()); } diff --git a/src/vs/base/node/processes.ts b/src/vs/base/node/processes.ts index 0dab519e6a4..df8690d0011 100644 --- a/src/vs/base/node/processes.ts +++ b/src/vs/base/node/processes.ts @@ -42,7 +42,7 @@ function getWindowsCode(status: number): TerminateResponseCode { export function terminateProcess(process: cp.ChildProcess, cwd?: string): TerminateResponse { if (Platform.isWindows) { try { - let options: any = { + const options: any = { stdio: ['pipe', 'pipe', 'ignore'] }; if (cwd) { @@ -54,8 +54,8 @@ export function terminateProcess(process: cp.ChildProcess, cwd?: string): Termin } } else if (Platform.isLinux || Platform.isMacintosh) { try { - let cmd = getPathFromAmdModule(require, 'vs/base/node/terminateProcess.sh'); - let result = cp.spawnSync(cmd, [process.pid.toString()]); + const cmd = getPathFromAmdModule(require, 'vs/base/node/terminateProcess.sh'); + const result = cp.spawnSync(cmd, [process.pid.toString()]); if (result.error) { return { success: false, error: result.error }; } @@ -72,27 +72,6 @@ export function getWindowsShell(): string { return process.env['comspec'] || 'cmd.exe'; } -/** - * Sanitizes a VS Code process environment by removing all Electron/VS Code-related values. - */ -export function sanitizeProcessEnvironment(env: Platform.IProcessEnvironment): void { - const keysToRemove = [ - /^ELECTRON_.+$/, - /^GOOGLE_API_KEY$/, - /^VSCODE_.+$/, - /^SNAP(|_.*)$/ - ]; - const envKeys = Object.keys(env); - envKeys.forEach(envKey => { - for (let i = 0; i < keysToRemove.length; i++) { - if (envKey.search(keysToRemove[i]) !== -1) { - delete env[envKey]; - break; - } - } - }); -} - export abstract class AbstractProcess { private cmd: string; private args: string[]; @@ -134,7 +113,7 @@ export abstract class AbstractProcess { this.shell = arg3; this.options = arg4; } else { - let executable = arg1; + const executable = arg1; this.cmd = executable.command; this.shell = executable.isShellCommand; this.args = executable.args.slice(0); @@ -145,7 +124,7 @@ export abstract class AbstractProcess { this.terminateRequested = false; if (this.options.env) { - let newEnv: IStringDictionary = Object.create(null); + const newEnv: IStringDictionary = Object.create(null); Object.keys(process.env).forEach((key) => { newEnv[key] = process.env[key]!; }); @@ -158,7 +137,7 @@ export abstract class AbstractProcess { public getSanitizedCommand(): string { let result = this.cmd.toLowerCase(); - let index = result.lastIndexOf(path.sep); + const index = result.lastIndexOf(path.sep); if (index !== -1) { result = result.substring(index + 1); } @@ -175,7 +154,7 @@ export abstract class AbstractProcess { return this.useExec().then((useExec) => { let cc: ValueCallback; let ee: ErrorCallback; - let result = new Promise((c, e) => { + const result = new Promise((c, e) => { cc = c; ee = e; }); @@ -187,7 +166,7 @@ export abstract class AbstractProcess { } this.childProcess = cp.exec(cmd, this.options, (error, stdout, stderr) => { this.childProcess = null; - let err: any = error; + const err: any = error; // This is tricky since executing a command shell reports error back in case the executed command return an // error or the command didn't exist at all. So we can't blindly treat an error as a failed command. So we // always parse the output and report success unless the job got killed. @@ -199,11 +178,11 @@ export abstract class AbstractProcess { }); } else { let childProcess: cp.ChildProcess | null = null; - let closeHandler = (data: any) => { + const closeHandler = (data: any) => { this.childProcess = null; this.childProcessPromise = null; this.handleClose(data, cc, pp, ee); - let result: SuccessData = { + const result: SuccessData = { terminated: this.terminateRequested }; if (Types.isNumber(data)) { @@ -212,12 +191,12 @@ export abstract class AbstractProcess { cc(result); }; if (this.shell && Platform.isWindows) { - let options: any = Objects.deepClone(this.options); + const options: any = Objects.deepClone(this.options); options.windowsVerbatimArguments = true; options.detached = false; let quotedCommand: boolean = false; let quotedArg: boolean = false; - let commandLine: string[] = []; + const commandLine: string[] = []; let quoted = this.ensureQuotes(this.cmd); commandLine.push(quoted.value); quotedCommand = quoted.quoted; @@ -228,7 +207,7 @@ export abstract class AbstractProcess { quotedArg = quotedArg && quoted.quoted; }); } - let args: string[] = [ + const args: string[] = [ '/s', '/c', ]; @@ -308,7 +287,7 @@ export abstract class AbstractProcess { } return this.childProcessPromise.then((childProcess) => { this.terminateRequested = true; - let result = terminateProcess(childProcess, this.options.cwd); + const result = terminateProcess(childProcess, this.options.cwd); if (result.success) { this.childProcess = null; } @@ -321,14 +300,14 @@ export abstract class AbstractProcess { private useExec(): Promise { return new Promise((c, e) => { if (!this.shell || !Platform.isWindows) { - c(false); + return c(false); } - let cmdShell = cp.spawn(getWindowsShell(), ['/s', '/c']); + const cmdShell = cp.spawn(getWindowsShell(), ['/s', '/c']); cmdShell.on('error', (error: Error) => { - c(true); + return c(true); }); cmdShell.on('exit', (data: any) => { - c(false); + return c(false); }); }); } @@ -347,12 +326,12 @@ export class LineProcess extends AbstractProcess { protected handleExec(cc: ValueCallback, pp: ProgressCallback, error: Error, stdout: Buffer, stderr: Buffer) { [stdout, stderr].forEach((buffer: Buffer, index: number) => { - let lineDecoder = new LineDecoder(); - let lines = lineDecoder.write(buffer); + const lineDecoder = new LineDecoder(); + const lines = lineDecoder.write(buffer); lines.forEach((line) => { pp({ line: line, source: index === 0 ? Source.stdout : Source.stderr }); }); - let line = lineDecoder.end(); + const line = lineDecoder.end(); if (line) { pp({ line: line, source: index === 0 ? Source.stdout : Source.stderr }); } @@ -364,11 +343,11 @@ export class LineProcess extends AbstractProcess { this.stdoutLineDecoder = new LineDecoder(); this.stderrLineDecoder = new LineDecoder(); childProcess.stdout.on('data', (data: Buffer) => { - let lines = this.stdoutLineDecoder.write(data); + const lines = this.stdoutLineDecoder.write(data); lines.forEach(line => pp({ line: line, source: Source.stdout })); }); childProcess.stderr.on('data', (data: Buffer) => { - let lines = this.stderrLineDecoder.write(data); + const lines = this.stderrLineDecoder.write(data); lines.forEach(line => pp({ line: line, source: Source.stderr })); }); } @@ -401,7 +380,7 @@ export function createQueuedSender(childProcess: cp.ChildProcess): IQueuedSender return; } - let result = childProcess.send(msg, (error: Error) => { + const result = childProcess.send(msg, (error: Error) => { if (error) { console.error(error); // unlikely to happen, best we can do is log this error } @@ -433,7 +412,7 @@ export namespace win32 { if (cwd === undefined) { cwd = process.cwd(); } - let dir = path.dirname(command); + const dir = path.dirname(command); if (dir !== '.') { // We have a directory and the directory is relative (see above). Make the path absolute // to the current working directory. @@ -470,4 +449,4 @@ export namespace win32 { } return path.join(cwd, command); } -} \ No newline at end of file +} diff --git a/src/vs/base/node/ps.ts b/src/vs/base/node/ps.ts index e42e9420809..8a0b2845c4f 100644 --- a/src/vs/base/node/ps.ts +++ b/src/vs/base/node/ps.ts @@ -151,7 +151,7 @@ export function listProcesses(rootPid: number): Promise { rootItem = processItems.get(rootPid); if (rootItem) { processItems.forEach(item => { - let parent = processItems.get(item.ppid); + const parent = processItems.get(item.ppid); if (parent) { if (!parent.children) { parent.children = []; @@ -186,7 +186,7 @@ export function listProcesses(rootPid: number): Promise { const lines = stdout.toString().split('\n'); for (const line of lines) { - let matches = PID_CMD.exec(line.trim()); + const matches = PID_CMD.exec(line.trim()); if (matches && matches.length === 6) { addToTree(parseInt(matches[1]), parseInt(matches[2]), matches[5], parseFloat(matches[3]), parseFloat(matches[4])); } diff --git a/src/vs/base/node/request.ts b/src/vs/base/node/request.ts index 0d8d7434f66..ea24cdb7589 100644 --- a/src/vs/base/node/request.ts +++ b/src/vs/base/node/request.ts @@ -152,7 +152,7 @@ export function asText(context: IRequestContext): Promise { return c(null); } - let buffer: string[] = []; + const buffer: string[] = []; context.stream.on('data', (d: string) => buffer.push(d)); context.stream.on('end', () => c(buffer.join(''))); context.stream.on('error', e); diff --git a/src/vs/base/node/stats.ts b/src/vs/base/node/stats.ts index 4b89e868fe1..1c3598a5d30 100644 --- a/src/vs/base/node/stats.ts +++ b/src/vs/base/node/stats.ts @@ -21,15 +21,15 @@ export interface WorkspaceStats { } function asSortedItems(map: Map): WorkspaceStatItem[] { - let a: WorkspaceStatItem[] = []; + const a: WorkspaceStatItem[] = []; map.forEach((value, index) => a.push({ name: index, count: value })); return a.sort((a, b) => b.count - a.count); } export function collectLaunchConfigs(folder: string): Promise { - let launchConfigs = new Map(); + const launchConfigs = new Map(); - let launchConfig = join(folder, '.vscode', 'launch.json'); + const launchConfig = join(folder, '.vscode', 'launch.json'); return new Promise((resolve, reject) => { exists(launchConfig, (doesExist) => { if (doesExist) { @@ -87,8 +87,8 @@ export async function collectWorkspaceStats(folder: string, filter: string[]): P { 'tag': 'cmake', 'pattern': /^.+\.cmake$/i } ]; - let fileTypes = new Map(); - let configFiles = new Map(); + const fileTypes = new Map(); + const configFiles = new Map(); const MAX_FILES = 20000; @@ -149,7 +149,7 @@ export async function collectWorkspaceStats(folder: string, filter: string[]): P }); } - let addFileType = (fileType: string) => { + const addFileType = (fileType: string) => { if (fileTypes.has(fileType)) { fileTypes.set(fileType, fileTypes.get(fileType)! + 1); } @@ -158,7 +158,7 @@ export async function collectWorkspaceStats(folder: string, filter: string[]): P } }; - let addConfigFiles = (fileName: string) => { + const addConfigFiles = (fileName: string) => { for (const each of configFilePatterns) { if (each.pattern.test(fileName)) { if (configFiles.has(each.tag)) { @@ -170,9 +170,9 @@ export async function collectWorkspaceStats(folder: string, filter: string[]): P } }; - let acceptFile = (name: string) => { + const acceptFile = (name: string) => { if (name.lastIndexOf('.') >= 0) { - let suffix: string | undefined = name.split('.').pop(); + const suffix: string | undefined = name.split('.').pop(); if (suffix) { addFileType(suffix); } @@ -180,13 +180,13 @@ export async function collectWorkspaceStats(folder: string, filter: string[]): P addConfigFiles(name); }; - let token: { count: number, maxReached: boolean } = { count: 0, maxReached: false }; + const token: { count: number, maxReached: boolean } = { count: 0, maxReached: false }; return new Promise((resolve, reject) => { walk(folder, filter, token, async (files) => { files.forEach(acceptFile); - let launchConfigs = await collectLaunchConfigs(folder); + const launchConfigs = await collectLaunchConfigs(folder); resolve({ configFiles: asSortedItems(configFiles), diff --git a/src/vs/base/node/storage.ts b/src/vs/base/node/storage.ts index 546396f1959..455b278aac7 100644 --- a/src/vs/base/node/storage.ts +++ b/src/vs/base/node/storage.ts @@ -61,8 +61,8 @@ export interface IStorage extends IDisposable { getBoolean(key: string, fallbackValue: boolean): boolean; getBoolean(key: string, fallbackValue?: boolean): boolean | undefined; - getInteger(key: string, fallbackValue: number): number; - getInteger(key: string, fallbackValue?: number): number | undefined; + getNumber(key: string, fallbackValue: number): number; + getNumber(key: string, fallbackValue?: number): number | undefined; set(key: string, value: string | boolean | number): Promise; delete(key: string): Promise; @@ -195,9 +195,9 @@ export class Storage extends Disposable implements IStorage { return value === 'true'; } - getInteger(key: string, fallbackValue: number): number; - getInteger(key: string, fallbackValue?: number): number | undefined; - getInteger(key: string, fallbackValue?: number): number | undefined { + getNumber(key: string, fallbackValue: number): number; + getNumber(key: string, fallbackValue?: number): number | undefined; + getNumber(key: string, fallbackValue?: number): number | undefined { const value = this.get(key); if (isUndefinedOrNull(value)) { @@ -352,7 +352,7 @@ export class SQLiteStorageDatabase implements IStorageDatabase { rows.forEach(row => items.set(row.key, row.value)); if (this.logger.isTracing) { - this.logger.trace(`[storage ${this.name}] getItems(): ${mapToString(items)}`); + this.logger.trace(`[storage ${this.name}] getItems(): ${items.size} rows`); } return items; diff --git a/src/vs/base/node/stream.ts b/src/vs/base/node/stream.ts index 845afdab8e9..1c8d7e73ede 100644 --- a/src/vs/base/node/stream.ts +++ b/src/vs/base/node/stream.ts @@ -92,7 +92,7 @@ export function readToMatchingString(file: string, matchingString: string, chunk }); } - let buffer = Buffer.allocUnsafe(maximumBytesToRead); + const buffer = Buffer.allocUnsafe(maximumBytesToRead); let offset = 0; function readChunk(): void { diff --git a/src/vs/base/node/zip.ts b/src/vs/base/node/zip.ts index cb9fbfbf25e..e2221f1b243 100644 --- a/src/vs/base/node/zip.ts +++ b/src/vs/base/node/zip.ts @@ -49,7 +49,7 @@ export class ExtractError extends Error { } function modeFromEntry(entry: Entry) { - let attr = entry.externalFileAttributes >> 16 || 33188; + const attr = entry.externalFileAttributes >> 16 || 33188; return [448 /* S_IRWXU */, 56 /* S_IRWXG */, 7 /* S_IRWXO */] .map(mask => attr & mask) diff --git a/src/vs/base/parts/ipc/electron-browser/ipc.electron-browser.ts b/src/vs/base/parts/ipc/electron-browser/ipc.electron-browser.ts index 225370373a7..055d6a54130 100644 --- a/src/vs/base/parts/ipc/electron-browser/ipc.electron-browser.ts +++ b/src/vs/base/parts/ipc/electron-browser/ipc.electron-browser.ts @@ -14,7 +14,7 @@ export class Client extends IPCClient implements IDisposable { private protocol: Protocol; private static createProtocol(): Protocol { - const onMessage = Event.fromNodeEventEmitter(ipcRenderer, 'ipc:message', (_, message: string) => message); + const onMessage = Event.fromNodeEventEmitter(ipcRenderer, 'ipc:message', (_, message: Buffer) => message); ipcRenderer.send('ipc:hello'); return new Protocol(ipcRenderer, onMessage); } diff --git a/src/vs/base/parts/ipc/electron-main/ipc.electron-main.ts b/src/vs/base/parts/ipc/electron-main/ipc.electron-main.ts index 17ae7bb81b6..f7d72041911 100644 --- a/src/vs/base/parts/ipc/electron-main/ipc.electron-main.ts +++ b/src/vs/base/parts/ipc/electron-main/ipc.electron-main.ts @@ -11,11 +11,11 @@ import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; interface IIPCEvent { event: { sender: Electron.WebContents; }; - message: string; + message: Buffer | null; } -function createScopedOnMessageEvent(senderId: number, eventName: string): Event { - const onMessage = Event.fromNodeEventEmitter(ipcMain, eventName, (event, message: string) => ({ event, message })); +function createScopedOnMessageEvent(senderId: number, eventName: string): Event { + const onMessage = Event.fromNodeEventEmitter(ipcMain, eventName, (event, message) => ({ event, message })); const onMessageFromSender = Event.filter(onMessage, ({ event }) => event.sender.id === senderId); return Event.map(onMessageFromSender, ({ message }) => message); } @@ -38,7 +38,7 @@ export class Server extends IPCServer { const onDidClientReconnect = new Emitter(); Server.Clients.set(id, toDisposable(() => onDidClientReconnect.fire())); - const onMessage = createScopedOnMessageEvent(id, 'ipc:message'); + const onMessage = createScopedOnMessageEvent(id, 'ipc:message') as Event; const onDidClientDisconnect = Event.any(Event.signal(createScopedOnMessageEvent(id, 'ipc:disconnect')), onDidClientReconnect.event); const protocol = new Protocol(webContents, onMessage); diff --git a/src/vs/base/parts/ipc/node/ipc.electron.ts b/src/vs/base/parts/ipc/node/ipc.electron.ts index 831a27377c1..e70289747fc 100644 --- a/src/vs/base/parts/ipc/node/ipc.electron.ts +++ b/src/vs/base/parts/ipc/node/ipc.electron.ts @@ -3,33 +3,20 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/node/ipc'; -import { Event, Emitter } from 'vs/base/common/event'; - -/** - * This implementation doesn't perform well since it uses base64 encoding for buffers. - * Electron 3.0 should have suport for buffers in IPC: https://github.com/electron/electron/pull/13055 - */ +import { Event } from 'vs/base/common/event'; export interface Sender { - send(channel: string, msg: string | null): void; + send(channel: string, msg: Buffer | null): void; } export class Protocol implements IMessagePassingProtocol { - private listener: IDisposable; - - private _onMessage = new Emitter(); - get onMessage(): Event { return this._onMessage.event; } - - constructor(private sender: Sender, onMessageEvent: Event) { - onMessageEvent(msg => this._onMessage.fire(Buffer.from(msg, 'base64'))); - } + constructor(private sender: Sender, readonly onMessage: Event) { } send(message: Buffer): void { try { - this.sender.send('ipc:message', message.toString('base64')); + this.sender.send('ipc:message', message); } catch (e) { // systems are going down } @@ -37,6 +24,5 @@ export class Protocol implements IMessagePassingProtocol { dispose(): void { this.sender.send('ipc:disconnect', null); - this.listener = dispose(this.listener); } } \ No newline at end of file diff --git a/src/vs/base/parts/ipc/node/ipc.net.ts b/src/vs/base/parts/ipc/node/ipc.net.ts index 08f2def654f..9cc68b10be5 100644 --- a/src/vs/base/parts/ipc/node/ipc.net.ts +++ b/src/vs/base/parts/ipc/node/ipc.net.ts @@ -10,7 +10,6 @@ import { join } from 'vs/base/common/path'; import { tmpdir } from 'os'; import { generateUuid } from 'vs/base/common/uuid'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { TimeoutTimer } from 'vs/base/common/async'; export function generateRandomPipeName(): string { const randomSuffix = generateUuid(); @@ -22,6 +21,80 @@ export function generateRandomPipeName(): string { } } +class ChunkStream { + + private _chunks: Buffer[]; + private _totalLength: number; + + public get byteLength() { + return this._totalLength; + } + + constructor() { + this._chunks = []; + this._totalLength = 0; + } + + public acceptChunk(buff: Buffer) { + this._chunks.push(buff); + this._totalLength += buff.byteLength; + } + + public readUInt32BE(): number { + let tmp = this.read(4); + return tmp.readUInt32BE(0); + } + + public read(byteCount: number): Buffer { + if (byteCount === 0) { + return Buffer.allocUnsafe(0); + } + + if (byteCount > this._totalLength) { + throw new Error(`Cannot read so many bytes!`); + } + + if (this._chunks[0].byteLength === byteCount) { + // super fast path, precisely first chunk must be returned + const result = this._chunks.shift()!; + this._totalLength -= byteCount; + return result; + } + + if (this._chunks[0].byteLength > byteCount) { + // fast path, the reading is entirely within the first chunk + const result = this._chunks[0].slice(0, byteCount); + this._chunks[0] = this._chunks[0].slice(byteCount); + this._totalLength -= byteCount; + return result; + } + + let result = Buffer.allocUnsafe(byteCount); + let resultOffset = 0; + while (byteCount > 0) { + const chunk = this._chunks[0]; + if (chunk.byteLength > byteCount) { + // this chunk will survive + this._chunks[0] = chunk.slice(byteCount); + + chunk.copy(result, resultOffset, 0, byteCount); + resultOffset += byteCount; + this._totalLength -= byteCount; + byteCount -= byteCount; + } else { + // this chunk will be entirely read + this._chunks.shift(); + + chunk.copy(result, resultOffset, 0, chunk.byteLength); + resultOffset += chunk.byteLength; + this._totalLength -= chunk.byteLength; + byteCount -= chunk.byteLength; + } + } + return result; + } +} + /** * A message has the following format: * @@ -35,9 +108,8 @@ export class Protocol implements IDisposable, IMessagePassingProtocol { private static readonly _headerLen = 4; private _isDisposed: boolean; - private _chunks: Buffer[]; + private _incomingData: ChunkStream; - private _firstChunkTimer: TimeoutTimer; private _socketDataListener: (data: Buffer) => void; private _socketEndListener: () => void; private _socketCloseListener: () => void; @@ -48,11 +120,9 @@ export class Protocol implements IDisposable, IMessagePassingProtocol { private _onClose = new Emitter(); readonly onClose: Event = this._onClose.event; - constructor(private _socket: Socket, firstDataChunk?: Buffer) { + constructor(private _socket: Socket) { this._isDisposed = false; - this._chunks = []; - - let totalLength = 0; + this._incomingData = new ChunkStream(); const state = { readHead: true, @@ -61,24 +131,15 @@ export class Protocol implements IDisposable, IMessagePassingProtocol { const acceptChunk = (data: Buffer) => { - this._chunks.push(data); - totalLength += data.length; + this._incomingData.acceptChunk(data); - while (totalLength > 0) { + while (this._incomingData.byteLength > 0) { if (state.readHead) { - // expecting header -> read 5bytes for header - // information: `bodyIsJson` and `bodyLen` - if (totalLength >= Protocol._headerLen) { - const all = Buffer.concat(this._chunks); - - state.bodyLen = all.readUInt32BE(0); + // expecting header -> read header + if (this._incomingData.byteLength >= Protocol._headerLen) { + state.bodyLen = this._incomingData.readUInt32BE(); state.readHead = false; - - const rest = all.slice(Protocol._headerLen); - totalLength = rest.length; - this._chunks = [rest]; - } else { break; } @@ -87,15 +148,8 @@ export class Protocol implements IDisposable, IMessagePassingProtocol { if (!state.readHead) { // expecting body -> read bodyLen-bytes for // the actual message or wait for more data - if (totalLength >= state.bodyLen) { - - const all = Buffer.concat(this._chunks); - const buffer = all.slice(0, state.bodyLen); - - // ensure the getBuffer returns a valid value if invoked from the event listeners - const rest = all.slice(state.bodyLen); - totalLength = rest.length; - this._chunks = [rest]; + if (this._incomingData.byteLength >= state.bodyLen) { + const buffer = this._incomingData.read(state.bodyLen); state.bodyLen = -1; state.readHead = true; @@ -113,28 +167,12 @@ export class Protocol implements IDisposable, IMessagePassingProtocol { } }; - const acceptFirstDataChunk = () => { - if (firstDataChunk && firstDataChunk.length > 0) { - let tmp = firstDataChunk; - firstDataChunk = undefined; - acceptChunk(tmp); - } - }; - - // Make sure to always handle the firstDataChunk if no more `data` event comes in - this._firstChunkTimer = new TimeoutTimer(); - this._firstChunkTimer.setIfNotSet(() => { - acceptFirstDataChunk(); - }, 0); - this._socketDataListener = (data: Buffer) => { - acceptFirstDataChunk(); acceptChunk(data); }; _socket.on('data', this._socketDataListener); this._socketEndListener = () => { - acceptFirstDataChunk(); }; _socket.on('end', this._socketEndListener); @@ -146,7 +184,6 @@ export class Protocol implements IDisposable, IMessagePassingProtocol { dispose(): void { this._isDisposed = true; - this._firstChunkTimer.dispose(); this._socket.removeListener('data', this._socketDataListener); this._socket.removeListener('end', this._socketEndListener); this._socket.removeListener('close', this._socketCloseListener); @@ -156,8 +193,8 @@ export class Protocol implements IDisposable, IMessagePassingProtocol { this._socket.end(); } - getBuffer(): Buffer { - return Buffer.concat(this._chunks); + readEntireBuffer(): Buffer { + return this._incomingData.read(this._incomingData.byteLength); } send(buffer: Buffer): void { diff --git a/src/vs/base/parts/ipc/test/node/ipc.net.test.ts b/src/vs/base/parts/ipc/test/node/ipc.net.test.ts index 439dce11f52..5288308dc37 100644 --- a/src/vs/base/parts/ipc/test/node/ipc.net.test.ts +++ b/src/vs/base/parts/ipc/test/node/ipc.net.test.ts @@ -85,39 +85,4 @@ suite('IPC, Socket Protocol', () => { }); }); }); - - test('can devolve to a socket and evolve again without losing data', () => { - let resolve: (v: void) => void; - let result = new Promise((_resolve, _reject) => { - resolve = _resolve; - }); - const sender = new Protocol(stream); - const receiver1 = new Protocol(stream); - - assert.equal(stream.listenerCount('data'), 2); - assert.equal(stream.listenerCount('end'), 2); - - receiver1.onMessage((msg) => { - assert.equal(JSON.parse(msg.toString()).value, 1); - - let buffer = receiver1.getBuffer(); - receiver1.dispose(); - - assert.equal(stream.listenerCount('data'), 1); - assert.equal(stream.listenerCount('end'), 1); - - const receiver2 = new Protocol(stream, buffer); - receiver2.onMessage((msg) => { - assert.equal(JSON.parse(msg.toString()).value, 2); - resolve(undefined); - }); - }); - - const msg1 = { value: 1 }; - const msg2 = { value: 2 }; - sender.send(Buffer.from(JSON.stringify(msg1))); - sender.send(Buffer.from(JSON.stringify(msg2))); - - return result; - }); }); diff --git a/src/vs/base/parts/tree/browser/treeView.ts b/src/vs/base/parts/tree/browser/treeView.ts index 5e7e31b7d3e..39ed5525f7f 100644 --- a/src/vs/base/parts/tree/browser/treeView.ts +++ b/src/vs/base/parts/tree/browser/treeView.ts @@ -1422,6 +1422,8 @@ export class TreeView extends HeightMap { } private onDragOver(e: DragEvent): boolean { + e.preventDefault(); // needed so that the drop event fires (https://stackoverflow.com/questions/21339924/drop-event-not-firing-in-chrome) + let event = new Mouse.DragMouseEvent(e); let viewItem = this.getItemAround(event.target); diff --git a/src/vs/base/test/browser/ui/menu/menubar.test.ts b/src/vs/base/test/browser/ui/menu/menubar.test.ts new file mode 100644 index 00000000000..8bbeddcfc58 --- /dev/null +++ b/src/vs/base/test/browser/ui/menu/menubar.test.ts @@ -0,0 +1,81 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { $ } from 'vs/base/browser/dom'; +import { MenuBar } from 'vs/base/browser/ui/menu/menubar'; + +function getButtonElementByAriaLabel(menubarElement: HTMLElement, ariaLabel: string): HTMLElement | null { + let i; + for (i = 0; i < menubarElement.childElementCount; i++) { + + if (menubarElement.children[i].getAttribute('aria-label') === ariaLabel) { + return menubarElement.children[i] as HTMLElement; + } + } + + return null; +} + +function getTitleDivFromButtonDiv(menuButtonElement: HTMLElement): HTMLElement | null { + let i; + for (i = 0; i < menuButtonElement.childElementCount; i++) { + if (menuButtonElement.children[i].classList.contains('menubar-menu-title')) { + return menuButtonElement.children[i] as HTMLElement; + } + } + + return null; +} + +function getMnemonicFromTitleDiv(menuTitleDiv: HTMLElement): string | null { + let i; + for (i = 0; i < menuTitleDiv.childElementCount; i++) { + if (menuTitleDiv.children[i].tagName.toLocaleLowerCase() === 'mnemonic') { + return menuTitleDiv.children[i].textContent; + } + } + + return null; +} + +function validateMenuBarItem(menubar: MenuBar, menubarContainer: HTMLElement, label: string, readableLabel: string, mnemonic: string) { + menubar.push([ + { + actions: [], + label: label + } + ]); + + const buttonElement = getButtonElementByAriaLabel(menubarContainer, readableLabel); + assert(buttonElement !== null, `Button element not found for ${readableLabel} button.`); + + const titleDiv = getTitleDivFromButtonDiv(buttonElement!); + assert(titleDiv !== null, `Title div not found for ${readableLabel} button.`); + + const mnem = getMnemonicFromTitleDiv(titleDiv!); + assert.equal(mnem, mnemonic, 'Mnemonic not correct'); +} + +suite('Menubar', () => { + const container = $('.container'); + + const menubar = new MenuBar(container, { + enableMnemonics: true, + visibility: 'visible' + }); + + test('English File menu renders mnemonics', function () { + validateMenuBarItem(menubar, container, '&File', 'File', 'F'); + }); + + test('Russian File menu renders mnemonics', function () { + validateMenuBarItem(menubar, container, '&Файл', 'Файл', 'Ф'); + }); + + test('Chinese File menu renders mnemonics', function () { + validateMenuBarItem(menubar, container, '文件(&F)', '文件', 'F'); + }); +}); \ No newline at end of file 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 6ceccbd08dc..3c934126df9 100644 --- a/src/vs/base/test/browser/ui/tree/asyncDataTree.test.ts +++ b/src/vs/base/test/browser/ui/tree/asyncDataTree.test.ts @@ -189,4 +189,141 @@ suite('AsyncDataTree', function () { assert(hasClass(twistie, 'collapsed')); assert(tree.getNode().children[0].collapsed); }); + + test('issue #67722 - once resolved, refreshed collapsed nodes should only get children when expanded', async () => { + const container = document.createElement('div'); + container.style.width = '200px'; + container.style.height = '200px'; + + const delegate = new class implements IListVirtualDelegate { + getHeight() { return 20; } + getTemplateId(element: Element): string { return 'default'; } + }; + + const renderer = new class implements ITreeRenderer { + readonly templateId = 'default'; + renderTemplate(container: HTMLElement): HTMLElement { + return container; + } + renderElement(element: ITreeNode, index: number, templateData: HTMLElement): void { + templateData.textContent = element.element.id; + } + disposeTemplate(templateData: HTMLElement): void { + // noop + } + }; + + const getChildrenCalls: string[] = []; + const dataSource = new class implements IAsyncDataSource { + hasChildren(element: Element): boolean { + return !!element.children && element.children.length > 0; + } + getChildren(element: Element): Promise { + getChildrenCalls.push(element.id); + return Promise.resolve(element.children || []); + } + }; + + const identityProvider = new class implements IIdentityProvider { + getId(element: Element) { + return element.id; + } + }; + + const root: Element = { + id: 'root', + children: [{ + id: 'a', children: [{ id: 'aa' }, { id: 'ab' }, { id: 'ac' }] + }] + }; + + const _: (id: string) => Element = find.bind(null, root.children); + + const tree = new AsyncDataTree(container, delegate, [renderer], dataSource, { identityProvider }); + tree.layout(200); + + await tree.setInput(root); + assert(tree.getNode(_('a')).collapsed); + assert.deepStrictEqual(getChildrenCalls, ['root']); + + await tree.expand(_('a')); + assert(!tree.getNode(_('a')).collapsed); + assert.deepStrictEqual(getChildrenCalls, ['root', 'a']); + + tree.collapse(_('a')); + assert(tree.getNode(_('a')).collapsed); + assert.deepStrictEqual(getChildrenCalls, ['root', 'a']); + + await tree.updateChildren(); + assert(tree.getNode(_('a')).collapsed); + assert.deepStrictEqual(getChildrenCalls, ['root', 'a', 'root'], 'a should not be refreshed, since it\' collapsed'); + }); + + test('resolved collapsed nodes which lose children should lose twistie as well', async () => { + const container = document.createElement('div'); + container.style.width = '200px'; + container.style.height = '200px'; + + const delegate = new class implements IListVirtualDelegate { + getHeight() { return 20; } + getTemplateId(element: Element): string { return 'default'; } + }; + + const renderer = new class implements ITreeRenderer { + readonly templateId = 'default'; + renderTemplate(container: HTMLElement): HTMLElement { + return container; + } + renderElement(element: ITreeNode, index: number, templateData: HTMLElement): void { + templateData.textContent = element.element.id; + } + disposeTemplate(templateData: HTMLElement): void { + // noop + } + }; + + const dataSource = new class implements IAsyncDataSource { + hasChildren(element: Element): boolean { + return !!element.children && element.children.length > 0; + } + getChildren(element: Element): Promise { + return Promise.resolve(element.children || []); + } + }; + + const identityProvider = new class implements IIdentityProvider { + getId(element: Element) { + return element.id; + } + }; + + const root: Element = { + id: 'root', + children: [{ + id: 'a', children: [{ id: 'aa' }, { id: 'ab' }, { id: 'ac' }] + }] + }; + + const _: (id: string) => Element = find.bind(null, root.children); + + const tree = new AsyncDataTree(container, delegate, [renderer], dataSource, { identityProvider }); + tree.layout(200); + + await tree.setInput(root); + await tree.expand(_('a')); + + let twistie = container.querySelector('.monaco-list-row:first-child .monaco-tl-twistie') as HTMLElement; + assert(hasClass(twistie, 'collapsible')); + assert(!hasClass(twistie, 'collapsed')); + assert(!tree.getNode(_('a')).collapsed); + + tree.collapse(_('a')); + _('a').children = []; + await tree.updateChildren(root); + + twistie = container.querySelector('.monaco-list-row:first-child .monaco-tl-twistie') as HTMLElement; + assert(!hasClass(twistie, 'collapsible')); + assert(!hasClass(twistie, 'collapsed')); + assert(tree.getNode(_('a')).collapsed); + }); }); \ No newline at end of file diff --git a/src/vs/base/test/common/path.test.ts b/src/vs/base/test/common/path.test.ts index f16f61a9bea..e135fd3a177 100644 --- a/src/vs/base/test/common/path.test.ts +++ b/src/vs/base/test/common/path.test.ts @@ -753,11 +753,11 @@ suite('Paths (Node Implementation)', () => { // posix assert.strictEqual(path.posix.delimiter, ':'); - if (isWindows) { - assert.strictEqual(path, path.win32); - } else { - assert.strictEqual(path, path.posix); - } + // if (isWindows) { + // assert.strictEqual(path, path.win32); + // } else { + // assert.strictEqual(path, path.posix); + // } }); // test('perf', () => { diff --git a/src/vs/base/test/common/processes.test.ts b/src/vs/base/test/common/processes.test.ts new file mode 100644 index 00000000000..cd54b6fd724 --- /dev/null +++ b/src/vs/base/test/common/processes.test.ts @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import * as processes from 'vs/base/common/processes'; + +suite('Processes', () => { + test('sanitizeProcessEnvironment', () => { + let env = { + FOO: 'bar', + ELECTRON_ENABLE_STACK_DUMPING: 'x', + ELECTRON_ENABLE_LOGGING: 'x', + ELECTRON_NO_ASAR: 'x', + ELECTRON_NO_ATTACH_CONSOLE: 'x', + ELECTRON_RUN_AS_NODE: 'x', + GOOGLE_API_KEY: 'x', + VSCODE_CLI: 'x', + VSCODE_DEV: 'x', + VSCODE_IPC_HOOK: 'x', + VSCODE_LOGS: 'x', + VSCODE_NLS_CONFIG: 'x', + VSCODE_PORTABLE: 'x', + VSCODE_PID: 'x', + VSCODE_NODE_CACHED_DATA_DIR: 'x', + VSCODE_NEW_VAR: 'x' + }; + processes.sanitizeProcessEnvironment(env); + assert.equal(env['FOO'], 'bar'); + assert.equal(Object.keys(env).length, 1); + }); +}); diff --git a/src/vs/base/test/common/resources.test.ts b/src/vs/base/test/common/resources.test.ts index cf1ad37c228..fc2428541d6 100644 --- a/src/vs/base/test/common/resources.test.ts +++ b/src/vs/base/test/common/resources.test.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { dirname, basename, distinctParents, joinPath, isEqual, isEqualOrParent, hasToIgnoreCase, normalizePath, isAbsolutePath, isMalformedFileUri, relativePath, removeTrailingPathSeparator, hasTrailingPathSeparator, resolvePath } from 'vs/base/common/resources'; -import { URI, setUriThrowOnMissingScheme } from 'vs/base/common/uri'; +import { dirname, basename, distinctParents, joinPath, isEqual, isEqualOrParent, hasToIgnoreCase, normalizePath, isAbsolutePath, relativePath, removeTrailingPathSeparator, hasTrailingPathSeparator, resolvePath } from 'vs/base/common/resources'; +import { URI } from 'vs/base/common/uri'; import { isWindows } from 'vs/base/common/platform'; import { toSlashes } from 'vs/base/common/extpath'; import { startsWith } from 'vs/base/common/strings'; @@ -357,27 +357,4 @@ suite('Resources', () => { assert.equal(isEqualOrParent(fileURI3, fileURI, true), false, '15'); assert.equal(isEqualOrParent(fileURI5, fileURI5, true), true, '16'); }); - - function assertMalformedFileUri(path: string, expected: string | undefined) { - const old = setUriThrowOnMissingScheme(false); - const newURI = isMalformedFileUri(URI.parse(path)); - assert.equal(newURI && newURI.toString(), expected); - setUriThrowOnMissingScheme(old); - } - - test('isMalformedFileUri', () => { - if (isWindows) { - assertMalformedFileUri('c:/foo/bar', 'file:///c%3A/foo/bar'); - assertMalformedFileUri('c:\\foo\\bar', 'file:///c%3A/foo/bar'); - assertMalformedFileUri('C:\\foo\\bar', 'file:///c%3A/foo/bar'); - assertMalformedFileUri('\\\\localhost\\c$\\devel\\test', 'file://localhost/c%24/devel/test'); - } - assertMalformedFileUri('/foo/bar', 'file:///foo/bar'); - - assertMalformedFileUri('file:///foo/bar', undefined); - assertMalformedFileUri('file:///c%3A/foo/bar', undefined); - assertMalformedFileUri('file://localhost/c$/devel/test', undefined); - assertMalformedFileUri('foo://dadie/foo/bar', undefined); - assertMalformedFileUri('foo:///dadie/foo/bar', undefined); - }); }); diff --git a/src/vs/base/test/node/processes/processes.test.ts b/src/vs/base/test/node/processes/processes.test.ts index 10199cf5bf3..76719506d6e 100644 --- a/src/vs/base/test/node/processes/processes.test.ts +++ b/src/vs/base/test/node/processes/processes.test.ts @@ -84,29 +84,4 @@ suite('Processes', () => { } }); }); - - - test('sanitizeProcessEnvironment', () => { - let env = { - FOO: 'bar', - ELECTRON_ENABLE_STACK_DUMPING: 'x', - ELECTRON_ENABLE_LOGGING: 'x', - ELECTRON_NO_ASAR: 'x', - ELECTRON_NO_ATTACH_CONSOLE: 'x', - ELECTRON_RUN_AS_NODE: 'x', - GOOGLE_API_KEY: 'x', - VSCODE_CLI: 'x', - VSCODE_DEV: 'x', - VSCODE_IPC_HOOK: 'x', - VSCODE_LOGS: 'x', - VSCODE_NLS_CONFIG: 'x', - VSCODE_PORTABLE: 'x', - VSCODE_PID: 'x', - VSCODE_NODE_CACHED_DATA_DIR: 'x', - VSCODE_NEW_VAR: 'x' - }; - processes.sanitizeProcessEnvironment(env); - assert.equal(env['FOO'], 'bar'); - assert.equal(Object.keys(env).length, 1); - }); }); diff --git a/src/vs/base/test/node/storage/storage.test.ts b/src/vs/base/test/node/storage/storage.test.ts index 67c918e9e77..bbd9e00d992 100644 --- a/src/vs/base/test/node/storage/storage.test.ts +++ b/src/vs/base/test/node/storage/storage.test.ts @@ -31,7 +31,7 @@ suite('Storage Library', () => { // Empty fallbacks equal(storage.get('foo', 'bar'), 'bar'); - equal(storage.getInteger('foo', 55), 55); + equal(storage.getNumber('foo', 55), 55); equal(storage.getBoolean('foo', true), true); let changes = new Set(); @@ -45,7 +45,7 @@ suite('Storage Library', () => { const set3Promise = storage.set('barBoolean', true); equal(storage.get('bar'), 'foo'); - equal(storage.getInteger('barNumber'), 55); + equal(storage.getNumber('barNumber'), 55); equal(storage.getBoolean('barBoolean'), true); equal(changes.size, 3); @@ -71,7 +71,7 @@ suite('Storage Library', () => { const delete3Promise = storage.delete('barBoolean'); ok(!storage.get('bar')); - ok(!storage.getInteger('barNumber')); + ok(!storage.getNumber('barNumber')); ok(!storage.getBoolean('barBoolean')); equal(changes.size, 3); diff --git a/src/vs/base/worker/defaultWorkerFactory.ts b/src/vs/base/worker/defaultWorkerFactory.ts index 30ad9bf632f..eccaa81f139 100644 --- a/src/vs/base/worker/defaultWorkerFactory.ts +++ b/src/vs/base/worker/defaultWorkerFactory.ts @@ -6,7 +6,7 @@ import { globals } from 'vs/base/common/platform'; import { IWorker, IWorkerCallback, IWorkerFactory, logOnceWebWorkerWarning } from 'vs/base/common/worker/simpleWorker'; -function getWorker(workerId: string, label: string): Worker { +function getWorker(workerId: string, label: string): Worker | Promise { // Option for hosts to overwrite the worker script (used in the standalone editor) if (globals.MonacoEnvironment) { if (typeof globals.MonacoEnvironment.getWorker === 'function') { @@ -18,12 +18,34 @@ function getWorker(workerId: string, label: string): Worker { } // ESM-comment-begin if (typeof require === 'function') { - return new Worker(require.toUrl('./' + workerId) + '#' + label); + // check if the JS lives on a different origin + + const workerMain = require.toUrl('./' + workerId); + if (/^(http:)|(https:)|(file:)/.test(workerMain)) { + const currentUrl = String(window.location); + const currentOrigin = currentUrl.substr(0, currentUrl.length - window.location.hash.length - window.location.search.length - window.location.pathname.length); + if (workerMain.substring(0, currentOrigin.length) !== currentOrigin) { + // this is the cross-origin case + // i.e. the webpage is running at a different origin than where the scripts are loaded from + const workerBaseUrl = workerMain.substr(0, workerMain.length - 'vs/base/worker/workerMain.js'.length); + const js = `/*${label}*/self.MonacoEnvironment={baseUrl: '${workerBaseUrl}'};importScripts('${workerMain}');/*${label}*/`; + const url = `data:text/javascript;charset=utf-8,${encodeURIComponent(js)}`; + return new Worker(url); + } + } + return new Worker(workerMain + '#' + label); } // ESM-comment-end throw new Error(`You must define a function MonacoEnvironment.getWorkerUrl or MonacoEnvironment.getWorker`); } +function isPromiseLike(obj: any): obj is PromiseLike { + if (typeof obj.then === 'function') { + return true; + } + return false; +} + /** * A worker that uses HTML5 web workers so that is has * its own global scope and its own thread. @@ -31,18 +53,26 @@ function getWorker(workerId: string, label: string): Worker { class WebWorker implements IWorker { private id: number; - private worker: Worker | null; + private worker: Promise | null; constructor(moduleId: string, id: number, label: string, onMessageCallback: IWorkerCallback, onErrorCallback: (err: any) => void) { this.id = id; - this.worker = getWorker('workerMain.js', label); - this.postMessage(moduleId); - this.worker.onmessage = function (ev: any) { - onMessageCallback(ev.data); - }; - if (typeof this.worker.addEventListener === 'function') { - this.worker.addEventListener('error', onErrorCallback); + const workerOrPromise = getWorker('workerMain.js', label); + if (isPromiseLike(workerOrPromise)) { + this.worker = workerOrPromise; + } else { + this.worker = Promise.resolve(workerOrPromise); } + this.postMessage(moduleId); + this.worker.then((w) => { + w.onmessage = function (ev: any) { + onMessageCallback(ev.data); + }; + (w).onmessageerror = onErrorCallback; + if (typeof w.addEventListener === 'function') { + w.addEventListener('error', onErrorCallback); + } + }); } public getId(): number { @@ -51,13 +81,13 @@ class WebWorker implements IWorker { public postMessage(msg: string): void { if (this.worker) { - this.worker.postMessage(msg); + this.worker.then(w => w.postMessage(msg)); } } public dispose(): void { if (this.worker) { - this.worker.terminate(); + this.worker.then(w => w.terminate()); } this.worker = null; } diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index ac399cc99a2..d3d8f3fa86d 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -8,14 +8,15 @@ 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/product/node/product'; -import { parseMainProcessArgv } from 'vs/platform/environment/node/argvHelper'; +import { parseMainProcessArgv, createWaitMarkerFile } from 'vs/platform/environment/node/argvHelper'; +import { addArg } from 'vs/platform/environment/node/argv'; 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'; import { LaunchChannelClient } from 'vs/platform/launch/electron-main/launchService'; import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { InstantiationService } from 'vs/platform/instantiation/node/instantiationService'; +import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { ILogService, ConsoleLogMainService, MultiplexLogService, getLogLevel } from 'vs/platform/log/common/log'; @@ -36,7 +37,6 @@ 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 { createWaitMarkerFile } from 'vs/code/node/wait'; class ExpectedError extends Error { readonly isExpected = true; @@ -216,7 +216,7 @@ function handleStartupDataDirError(environmentService: IEnvironmentService, erro 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) + localize('startupDataDirErrorDetail', "Please make sure the directories {0} and {1} are writeable.", environmentService.userDataPath, environmentService.extensionsPath) ); } } @@ -359,7 +359,7 @@ function main(): void { if (args.wait && !args.waitMarkerFilePath) { createWaitMarkerFile(args.verbose).then(waitMarkerFilePath => { if (waitMarkerFilePath) { - process.argv.push('--waitMarkerFilePath', waitMarkerFilePath); + addArg(process.argv, '--waitMarkerFilePath', waitMarkerFilePath); args.waitMarkerFilePath = waitMarkerFilePath; } diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 4f9dfee68b6..b3a582ce9c5 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -1074,7 +1074,7 @@ export class WindowsManager implements IWindowsMainService { if (!options.forceOpenWorkspaceAsFile) { const workspace = this.workspacesMainService.resolveLocalWorkspaceSync(URI.file(candidate)); if (workspace) { - return { workspace: { id: workspace.id, configPath: workspace.configPath }, remoteAuthority }; + return { workspace: { id: workspace.id, configPath: workspace.configPath }, remoteAuthority: workspace.remoteAuthority }; } } diff --git a/src/vs/code/node/cli.ts b/src/vs/code/node/cli.ts index 12409054728..6f558debea5 100644 --- a/src/vs/code/node/cli.ts +++ b/src/vs/code/node/cli.ts @@ -5,7 +5,8 @@ import { spawn, ChildProcess } from 'child_process'; import { assign } from 'vs/base/common/objects'; -import { buildHelpMessage, buildVersionMessage } from 'vs/platform/environment/node/argv'; +import { buildHelpMessage, buildVersionMessage, addArg } from 'vs/platform/environment/node/argv'; +import { parseCLIProcessArgv, createWaitMarkerFile } from 'vs/platform/environment/node/argvHelper'; import { ParsedArgs } from 'vs/platform/environment/common/environment'; import product from 'vs/platform/product/node/product'; import pkg from 'vs/platform/product/node/package'; @@ -19,8 +20,6 @@ import * as iconv from 'iconv-lite'; import { writeFileAndFlushSync } from 'vs/base/node/extfs'; import { isWindows } from 'vs/base/common/platform'; import { ProfilingSession, Target } from 'v8-inspect-profiler'; -import { createWaitMarkerFile } from 'vs/code/node/wait'; -import { parseCLIProcessArgv } from 'vs/platform/environment/node/argvHelper'; function shouldSpawnCliProcess(argv: ParsedArgs): boolean { return !!argv['install-source'] @@ -180,11 +179,11 @@ export async function main(argv: string[]): Promise { }); // Make sure to open tmp file - argv.push(stdinFilePath); + addArg(argv, stdinFilePath); // Enable --wait to get all data and ignore adding this to history - argv.push('--wait'); - argv.push('--skip-add-to-recently-opened'); + addArg(argv, '--wait'); + addArg(argv, '--skip-add-to-recently-opened'); args.wait = true; } @@ -232,7 +231,7 @@ export async function main(argv: string[]): Promise { if (args.wait) { waitMarkerFilePath = await createWaitMarkerFile(verbose); if (waitMarkerFilePath) { - argv.push('--waitMarkerFilePath', waitMarkerFilePath); + addArg(argv, '--waitMarkerFilePath', waitMarkerFilePath); } } @@ -252,11 +251,11 @@ export async function main(argv: string[]): Promise { const filenamePrefix = paths.join(os.homedir(), 'prof-' + Math.random().toString(16).slice(-4)); - argv.push(`--inspect-brk=${portMain}`); - argv.push(`--remote-debugging-port=${portRenderer}`); - argv.push(`--inspect-brk-extensions=${portExthost}`); - argv.push(`--prof-startup-prefix`, filenamePrefix); - argv.push(`--no-cached-data`); + addArg(argv, `--inspect-brk=${portMain}`); + addArg(argv, `--remote-debugging-port=${portRenderer}`); + addArg(argv, `--inspect-brk-extensions=${portExthost}`); + addArg(argv, `--prof-startup-prefix`, filenamePrefix); + addArg(argv, `--no-cached-data`); fs.writeFileSync(filenamePrefix, argv.slice(-6).join('|')); @@ -341,7 +340,7 @@ export async function main(argv: string[]): Promise { if (args['js-flags']) { const match = /max_old_space_size=(\d+)/g.exec(args['js-flags']); if (match && !args['max-memory']) { - argv.push(`--max-memory=${match[1]}`); + addArg(argv, `--max-memory=${match[1]}`); } } diff --git a/src/vs/code/node/wait.ts b/src/vs/code/node/wait.ts deleted file mode 100644 index 9f9b9aaa41d..00000000000 --- a/src/vs/code/node/wait.ts +++ /dev/null @@ -1,26 +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 { join } from 'vs/base/common/path'; -import { tmpdir } from 'os'; -import { writeFile } from 'vs/base/node/pfs'; - -export function createWaitMarkerFile(verbose?: boolean): Promise { - const randomWaitMarkerPath = join(tmpdir(), Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 10)); - - return writeFile(randomWaitMarkerPath, '').then(() => { - if (verbose) { - console.log(`Marker file for --wait created: ${randomWaitMarkerPath}`); - } - - return randomWaitMarkerPath; - }, error => { - if (verbose) { - console.error(`Failed to create marker file for --wait: ${error}`); - } - - return Promise.resolve(undefined); - }); -} diff --git a/src/vs/code/test/node/argv.test.ts b/src/vs/code/test/node/argv.test.ts index 2988c631ee7..6ce5684fab0 100644 --- a/src/vs/code/test/node/argv.test.ts +++ b/src/vs/code/test/node/argv.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { formatOptions, Option } from 'vs/platform/environment/node/argv'; +import { formatOptions, Option, addArg } from 'vs/platform/environment/node/argv'; suite('formatOptions', () => { @@ -54,4 +54,13 @@ suite('formatOptions', () => { ' bar bar bar bar bar bar bar bar bar ' ]); }); + + test('addArg', () => { + assert.deepEqual(addArg([], 'foo'), ['foo']); + assert.deepEqual(addArg([], 'foo', 'bar'), ['foo', 'bar']); + assert.deepEqual(addArg(['foo'], 'bar'), ['foo', 'bar']); + assert.deepEqual(addArg(['--wait'], 'bar'), ['--wait', 'bar']); + assert.deepEqual(addArg(['--wait', '--', '--foo'], 'bar'), ['--wait', 'bar', '--', '--foo']); + assert.deepEqual(addArg(['--', '--foo'], 'bar'), ['bar', '--', '--foo']); + }); }); diff --git a/src/vs/editor/browser/config/configuration.ts b/src/vs/editor/browser/config/configuration.ts index f4a579bc8ce..32e87b2a794 100644 --- a/src/vs/editor/browser/config/configuration.ts +++ b/src/vs/editor/browser/config/configuration.ts @@ -54,6 +54,10 @@ class CSSBasedConfigurationCache { } } +export function clearAllFontInfos(): void { + CSSBasedConfiguration.INSTANCE.clearCache(); +} + export function readFontInfo(bareFontInfo: BareFontInfo): FontInfo { return CSSBasedConfiguration.INSTANCE.readConfiguration(bareFontInfo); } @@ -122,6 +126,11 @@ class CSSBasedConfiguration extends Disposable { super.dispose(); } + public clearCache(): void { + this._cache = new CSSBasedConfigurationCache(); + this._onDidChange.fire(); + } + private _writeToCache(item: BareFontInfo, value: FontInfo): void { this._cache.put(item, value); diff --git a/src/vs/editor/browser/controller/textAreaInput.ts b/src/vs/editor/browser/controller/textAreaInput.ts index 7ccf0e99d29..bc986faeb6f 100644 --- a/src/vs/editor/browser/controller/textAreaInput.ts +++ b/src/vs/editor/browser/controller/textAreaInput.ts @@ -10,7 +10,7 @@ import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { RunOnceScheduler } from 'vs/base/common/async'; import { Emitter, Event } from 'vs/base/common/event'; import { KeyCode } from 'vs/base/common/keyCodes'; -import { Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import * as platform from 'vs/base/common/platform'; import * as strings from 'vs/base/common/strings'; import { ITextAreaWrapper, ITypeData, TextAreaState } from 'vs/editor/browser/controller/textAreaState'; @@ -105,6 +105,7 @@ export class TextAreaInput extends Disposable { private readonly _asyncTriggerCut: RunOnceScheduler; private _textAreaState: TextAreaState; + private _selectionChangeListener: IDisposable | null; private _hasFocus: boolean; private _isDoingComposition: boolean; @@ -330,8 +331,9 @@ export class TextAreaInput extends Disposable { this._lastTextAreaEvent = TextAreaInputEventType.blur; this._setHasFocus(false); })); + } - + private _installSelectionChangeListener(): IDisposable { // See https://github.com/Microsoft/vscode/issues/27216 // When using a Braille display, it is possible for users to reposition the // system caret. This is reflected in Chrome as a `selectionchange` event. @@ -351,7 +353,7 @@ export class TextAreaInput extends Disposable { // `selectionchange` events often come multiple times for a single logical change // so throttle multiple `selectionchange` events that burst in a short period of time. let previousSelectionChangeEventTime = 0; - this._register(dom.addDisposableListener(document, 'selectionchange', (e) => { + return dom.addDisposableListener(document, 'selectionchange', (e) => { if (!this._hasFocus) { return; } @@ -411,11 +413,15 @@ export class TextAreaInput extends Disposable { ); this._onSelectionChangeRequest.fire(newSelection); - })); + }); } public dispose(): void { super.dispose(); + if (this._selectionChangeListener) { + this._selectionChangeListener.dispose(); + this._selectionChangeListener = null; + } } public focusTextArea(): void { @@ -435,6 +441,14 @@ export class TextAreaInput extends Disposable { } this._hasFocus = newHasFocus; + if (this._selectionChangeListener) { + this._selectionChangeListener.dispose(); + this._selectionChangeListener = null; + } + if (this._hasFocus) { + this._selectionChangeListener = this._installSelectionChangeListener(); + } + if (this._hasFocus) { if (browser.isEdge) { // Edge has a bug where setting the selection range while the focus event diff --git a/src/vs/editor/common/config/commonEditorConfig.ts b/src/vs/editor/common/config/commonEditorConfig.ts index 0146d8c3148..fa004fba308 100644 --- a/src/vs/editor/common/config/commonEditorConfig.ts +++ b/src/vs/editor/common/config/commonEditorConfig.ts @@ -289,20 +289,20 @@ const editorConfiguration: IConfigurationNode = { 'minimum': 1, 'markdownDescription': nls.localize('tabSize', "The number of spaces a tab is equal to. This setting is overridden based on the file contents when `#editor.detectIndentation#` is on.") }, - 'editor.indentSize': { - 'anyOf': [ - { - 'type': 'string', - 'enum': ['tabSize'] - }, - { - 'type': 'number', - 'minimum': 1 - } - ], - 'default': 'tabSize', - 'markdownDescription': nls.localize('indentSize', "The number of spaces used for indentation or 'tabSize' to use the value from `#editor.tabSize#`. This setting is overridden based on the file contents when `#editor.detectIndentation#` is on.") - }, + // 'editor.indentSize': { + // 'anyOf': [ + // { + // 'type': 'string', + // 'enum': ['tabSize'] + // }, + // { + // 'type': 'number', + // 'minimum': 1 + // } + // ], + // 'default': 'tabSize', + // 'markdownDescription': nls.localize('indentSize', "The number of spaces used for indentation or 'tabSize' to use the value from `#editor.tabSize#`. This setting is overridden based on the file contents when `#editor.detectIndentation#` is on.") + // }, 'editor.insertSpaces': { 'type': 'boolean', 'default': EDITOR_MODEL_DEFAULTS.insertSpaces, diff --git a/src/vs/editor/common/controller/cursor.ts b/src/vs/editor/common/controller/cursor.ts index 15ccc7a4e1f..81493777b1c 100644 --- a/src/vs/editor/common/controller/cursor.ts +++ b/src/vs/editor/common/controller/cursor.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from 'vs/nls'; import { onUnexpectedError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; import * as strings from 'vs/base/common/strings'; @@ -828,7 +827,8 @@ class CommandExecutor { try { command.getEditOperations(ctx.model, editOperationBuilder); } catch (e) { - e.friendlyMessage = nls.localize('corrupt.commands', "Unexpected exception while executing command."); + // TODO@Alex use notification service if this should be user facing + // e.friendlyMessage = nls.localize('corrupt.commands', "Unexpected exception while executing command."); onUnexpectedError(e); return { operations: [], diff --git a/src/vs/editor/common/controller/cursorTypeOperations.ts b/src/vs/editor/common/controller/cursorTypeOperations.ts index 032d035e41e..c18d081379d 100644 --- a/src/vs/editor/common/controller/cursorTypeOperations.ts +++ b/src/vs/editor/common/controller/cursorTypeOperations.ts @@ -526,7 +526,7 @@ export class TypeOperations { const lineText = model.getLineContent(position.lineNumber); // Do not auto-close ' or " after a word character - if (chIsQuote && position.column > 1) { + if ((chIsQuote && position.column > 1) && autoCloseConfig !== 'always') { const wordSeparators = getMapForWordSeparators(config.wordSeparators); const characterBeforeCode = lineText.charCodeAt(position.column - 2); const characterBeforeType = wordSeparators.get(characterBeforeCode); diff --git a/src/vs/editor/common/core/editOperation.ts b/src/vs/editor/common/core/editOperation.ts index fe3cf2e6084..9029c7a3cf4 100644 --- a/src/vs/editor/common/core/editOperation.ts +++ b/src/vs/editor/common/core/editOperation.ts @@ -24,14 +24,14 @@ export class EditOperation { }; } - public static replace(range: Range, text: string): IIdentifiedSingleEditOperation { + public static replace(range: Range, text: string | null): IIdentifiedSingleEditOperation { return { range: range, text: text }; } - public static replaceMove(range: Range, text: string): IIdentifiedSingleEditOperation { + public static replaceMove(range: Range, text: string | null): IIdentifiedSingleEditOperation { return { range: range, text: text, diff --git a/src/vs/editor/common/editorCommon.ts b/src/vs/editor/common/editorCommon.ts index e969ff60627..bde26ab9802 100644 --- a/src/vs/editor/common/editorCommon.ts +++ b/src/vs/editor/common/editorCommon.ts @@ -469,7 +469,7 @@ export interface IDiffEditor extends IEditor { /** * Type the getModel() of IEditor. */ - getModel(): IDiffEditorModel; + getModel(): IDiffEditorModel | null; /** * Get the `original` editor. diff --git a/src/vs/editor/common/model.ts b/src/vs/editor/common/model.ts index fa9125ff8f8..48a268b0bd6 100644 --- a/src/vs/editor/common/model.ts +++ b/src/vs/editor/common/model.ts @@ -12,7 +12,7 @@ import { IRange, Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import { IModelContentChange, IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguageChangedEvent, IModelLanguageConfigurationChangedEvent, IModelOptionsChangedEvent, IModelTokensChangedEvent, ModelRawContentChangedEvent } from 'vs/editor/common/model/textModelEvents'; import { SearchData } from 'vs/editor/common/model/textModelSearch'; -import { LanguageId, LanguageIdentifier } from 'vs/editor/common/modes'; +import { LanguageId, LanguageIdentifier, FormattingOptions } from 'vs/editor/common/modes'; import { ITextSnapshot } from 'vs/platform/files/common/files'; import { ThemeColor } from 'vs/platform/theme/common/themeService'; @@ -289,7 +289,7 @@ export interface ISingleEditOperation { /** * The text to replace with. This can be null to emulate a simple delete. */ - text: string; + text: string | null; /** * This indicates that this operation has "insert" semantics. * i.e. forceMoveMarkers = true => if `range` is collapsed, all markers at the position will be moved. @@ -362,7 +362,7 @@ export class TextModelResolvedOptions { trimAutoWhitespace: boolean; }) { this.tabSize = src.tabSize | 0; - this.indentSize = src.indentSize | 0; + this.indentSize = src.tabSize | 0; this.insertSpaces = Boolean(src.insertSpaces); this.defaultEOL = src.defaultEOL | 0; this.trimAutoWhitespace = Boolean(src.trimAutoWhitespace); @@ -500,6 +500,12 @@ export interface ITextModel { */ getOptions(): TextModelResolvedOptions; + /** + * Get the formatting options for this model. + * @internal + */ + getFormattingOptions(): FormattingOptions; + /** * Get the current version id of the model. * Anytime a change happens to the model (even undo/redo), diff --git a/src/vs/editor/common/model/editStack.ts b/src/vs/editor/common/model/editStack.ts index 39ecc249c01..b60b083c1b6 100644 --- a/src/vs/editor/common/model/editStack.ts +++ b/src/vs/editor/common/model/editStack.ts @@ -210,7 +210,7 @@ export class EditStack { } public canUndo(): boolean { - return (this.past.length > 0); + return (this.past.length > 0) || this.currentOpenStackElement !== null; } public redo(): IUndoRedoResult | null { diff --git a/src/vs/editor/common/model/textModel.ts b/src/vs/editor/common/model/textModel.ts index 7f9d8351b14..1b67ebef2c8 100644 --- a/src/vs/editor/common/model/textModel.ts +++ b/src/vs/editor/common/model/textModel.ts @@ -25,7 +25,7 @@ import { IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguag import { SearchData, SearchParams, TextModelSearch } from 'vs/editor/common/model/textModelSearch'; import { ModelLinesTokens, ModelTokensChangedEventBuilder } from 'vs/editor/common/model/textModelTokens'; import { getWordAtText } from 'vs/editor/common/model/wordHelper'; -import { IState, LanguageId, LanguageIdentifier, TokenizationRegistry } from 'vs/editor/common/modes'; +import { IState, LanguageId, LanguageIdentifier, TokenizationRegistry, FormattingOptions } from 'vs/editor/common/modes'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { NULL_LANGUAGE_IDENTIFIER } from 'vs/editor/common/modes/nullMode'; import { ignoreBracketsInToken } from 'vs/editor/common/modes/supports'; @@ -590,6 +590,13 @@ export class TextModel extends Disposable implements model.ITextModel { return this._options; } + public getFormattingOptions(): FormattingOptions { + return { + tabSize: this._options.indentSize, + insertSpaces: this._options.insertSpaces + }; + } + public updateOptions(_newOpts: model.ITextModelUpdateOptions): void { this._assertNotDisposed(); let tabSize = (typeof _newOpts.tabSize !== 'undefined') ? _newOpts.tabSize : this._options.tabSize; diff --git a/src/vs/editor/common/modes/supports/tokenization.ts b/src/vs/editor/common/modes/supports/tokenization.ts index e58c791f60b..d245a41fb89 100644 --- a/src/vs/editor/common/modes/supports/tokenization.ts +++ b/src/vs/editor/common/modes/supports/tokenization.ts @@ -240,7 +240,7 @@ export class TokenTheme { } } -const STANDARD_TOKEN_TYPE_REGEXP = /\b(comment|string|regex)\b/; +const STANDARD_TOKEN_TYPE_REGEXP = /\b(comment|string|regex|regexp)\b/; export function toStandardTokenType(tokenType: string): StandardTokenType { let m = tokenType.match(STANDARD_TOKEN_TYPE_REGEXP); if (!m) { @@ -253,6 +253,8 @@ export function toStandardTokenType(tokenType: string): StandardTokenType { return StandardTokenType.String; case 'regex': return StandardTokenType.RegEx; + case 'regexp': + return StandardTokenType.RegEx; } throw new Error('Unexpected match for standard token type!'); } diff --git a/src/vs/editor/common/services/editorSimpleWorker.ts b/src/vs/editor/common/services/editorSimpleWorker.ts index 4ef83f744a7..f5564b6f567 100644 --- a/src/vs/editor/common/services/editorSimpleWorker.ts +++ b/src/vs/editor/common/services/editorSimpleWorker.ts @@ -491,12 +491,15 @@ export abstract class BaseEditorSimpleWorker { return Promise.resolve(null); } + const seen: Record = Object.create(null); const suggestions: CompletionItem[] = []; const wordDefRegExp = new RegExp(wordDef, wordDefFlags); - const currentWord = model.getWordUntilPosition(position, wordDefRegExp); + const wordUntil = model.getWordUntilPosition(position, wordDefRegExp); - const seen: Record = Object.create(null); - seen[currentWord.word] = true; + const wordAt = model.getWordAtPosition(position, wordDefRegExp); + if (wordAt) { + seen[model.getValueInRange(wordAt)] = true; + } for ( let iter = model.createWordIterator(wordDefRegExp), e = iter.next(); @@ -516,10 +519,9 @@ export abstract class BaseEditorSimpleWorker { kind: CompletionItemKind.Text, label: word, insertText: word, - range: { startLineNumber: position.lineNumber, startColumn: currentWord.startColumn, endLineNumber: position.lineNumber, endColumn: currentWord.endColumn } + range: { startLineNumber: position.lineNumber, startColumn: wordUntil.startColumn, endLineNumber: position.lineNumber, endColumn: wordUntil.endColumn } }); } - return Promise.resolve({ suggestions }); } diff --git a/src/vs/editor/common/services/getIconClasses.ts b/src/vs/editor/common/services/getIconClasses.ts index d8f7d5bf122..ca49de2528f 100644 --- a/src/vs/editor/common/services/getIconClasses.ts +++ b/src/vs/editor/common/services/getIconClasses.ts @@ -12,9 +12,11 @@ import { IModelService } from 'vs/editor/common/services/modelService'; import { FileKind } from 'vs/platform/files/common/files'; export function getIconClasses(modelService: IModelService, modeService: IModeService, resource: uri | undefined, fileKind?: FileKind): string[] { + // we always set these base classes even if we do not have a path const classes = fileKind === FileKind.ROOT_FOLDER ? ['rootfolder-icon'] : fileKind === FileKind.FOLDER ? ['folder-icon'] : ['file-icon']; if (resource) { + // Get the path and name of the resource. For data-URIs, we need to parse specially let name: string | undefined; let path: string | undefined; @@ -22,17 +24,19 @@ export function getIconClasses(modelService: IModelService, modeService: IModeSe const metadata = DataUri.parseMetaData(resource); name = metadata.get(DataUri.META_DATA_LABEL); path = name; - } - else { + } else { name = cssEscape(basenameOrAuthority(resource).toLowerCase()); path = resource.path.toLowerCase(); } + // Folders if (fileKind === FileKind.FOLDER) { classes.push(`${name}-name-folder-icon`); } + // Files else { + // Name & Extension(s) if (name) { classes.push(`${name}-name-file-icon`); @@ -42,8 +46,9 @@ export function getIconClasses(modelService: IModelService, modeService: IModeSe } classes.push(`ext-file-icon`); // extra segment to increase file-ext score } + // Configured Language - let configuredLangId: string | null = getConfiguredLangId(modelService, resource); + let configuredLangId: string | null = getConfiguredLangId(modelService, modeService, resource); configuredLangId = configuredLangId || (path ? modeService.getModeIdByFilepathOrFirstLine(path) : null); if (configuredLangId) { classes.push(`${cssEscape(configuredLangId)}-lang-file-icon`); @@ -53,16 +58,32 @@ export function getIconClasses(modelService: IModelService, modeService: IModeSe return classes; } -export function getConfiguredLangId(modelService: IModelService, resource: uri): string | null { +export function getConfiguredLangId(modelService: IModelService, modeService: IModeService, resource: uri): string | null { let configuredLangId: string | null = null; if (resource) { - const model = modelService.getModel(resource); - if (model) { - const modeId = model.getLanguageIdentifier().language; - if (modeId && modeId !== PLAINTEXT_MODE_ID) { - configuredLangId = modeId; // only take if the mode is specific (aka no just plain text) + let modeId: string | null = null; + + // Data URI: check for encoded metadata + if (resource.scheme === Schemas.data) { + const metadata = DataUri.parseMetaData(resource); + const mime = metadata.get(DataUri.META_DATA_MIME); + + if (mime) { + modeId = modeService.getModeId(mime); } } + + // Any other URI: check for model if existing + else { + const model = modelService.getModel(resource); + if (model) { + modeId = model.getLanguageIdentifier().language; + } + } + + if (modeId && modeId !== PLAINTEXT_MODE_ID) { + configuredLangId = modeId; // only take if the mode is specific (aka no just plain text) + } } return configuredLangId; diff --git a/src/vs/editor/common/services/languagesRegistry.ts b/src/vs/editor/common/services/languagesRegistry.ts index e5f39e13df3..5534b442510 100644 --- a/src/vs/editor/common/services/languagesRegistry.ts +++ b/src/vs/editor/common/services/languagesRegistry.ts @@ -270,7 +270,7 @@ export class LanguagesRegistry extends Disposable { return (language.mimetypes[0] || null); } - public extractModeIds(commaSeparatedMimetypesOrCommaSeparatedIds: string): string[] { + public extractModeIds(commaSeparatedMimetypesOrCommaSeparatedIds: string | undefined): string[] { if (!commaSeparatedMimetypesOrCommaSeparatedIds) { return []; } diff --git a/src/vs/editor/common/services/markerDecorationsServiceImpl.ts b/src/vs/editor/common/services/markerDecorationsServiceImpl.ts index ecf4af882d6..70748519a41 100644 --- a/src/vs/editor/common/services/markerDecorationsServiceImpl.ts +++ b/src/vs/editor/common/services/markerDecorationsServiceImpl.ts @@ -15,6 +15,7 @@ import { Range } from 'vs/editor/common/core/range'; import { keys } from 'vs/base/common/map'; import { IMarkerDecorationsService } from 'vs/editor/common/services/markersDecorationService'; import { Schemas } from 'vs/base/common/network'; +import { Emitter, Event } from 'vs/base/common/event'; function MODEL_ID(resource: URI): string { return resource.toString(); @@ -44,12 +45,26 @@ class MarkerDecorations extends Disposable { getMarker(decoration: IModelDecoration): IMarker | undefined { return this._markersData.get(decoration.id); } + + getMarkers(): [Range, IMarker][] { + const res: [Range, IMarker][] = []; + this._markersData.forEach((marker, id) => { + let range = this.model.getDecorationRange(id); + if (range) { + res.push([range, marker]); + } + }); + return res; + } } export class MarkerDecorationsService extends Disposable implements IMarkerDecorationsService { _serviceBrand: any; + private readonly _onDidChangeMarker = new Emitter(); + readonly onDidChangeMarker: Event = this._onDidChangeMarker.event; + private readonly _markerDecorations: Map = new Map(); constructor( @@ -68,11 +83,16 @@ export class MarkerDecorationsService extends Disposable implements IMarkerDecor return markerDecorations ? markerDecorations.getMarker(decoration) || null : null; } + getLiveMarkers(model: ITextModel): [Range, IMarker][] { + const markerDecorations = this._markerDecorations.get(MODEL_ID(model.uri)); + return markerDecorations ? markerDecorations.getMarkers() : []; + } + private _handleMarkerChange(changedResources: URI[]): void { changedResources.forEach((resource) => { const markerDecorations = this._markerDecorations.get(MODEL_ID(resource)); if (markerDecorations) { - this.updateDecorations(markerDecorations); + this._updateDecorations(markerDecorations); } }); } @@ -80,7 +100,7 @@ export class MarkerDecorationsService extends Disposable implements IMarkerDecor private _onModelAdded(model: ITextModel): void { const markerDecorations = new MarkerDecorations(model); this._markerDecorations.set(MODEL_ID(model.uri), markerDecorations); - this.updateDecorations(markerDecorations); + this._updateDecorations(markerDecorations); } private _onModelRemoved(model: ITextModel): void { @@ -100,7 +120,7 @@ export class MarkerDecorationsService extends Disposable implements IMarkerDecor } } - private updateDecorations(markerDecorations: MarkerDecorations): void { + private _updateDecorations(markerDecorations: MarkerDecorations): void { // Limit to the first 500 errors/warnings const markers = this._markerService.read({ resource: markerDecorations.model.uri, take: 500 }); let newModelDecorations: IModelDeltaDecoration[] = markers.map((marker) => { @@ -110,6 +130,7 @@ export class MarkerDecorationsService extends Disposable implements IMarkerDecor }; }); markerDecorations.update(markers, newModelDecorations); + this._onDidChangeMarker.fire(markerDecorations.model); } private _createDecorationRange(model: ITextModel, rawMarker: IMarker): Range { diff --git a/src/vs/editor/common/services/markersDecorationService.ts b/src/vs/editor/common/services/markersDecorationService.ts index b2424a4d312..44aa7ca2463 100644 --- a/src/vs/editor/common/services/markersDecorationService.ts +++ b/src/vs/editor/common/services/markersDecorationService.ts @@ -6,11 +6,17 @@ import { ITextModel, IModelDecoration } from 'vs/editor/common/model'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IMarker } from 'vs/platform/markers/common/markers'; +import { Event } from 'vs/base/common/event'; +import { Range } from 'vs/editor/common/core/range'; export const IMarkerDecorationsService = createDecorator('markerDecorationsService'); export interface IMarkerDecorationsService { _serviceBrand: any; + onDidChangeMarker: Event; + getMarker(model: ITextModel, decoration: IModelDecoration): IMarker | null; -} \ No newline at end of file + + getLiveMarkers(model: ITextModel): [Range, IMarker][]; +} diff --git a/src/vs/editor/common/services/modeService.ts b/src/vs/editor/common/services/modeService.ts index bf6a6e51a06..de852f670f7 100644 --- a/src/vs/editor/common/services/modeService.ts +++ b/src/vs/editor/common/services/modeService.ts @@ -47,7 +47,7 @@ export interface IModeService { getConfigurationFiles(modeId: string): URI[]; // --- instantiation - create(commaSeparatedMimetypesOrCommaSeparatedIds: string): ILanguageSelection; + create(commaSeparatedMimetypesOrCommaSeparatedIds: string | undefined): ILanguageSelection; createByLanguageName(languageName: string): ILanguageSelection; createByFilepathOrFirstLine(filepath: string | null, firstLine?: string): ILanguageSelection; diff --git a/src/vs/editor/common/services/modeServiceImpl.ts b/src/vs/editor/common/services/modeServiceImpl.ts index db3cd258a1f..9b4667220e8 100644 --- a/src/vs/editor/common/services/modeServiceImpl.ts +++ b/src/vs/editor/common/services/modeServiceImpl.ts @@ -104,7 +104,7 @@ export class ModeServiceImpl implements IModeService { return null; } - public getModeId(commaSeparatedMimetypesOrCommaSeparatedIds: string): string | null { + public getModeId(commaSeparatedMimetypesOrCommaSeparatedIds: string | undefined): string | null { const modeIds = this._registry.extractModeIds(commaSeparatedMimetypesOrCommaSeparatedIds); if (modeIds.length > 0) { @@ -124,7 +124,7 @@ export class ModeServiceImpl implements IModeService { // --- instantiation - public create(commaSeparatedMimetypesOrCommaSeparatedIds: string): ILanguageSelection { + public create(commaSeparatedMimetypesOrCommaSeparatedIds: string | undefined): ILanguageSelection { return new LanguageSelection(this.onLanguagesMaybeChanged, () => { const modeId = this.getModeId(commaSeparatedMimetypesOrCommaSeparatedIds); return this._createModeAndGetLanguageIdentifier(modeId); diff --git a/src/vs/editor/common/services/resolverService.ts b/src/vs/editor/common/services/resolverService.ts index e000629c298..d87baf65a43 100644 --- a/src/vs/editor/common/services/resolverService.ts +++ b/src/vs/editor/common/services/resolverService.ts @@ -18,7 +18,7 @@ export interface ITextModelService { * Provided a resource URI, it will return a model reference * which should be disposed once not needed anymore. */ - createModelReference(resource: URI): Promise>; + createModelReference(resource: URI): Promise>; /** * Registers a specific `scheme` content provider. @@ -36,7 +36,7 @@ export interface ITextModelContentProvider { /** * Given a resource, return the content of the resource as `ITextModel`. */ - provideTextContent(resource: URI): Promise | null; + provideTextContent(resource: URI): Promise | null | undefined; } export interface ITextEditorModel extends IEditorModel { @@ -44,7 +44,15 @@ export interface ITextEditorModel extends IEditorModel { /** * Provides access to the underlying `ITextModel`. */ - readonly textEditorModel: ITextModel; + readonly textEditorModel: ITextModel | null; isReadonly(): boolean; } + +export interface IResolvedTextEditorModel extends ITextEditorModel { + + /** + * Same as ITextEditorModel#textEditorModel, but never null. + */ + readonly textEditorModel: ITextModel; +} diff --git a/src/vs/editor/common/services/resourceConfiguration.ts b/src/vs/editor/common/services/resourceConfiguration.ts index e0926ee9fda..02e63369296 100644 --- a/src/vs/editor/common/services/resourceConfiguration.ts +++ b/src/vs/editor/common/services/resourceConfiguration.ts @@ -24,13 +24,13 @@ export interface ITextResourceConfigurationService { * Fetches the value of the section for the given resource by applying language overrides. * Value can be of native type or an object keyed off the section name. * - * @param resource - Resource for which the configuration has to be fetched. Can be `null` or `undefined`. - * @param postion - Position in the resource for which configuration has to be fetched. Can be `null` or `undefined`. - * @param section - Section of the configuraion. Can be `null` or `undefined`. + * @param resource - Resource for which the configuration has to be fetched. + * @param postion - Position in the resource for which configuration has to be fetched. + * @param section - Section of the configuraion. * */ - getValue(resource: URI, section?: string): T; - getValue(resource: URI, position?: IPosition, section?: string): T; + getValue(resource: URI | undefined, section?: string): T; + getValue(resource: URI | undefined, position?: IPosition, section?: string): T; } diff --git a/src/vs/editor/contrib/codeAction/codeActionWidget.ts b/src/vs/editor/contrib/codeAction/codeActionWidget.ts index 3522fc39320..9e543cb08ac 100644 --- a/src/vs/editor/contrib/codeAction/codeActionWidget.ts +++ b/src/vs/editor/contrib/codeAction/codeActionWidget.ts @@ -32,6 +32,7 @@ export class CodeActionContextMenu { // cancel when editor went off-dom return Promise.reject(canceled()); } + this._visible = true; const actions = codeActions.map(action => this.codeActionToAction(action)); this._contextMenuService.showContextMenu({ getAnchor: () => { @@ -51,7 +52,7 @@ export class CodeActionContextMenu { private codeActionToAction(action: CodeAction): Action { const id = action.command ? action.command.id : action.title; - const title = action.isPreferred ? `${action.title} ★` : action.title; + const title = action.title; return new Action(id, title, undefined, true, () => this._onApplyCodeAction(action) .finally(() => this._onDidExecuteCodeAction.fire(undefined))); diff --git a/src/vs/editor/contrib/dnd/dnd.ts b/src/vs/editor/contrib/dnd/dnd.ts index caa3ea75fd4..a716d8fa10d 100644 --- a/src/vs/editor/contrib/dnd/dnd.ts +++ b/src/vs/editor/contrib/dnd/dnd.ts @@ -37,7 +37,7 @@ export class DragAndDropController implements editorCommon.IEditorContribution { private _dragSelection: Selection | null; private _dndDecorationIds: string[]; private _mouseDown: boolean; - private _modiferPressed: boolean; + private _modifierPressed: boolean; static TRIGGER_KEY_VALUE = isMacintosh ? KeyCode.Alt : KeyCode.Ctrl; static get(editor: ICodeEditor): DragAndDropController { @@ -56,7 +56,7 @@ export class DragAndDropController implements editorCommon.IEditorContribution { this._toUnhook.push(this._editor.onDidBlurEditorWidget(() => this.onEditorBlur())); this._dndDecorationIds = []; this._mouseDown = false; - this._modiferPressed = false; + this._modifierPressed = false; this._dragSelection = null; } @@ -64,7 +64,7 @@ export class DragAndDropController implements editorCommon.IEditorContribution { this._removeDecoration(); this._dragSelection = null; this._mouseDown = false; - this._modiferPressed = false; + this._modifierPressed = false; } private onEditorKeyDown(e: IKeyboardEvent): void { @@ -73,7 +73,7 @@ export class DragAndDropController implements editorCommon.IEditorContribution { } if (hasTriggerModifier(e)) { - this._modiferPressed = true; + this._modifierPressed = true; } if (this._mouseDown && hasTriggerModifier(e)) { @@ -89,7 +89,7 @@ export class DragAndDropController implements editorCommon.IEditorContribution { } if (hasTriggerModifier(e)) { - this._modiferPressed = false; + this._modifierPressed = false; } if (this._mouseDown && e.keyCode === DragAndDropController.TRIGGER_KEY_VALUE) { @@ -170,13 +170,13 @@ export class DragAndDropController implements editorCommon.IEditorContribution { ( ( hasTriggerModifier(mouseEvent.event) || - this._modiferPressed + this._modifierPressed ) && ( this._dragSelection.getEndPosition().equals(newCursorPosition) || this._dragSelection.getStartPosition().equals(newCursorPosition) ) // we allow users to paste content beside the selection )) { this._editor.pushUndoStop(); - this._editor.executeCommand(DragAndDropController.ID, new DragAndDropCommand(this._dragSelection, newCursorPosition, hasTriggerModifier(mouseEvent.event) || this._modiferPressed)); + this._editor.executeCommand(DragAndDropController.ID, new DragAndDropCommand(this._dragSelection, newCursorPosition, hasTriggerModifier(mouseEvent.event) || this._modifierPressed)); this._editor.pushUndoStop(); } } @@ -227,7 +227,7 @@ export class DragAndDropController implements editorCommon.IEditorContribution { this._removeDecoration(); this._dragSelection = null; this._mouseDown = false; - this._modiferPressed = false; + this._modifierPressed = false; this._toUnhook = dispose(this._toUnhook); } } diff --git a/src/vs/editor/contrib/documentSymbols/outlineModel.ts b/src/vs/editor/contrib/documentSymbols/outlineModel.ts index 0c231026ede..61991f8a654 100644 --- a/src/vs/editor/contrib/documentSymbols/outlineModel.ts +++ b/src/vs/editor/contrib/documentSymbols/outlineModel.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { binarySearch, coalesceInPlace } from 'vs/base/common/arrays'; +import { binarySearch, coalesceInPlace, equals } from 'vs/base/common/arrays'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { first, forEach, size } from 'vs/base/common/collections'; import { onUnexpectedExternalError } from 'vs/base/common/errors'; @@ -13,7 +13,7 @@ import { IPosition } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; import { ITextModel } from 'vs/editor/common/model'; import { DocumentSymbol, DocumentSymbolProvider, DocumentSymbolProviderRegistry } from 'vs/editor/common/modes'; -import { IMarker, MarkerSeverity } from 'vs/platform/markers/common/markers'; +import { MarkerSeverity } from 'vs/platform/markers/common/markers'; export abstract class TreeElement { @@ -86,6 +86,14 @@ export abstract class TreeElement { } } +export interface IOutlineMarker { + startLineNumber: number; + startColumn: number; + endLineNumber: number; + endColumn: number; + severity: MarkerSeverity; +} + export class OutlineElement extends TreeElement { children: { [id: string]: OutlineElement; } = Object.create(null); @@ -140,13 +148,13 @@ export class OutlineGroup extends TreeElement { return undefined; } - updateMarker(marker: IMarker[]): void { + updateMarker(marker: IOutlineMarker[]): void { for (const key in this.children) { this._updateMarker(marker, this.children[key]); } } - private _updateMarker(markers: IMarker[], item: OutlineElement): void { + private _updateMarker(markers: IOutlineMarker[], item: OutlineElement): void { item.marker = undefined; // find the proper start index to check for item/marker overlap. @@ -161,7 +169,7 @@ export class OutlineGroup extends TreeElement { start = idx; } - let myMarkers: IMarker[] = []; + let myMarkers: IOutlineMarker[] = []; let myTopSev: MarkerSeverity | undefined; for (; start < markers.length && Range.areIntersecting(item.symbol.range, markers[start]); start++) { @@ -169,7 +177,7 @@ export class OutlineGroup extends TreeElement { // and store them in a 'private' array. let marker = markers[start]; myMarkers.push(marker); - (markers as Array)[start] = undefined; + (markers as Array)[start] = undefined; if (!myTopSev || marker.severity > myTopSev) { myTopSev = marker.severity; } @@ -264,19 +272,19 @@ export class OutlineModel extends TreeElement { }); } - private static _create(textModel: ITextModel, token: CancellationToken): Promise { + static _create(textModel: ITextModel, token: CancellationToken): Promise { - let chainedToken = new CancellationTokenSource(); - let listener = DocumentSymbolProviderRegistry.onDidChange(() => chainedToken.cancel()); - token.onCancellationRequested(() => chainedToken.cancel()); + const chainedCancellation = new CancellationTokenSource(); + token.onCancellationRequested(() => chainedCancellation.cancel()); - let result = new OutlineModel(textModel); - let promises = DocumentSymbolProviderRegistry.ordered(textModel).map((provider, index) => { + const result = new OutlineModel(textModel); + const provider = DocumentSymbolProviderRegistry.ordered(textModel); + const promises = provider.map((provider, index) => { let id = TreeElement.findId(`provider_${index}`, result); let group = new OutlineGroup(id, result, provider, index); - return Promise.resolve(provider.provideDocumentSymbols(result.textModel, chainedToken.token)).then(result => { + return Promise.resolve(provider.provideDocumentSymbols(result.textModel, chainedCancellation.token)).then(result => { for (const info of result || []) { OutlineModel._makeOutlineElement(info, group); } @@ -293,9 +301,22 @@ export class OutlineModel extends TreeElement { }); }); - return Promise.all(promises) - .then(() => result._compact()) - .finally(() => listener.dispose()); + const listener = DocumentSymbolProviderRegistry.onDidChange(() => { + const newProvider = DocumentSymbolProviderRegistry.ordered(textModel); + if (!equals(newProvider, provider)) { + chainedCancellation.cancel(); + } + }); + + return Promise.all(promises).then(() => { + if (chainedCancellation.token.isCancellationRequested && !token.isCancellationRequested) { + return OutlineModel._create(textModel, token); + } else { + return result._compact(); + } + }).finally(() => { + listener.dispose(); + }); } private static _makeOutlineElement(info: DocumentSymbol, container: OutlineGroup | OutlineElement): void { @@ -400,7 +421,7 @@ export class OutlineModel extends TreeElement { return TreeElement.getElementById(id, this); } - updateMarker(marker: IMarker[]): void { + updateMarker(marker: IOutlineMarker[]): void { // sort markers by start range so that we can use // outline element starts for quicker look up marker.sort(Range.compareRangesUsingStarts); diff --git a/src/vs/editor/contrib/documentSymbols/outlineTree.ts b/src/vs/editor/contrib/documentSymbols/outlineTree.ts index a15286a4a94..926d6a3da2f 100644 --- a/src/vs/editor/contrib/documentSymbols/outlineTree.ts +++ b/src/vs/editor/contrib/documentSymbols/outlineTree.ts @@ -122,6 +122,7 @@ export class OutlineElementRenderer implements ITreeRenderer[], title: localize('title.template', "{0} ({1})", element.symbol.name, OutlineElementRenderer._symbolKindNames[element.symbol.kind]) }; diff --git a/src/vs/editor/contrib/documentSymbols/test/outlineModel.test.ts b/src/vs/editor/contrib/documentSymbols/test/outlineModel.test.ts index 229c065da87..b15a1451072 100644 --- a/src/vs/editor/contrib/documentSymbols/test/outlineModel.test.ts +++ b/src/vs/editor/contrib/documentSymbols/test/outlineModel.test.ts @@ -46,7 +46,7 @@ suite('OutlineModel', function () { let isCancelled = false; let reg = DocumentSymbolProviderRegistry.register({ pattern: '**/path.foo' }, { - provideDocumentSymbols(_d, token) { + provideDocumentSymbols(d, token) { return new Promise(resolve => { token.onCancellationRequested(_ => { isCancelled = true; @@ -183,28 +183,4 @@ suite('OutlineModel', function () { assert.equal(model.children['g2']!.children['c2'].children['c2.2'].marker!.count, 1); }); - test('"Cannot read property \'adapter\' of undefined" #69147', async function () { - - let model = TextModel.createFromString('foo', undefined, undefined, URI.file('/fome/path.foo')); - let reg1 = DocumentSymbolProviderRegistry.register({ pattern: '**/path.foo' }, { - provideDocumentSymbols(_model, token) { - assert.equal(token.isCancellationRequested, true); - return []; - } - }); - let reg2 = DocumentSymbolProviderRegistry.register({ pattern: '**/path.foo' }, { - provideDocumentSymbols(_model, token) { - assert.equal(token.isCancellationRequested, false); - reg1.dispose(); - assert.equal(token.isCancellationRequested, true); - return []; - } - }); - - await OutlineModel.create(model, CancellationToken.None); - - reg1.dispose(); - reg2.dispose(); - }); - }); diff --git a/src/vs/editor/contrib/find/simpleFindWidget.ts b/src/vs/editor/contrib/find/simpleFindWidget.ts index d6b551ae95b..3334c2fa6bf 100644 --- a/src/vs/editor/contrib/find/simpleFindWidget.ts +++ b/src/vs/editor/contrib/find/simpleFindWidget.ts @@ -26,7 +26,7 @@ const NLS_CLOSE_BTN_LABEL = nls.localize('label.closeButton', "Close"); export abstract class SimpleFindWidget extends Widget { private _findInput: FindInput; - private _domNode?: HTMLElement; + private _domNode: HTMLElement; private _innerDomNode: HTMLElement; private _isVisible: boolean = false; private _focusTracker: dom.IFocusTracker; @@ -182,7 +182,6 @@ export abstract class SimpleFindWidget extends Widget { if (this._domNode && this._domNode.parentElement) { this._domNode.parentElement.removeChild(this._domNode); - this._domNode = undefined; } } diff --git a/src/vs/editor/contrib/find/test/findController.test.ts b/src/vs/editor/contrib/find/test/findController.test.ts index 41b06546a59..2e8e1c92f5d 100644 --- a/src/vs/editor/contrib/find/test/findController.test.ts +++ b/src/vs/editor/contrib/find/test/findController.test.ts @@ -65,7 +65,7 @@ suite('FindController', () => { onWillSaveState: Event.None, get: (key: string) => queryState[key], getBoolean: (key: string) => !!queryState[key], - getInteger: (key: string) => undefined, + getNumber: (key: string) => undefined, store: (key: string, value: any) => { queryState[key] = value; return Promise.resolve(); }, remove: (key) => undefined } as any); @@ -440,7 +440,7 @@ suite('FindController query options persistence', () => { onWillSaveState: Event.None, get: (key: string) => queryState[key], getBoolean: (key: string) => !!queryState[key], - getInteger: (key: string) => undefined, + getNumber: (key: string) => undefined, store: (key: string, value: any) => { queryState[key] = value; return Promise.resolve(); }, remove: (key) => undefined } as any); diff --git a/src/vs/editor/contrib/folding/folding.ts b/src/vs/editor/contrib/folding/folding.ts index d85f6bfbc75..e8393a98ce2 100644 --- a/src/vs/editor/contrib/folding/folding.ts +++ b/src/vs/editor/contrib/folding/folding.ts @@ -279,6 +279,9 @@ export class FoldingController implements IEditorContribution { } return foldingModel; }); + }).then(undefined, (err) => { + onUnexpectedError(err); + return null; }); } } diff --git a/src/vs/editor/contrib/format/formatActions.ts b/src/vs/editor/contrib/format/formatActions.ts index ffc30fdd8a9..06fd88eab28 100644 --- a/src/vs/editor/contrib/format/formatActions.ts +++ b/src/vs/editor/contrib/format/formatActions.ts @@ -213,34 +213,30 @@ class FormatOnType implements editorCommon.IEditorContribution { }); - let modelOpts = model.getOptions(); - getOnTypeFormattingEdits( this._telemetryService, this._workerService, model, position, ch, - { - tabSize: modelOpts.tabSize, - insertSpaces: modelOpts.insertSpaces - }).then(edits => { + model.getFormattingOptions() + ).then(edits => { - unbind.dispose(); + unbind.dispose(); - if (canceled) { - return; - } + if (canceled) { + return; + } - if (isNonEmptyArray(edits)) { - FormattingEdit.execute(this._editor, edits); - alertFormattingEdits(edits); - } + if (isNonEmptyArray(edits)) { + FormattingEdit.execute(this._editor, edits); + alertFormattingEdits(edits); + } - }, (err) => { - unbind.dispose(); - throw err; - }); + }, (err) => { + unbind.dispose(); + throw err; + }); } public getId(): string { @@ -311,8 +307,7 @@ class FormatOnPaste implements editorCommon.IEditorContribution { } const model = this.editor.getModel(); - const { tabSize, insertSpaces } = model.getOptions(); - formatDocumentRange(this.telemetryService, this.workerService, this.editor, range, { tabSize, insertSpaces }, CancellationToken.None); + formatDocumentRange(this.telemetryService, this.workerService, this.editor, range, model.getFormattingOptions(), CancellationToken.None); } public getId(): string { @@ -354,8 +349,7 @@ export class FormatDocumentAction extends EditorAction { } const workerService = accessor.get(IEditorWorkerService); const telemetryService = accessor.get(ITelemetryService); - const { tabSize, insertSpaces } = editor.getModel().getOptions(); - return formatDocument(telemetryService, workerService, editor, { tabSize, insertSpaces }, CancellationToken.None); + return formatDocument(telemetryService, workerService, editor, editor.getModel().getFormattingOptions(), CancellationToken.None); } } @@ -386,8 +380,7 @@ export class FormatSelectionAction extends EditorAction { } const workerService = accessor.get(IEditorWorkerService); const telemetryService = accessor.get(ITelemetryService); - const { tabSize, insertSpaces } = editor.getModel().getOptions(); - return formatDocumentRange(telemetryService, workerService, editor, FormatRangeType.Selection, { tabSize, insertSpaces }, CancellationToken.None); + return formatDocumentRange(telemetryService, workerService, editor, FormatRangeType.Selection, editor.getModel().getFormattingOptions(), CancellationToken.None); } } @@ -403,13 +396,12 @@ CommandsRegistry.registerCommand('editor.action.format', accessor => { if (!editor || !editor.hasModel()) { return undefined; } - const { tabSize, insertSpaces } = editor.getModel().getOptions(); const workerService = accessor.get(IEditorWorkerService); const telemetryService = accessor.get(ITelemetryService); if (editor.getSelection().isEmpty()) { - return formatDocument(telemetryService, workerService, editor, { tabSize, insertSpaces }, CancellationToken.None); + return formatDocument(telemetryService, workerService, editor, editor.getModel().getFormattingOptions(), CancellationToken.None); } else { - return formatDocumentRange(telemetryService, workerService, editor, FormatRangeType.Selection, { tabSize, insertSpaces }, CancellationToken.None); + return formatDocumentRange(telemetryService, workerService, editor, FormatRangeType.Selection, editor.getModel().getFormattingOptions(), CancellationToken.None); } }); diff --git a/src/vs/editor/contrib/format/formattingEdit.ts b/src/vs/editor/contrib/format/formattingEdit.ts index 23bab3f1830..27dbbd06762 100644 --- a/src/vs/editor/contrib/format/formattingEdit.ts +++ b/src/vs/editor/contrib/format/formattingEdit.ts @@ -45,7 +45,7 @@ export class FormattingEdit { static execute(editor: ICodeEditor, _edits: TextEdit[]) { editor.pushUndoStop(); - let edits = FormattingEdit._handleEolEdits(editor, _edits); + const edits = FormattingEdit._handleEolEdits(editor, _edits); if (edits.length === 1 && FormattingEdit._isFullModelReplaceEdit(editor, edits[0])) { // We use replace semantics and hope that markers stay put... editor.executeEdits('formatEditsCommand', edits.map(edit => EditOperation.replace(Range.lift(edit.range), edit.text))); diff --git a/src/vs/editor/contrib/hover/modesContentHover.ts b/src/vs/editor/contrib/hover/modesContentHover.ts index bf6bab44890..671da624c7b 100644 --- a/src/vs/editor/contrib/hover/modesContentHover.ts +++ b/src/vs/editor/contrib/hover/modesContentHover.ts @@ -24,7 +24,7 @@ import { ContentHoverWidget } from 'vs/editor/contrib/hover/hoverWidgets'; import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { coalesce, isNonEmptyArray } from 'vs/base/common/arrays'; -import { IMarker, IMarkerData } from 'vs/platform/markers/common/markers'; +import { IMarker, IMarkerData, MarkerSeverity } from 'vs/platform/markers/common/markers'; import { basename } from 'vs/base/common/resources'; import { IMarkerDecorationsService } from 'vs/editor/common/services/markersDecorationService'; import { onUnexpectedError } from 'vs/base/common/errors'; @@ -474,7 +474,8 @@ export class ModesContentHoverWidget extends ContentHoverWidget { if (markerMessages.length) { markerMessages.forEach(msg => fragment.appendChild(this.renderMarkerHover(msg))); - fragment.appendChild(this.renderMarkerStatusbar(markerMessages[0])); + const markerHoverForStatusbar = markerMessages.length === 1 ? markerMessages[0] : markerMessages.sort((a, b) => MarkerSeverity.compare(a.marker.severity, b.marker.severity))[0]; + fragment.appendChild(this.renderMarkerStatusbar(markerHoverForStatusbar)); } // show @@ -550,15 +551,17 @@ export class ModesContentHoverWidget extends ContentHoverWidget { }); } })); - disposables.push(this.renderAction(actionsElement, { - label: nls.localize('peek problem', "Peek Problem"), - commandId: NextMarkerAction.ID, - run: () => { - this.hide(); - MarkerController.get(this._editor).show(markerHover.marker); - this._editor.focus(); - } - })); + if (markerHover.marker.severity === MarkerSeverity.Error || markerHover.marker.severity === MarkerSeverity.Warning || markerHover.marker.severity === MarkerSeverity.Info) { + disposables.push(this.renderAction(actionsElement, { + label: nls.localize('peek problem', "Peek Problem"), + commandId: NextMarkerAction.ID, + run: () => { + this.hide(); + MarkerController.get(this._editor).show(markerHover.marker); + this._editor.focus(); + } + })); + } this.renderDisposable = combinedDisposable(disposables); return hoverElement; } diff --git a/src/vs/editor/contrib/multicursor/test/multicursor.test.ts b/src/vs/editor/contrib/multicursor/test/multicursor.test.ts index c26d0b0fd58..f2715cbe06a 100644 --- a/src/vs/editor/contrib/multicursor/test/multicursor.test.ts +++ b/src/vs/editor/contrib/multicursor/test/multicursor.test.ts @@ -65,7 +65,7 @@ suite('Multicursor selection', () => { onWillSaveState: Event.None, get: (key: string) => queryState[key], getBoolean: (key: string) => !!queryState[key], - getInteger: (key: string) => undefined!, + getNumber: (key: string) => undefined!, store: (key: string, value: any) => { queryState[key] = value; return Promise.resolve(); }, remove: (key) => undefined } as IStorageService); diff --git a/src/vs/editor/contrib/referenceSearch/referencesWidget.ts b/src/vs/editor/contrib/referenceSearch/referencesWidget.ts index 78037a31b7e..c637c11f7e0 100644 --- a/src/vs/editor/contrib/referenceSearch/referencesWidget.ts +++ b/src/vs/editor/contrib/referenceSearch/referencesWidget.ts @@ -412,16 +412,16 @@ export class ReferenceWidget extends PeekViewWidget { onEvent(e.elements[0], 'show'); } }); - this._tree.onMouseDblClick(e => { - const aside = e.browserEvent.ctrlKey || e.browserEvent.metaKey || e.browserEvent.altKey; - const goto = e.browserEvent.detail === 2; + this._tree.onDidOpen(e => { + const aside = (e.browserEvent instanceof MouseEvent) && (e.browserEvent.ctrlKey || e.browserEvent.metaKey || e.browserEvent.altKey); + const goto = !e.browserEvent || ((e.browserEvent instanceof MouseEvent) && e.browserEvent.detail === 2); if (aside) { - onEvent(e.element, 'side'); + onEvent(e.elements[0], 'side'); } else if (goto) { - onEvent(e.element, 'goto'); + onEvent(e.elements[0], 'goto'); } else { - onEvent(e.element, 'show'); + onEvent(e.elements[0], 'show'); } }); diff --git a/src/vs/editor/contrib/snippet/snippetController2.ts b/src/vs/editor/contrib/snippet/snippetController2.ts index febc52c54a6..eadc88a2545 100644 --- a/src/vs/editor/contrib/snippet/snippetController2.ts +++ b/src/vs/editor/contrib/snippet/snippetController2.ts @@ -19,6 +19,7 @@ import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from ' import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ILogService } from 'vs/platform/log/common/log'; import { SnippetSession } from './snippetSession'; +import { EditorState, CodeEditorStateFlag } from 'vs/editor/browser/core/editorState'; export class SnippetController2 implements IEditorContribution { @@ -113,10 +114,26 @@ export class SnippetController2 implements IEditorContribution { this._updateState(); + // we listen on model and selection changes. usually + // both events come in together and this is to prevent + // that we don't call _updateState twice. + let state: EditorState; + let dedupedUpdateState = () => { + if (!state || !state.validate(this._editor)) { + this._updateState(); + state = new EditorState(this._editor, CodeEditorStateFlag.Selection | CodeEditorStateFlag.Value); + } + }; this._snippetListener = [ - this._editor.onDidChangeModelContent(e => e.isFlush && this.cancel()), + this._editor.onDidChangeModelContent(e => { + if (e.isFlush) { + this.cancel(); + } else { + setTimeout(dedupedUpdateState, 0); + } + }), + this._editor.onDidChangeCursorSelection(dedupedUpdateState), this._editor.onDidChangeModel(() => this.cancel()), - this._editor.onDidChangeCursorSelection(() => this._updateState()) ]; } @@ -193,7 +210,7 @@ export class SnippetController2 implements IEditorContribution { } } - cancel(): void { + cancel(resetSelection: boolean = false): void { this._inSnippet.reset(); this._hasPrevTabstop.reset(); this._hasNextTabstop.reset(); @@ -201,6 +218,12 @@ export class SnippetController2 implements IEditorContribution { dispose(this._session); this._session = undefined; this._modelVersionId = -1; + if (resetSelection) { + // reset selection to the primary cursor when being asked + // for. this happens when explicitly cancelling snippet mode, + // e.g. when pressing ESC + this._editor.setSelections([this._editor.getSelection()!]); + } } prev(): void { @@ -257,7 +280,7 @@ registerEditorCommand(new CommandCtor({ registerEditorCommand(new CommandCtor({ id: 'leaveSnippet', precondition: SnippetController2.InSnippetMode, - handler: ctrl => ctrl.cancel(), + handler: ctrl => ctrl.cancel(true), kbOpts: { weight: KeybindingWeight.EditorContrib + 30, kbExpr: EditorContextKeys.editorTextFocus, diff --git a/src/vs/editor/contrib/snippet/snippetSession.ts b/src/vs/editor/contrib/snippet/snippetSession.ts index bf6c4909ac7..31cfa72a41c 100644 --- a/src/vs/editor/contrib/snippet/snippetSession.ts +++ b/src/vs/editor/contrib/snippet/snippetSession.ts @@ -15,9 +15,10 @@ import { Selection } from 'vs/editor/common/core/selection'; import { IIdentifiedSingleEditOperation, ITextModel, TrackedRangeStickiness } from 'vs/editor/common/model'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { optional } from 'vs/platform/instantiation/common/instantiation'; import { Choice, Placeholder, SnippetParser, Text, TextmateSnippet } from './snippetParser'; -import { ClipboardBasedVariableResolver, CompositeSnippetVariableResolver, ModelBasedVariableResolver, SelectionBasedVariableResolver, TimeBasedVariableResolver, CommentBasedVariableResolver } from './snippetVariables'; +import { ClipboardBasedVariableResolver, CompositeSnippetVariableResolver, ModelBasedVariableResolver, SelectionBasedVariableResolver, TimeBasedVariableResolver, CommentBasedVariableResolver, WorkspaceBasedVariableResolver } from './snippetVariables'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import * as colors from 'vs/platform/theme/common/colorRegistry'; @@ -196,10 +197,6 @@ export class OneSnippet { let ranges: Range[] | undefined; for (const placeholder of placeholdersWithEqualIndex) { - if (placeholder.isFinalTabstop) { - // ignore those - break; - } if (!ranges) { ranges = []; @@ -354,6 +351,7 @@ export class SnippetSession { const modelBasedVariableResolver = new ModelBasedVariableResolver(model); const clipboardService = editor.invokeWithinContext(accessor => accessor.get(IClipboardService, optional)); + const workspaceService = editor.invokeWithinContext(accessor => accessor.get(IWorkspaceContextService, optional)); let delta = 0; @@ -409,7 +407,8 @@ export class SnippetSession { new ClipboardBasedVariableResolver(clipboardService, idx, indexedSelections.length), new SelectionBasedVariableResolver(model, selection), new CommentBasedVariableResolver(model), - new TimeBasedVariableResolver + new TimeBasedVariableResolver, + new WorkspaceBasedVariableResolver(workspaceService), ])); const offset = model.getOffsetAt(start) + delta; @@ -571,6 +570,12 @@ export class SnippetSession { return false; } + if (allPossibleSelections.has(0)) { + // selection overlaps with a final tab stop which means + // we done + return false; + } + // add selections from 'this' snippet so that we know all // selections for this placeholder allPossibleSelections.forEach((array, index) => { diff --git a/src/vs/editor/contrib/snippet/snippetVariables.ts b/src/vs/editor/contrib/snippet/snippetVariables.ts index dd7591c79ab..d7307c5a043 100644 --- a/src/vs/editor/contrib/snippet/snippetVariables.ts +++ b/src/vs/editor/contrib/snippet/snippetVariables.ts @@ -11,6 +11,8 @@ import { VariableResolver, Variable, Text } from 'vs/editor/contrib/snippet/snip import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { getLeadingWhitespace, commonPrefixLength, isFalsyOrWhitespace, pad } from 'vs/base/common/strings'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { isSingleFolderWorkspaceIdentifier, toWorkspaceIdentifier, WORKSPACE_EXTENSION } from 'vs/platform/workspaces/common/workspaces'; export const KnownSnippetVariableNames = Object.freeze({ 'CURRENT_YEAR': true, @@ -38,6 +40,7 @@ export const KnownSnippetVariableNames = Object.freeze({ 'BLOCK_COMMENT_START': true, 'BLOCK_COMMENT_END': true, 'LINE_COMMENT': true, + 'WORKSPACE_NAME': true, }); export class CompositeSnippetVariableResolver implements VariableResolver { @@ -244,3 +247,29 @@ export class TimeBasedVariableResolver implements VariableResolver { return undefined; } } + +export class WorkspaceBasedVariableResolver implements VariableResolver { + constructor( + private readonly _workspaceService: IWorkspaceContextService, + ) { + // + } + + resolve(variable: Variable): string | undefined { + if (variable.name !== 'WORKSPACE_NAME' || !this._workspaceService) { + return undefined; + } + + const workspaceIdentifier = toWorkspaceIdentifier(this._workspaceService.getWorkspace()); + if (!workspaceIdentifier) { + return undefined; + } + + if (isSingleFolderWorkspaceIdentifier(workspaceIdentifier)) { + return basename(workspaceIdentifier.path); + } + + const filename = basename(workspaceIdentifier.configPath.path); + return filename.substr(0, filename.length - WORKSPACE_EXTENSION.length - 1); + } +} \ No newline at end of file diff --git a/src/vs/editor/contrib/snippet/test/snippetController2.test.ts b/src/vs/editor/contrib/snippet/test/snippetController2.test.ts index 668fc0b74b9..bfb0cf61727 100644 --- a/src/vs/editor/contrib/snippet/test/snippetController2.test.ts +++ b/src/vs/editor/contrib/snippet/test/snippetController2.test.ts @@ -11,6 +11,7 @@ import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKe import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { NullLogService } from 'vs/platform/log/common/log'; import { Handler } from 'vs/editor/common/editorCommon'; +import { timeout } from 'vs/base/common/async'; suite('SnippetController2', function () { @@ -133,11 +134,11 @@ suite('SnippetController2', function () { assertSelections(editor, new Selection(1, 1, 1, 7), new Selection(2, 5, 2, 11)); editor.trigger('test', 'cut', {}); - assertContextKeys(contextKeys, true, false, true); + assertContextKeys(contextKeys, false, false, false); assertSelections(editor, new Selection(1, 1, 1, 1), new Selection(2, 5, 2, 5)); editor.trigger('test', 'type', { text: 'abc' }); - assertContextKeys(contextKeys, true, false, true); + assertContextKeys(contextKeys, false, false, false); ctrl.next(); assertContextKeys(contextKeys, false, false, false); @@ -159,9 +160,9 @@ suite('SnippetController2', function () { assertSelections(editor, new Selection(1, 4, 1, 4), new Selection(2, 8, 2, 8)); assertContextKeys(contextKeys, true, false, true); - ctrl.next(); - assertSelections(editor, new Selection(1, 7, 1, 7), new Selection(2, 11, 2, 11)); - assertContextKeys(contextKeys, true, true, true); + // ctrl.next(); + // assertSelections(editor, new Selection(1, 7, 1, 7), new Selection(2, 11, 2, 11)); + // assertContextKeys(contextKeys, true, true, true); ctrl.next(); assertSelections(editor, new Selection(1, 7, 1, 7), new Selection(2, 11, 2, 11)); @@ -176,10 +177,10 @@ suite('SnippetController2', function () { ctrl.insert('farboo'); assertSelections(editor, new Selection(1, 7, 1, 7), new Selection(2, 11, 2, 11)); - assertContextKeys(contextKeys, true, false, true); + // assertContextKeys(contextKeys, true, false, true); - ctrl.next(); - assertSelections(editor, new Selection(1, 7, 1, 7), new Selection(2, 11, 2, 11)); + // ctrl.next(); + // assertSelections(editor, new Selection(1, 7, 1, 7), new Selection(2, 11, 2, 11)); assertContextKeys(contextKeys, false, false, false); }); @@ -361,4 +362,63 @@ suite('SnippetController2', function () { assertSelections(editor, new Selection(1, 7, 1, 7)); assertContextKeys(contextKeys, false, false, false); }); + + test('Cancelling snippet mode should discard added cursors #68512 (soft cancel)', function () { + const ctrl = new SnippetController2(editor, logService, contextKeys); + model.setValue(''); + editor.setSelection(new Selection(1, 1, 1, 1)); + + ctrl.insert('.REGION ${2:FUNCTION_NAME}\nCREATE.FUNCTION ${1:VOID} ${2:FUNCTION_NAME}(${3:})\n\t${4:}\nEND\n.ENDREGION$0'); + assertSelections(editor, new Selection(2, 17, 2, 21)); + + ctrl.next(); + assertSelections(editor, new Selection(1, 9, 1, 22), new Selection(2, 22, 2, 35)); + assertContextKeys(contextKeys, true, true, true); + + editor.setSelections([new Selection(1, 22, 1, 22), new Selection(2, 35, 2, 35)]); + assertContextKeys(contextKeys, true, true, true); + + editor.setSelections([new Selection(2, 1, 2, 1), new Selection(2, 36, 2, 36)]); + assertContextKeys(contextKeys, false, false, false); + assertSelections(editor, new Selection(2, 1, 2, 1), new Selection(2, 36, 2, 36)); + }); + + test('Cancelling snippet mode should discard added cursors #68512 (hard cancel)', function () { + const ctrl = new SnippetController2(editor, logService, contextKeys); + model.setValue(''); + editor.setSelection(new Selection(1, 1, 1, 1)); + + ctrl.insert('.REGION ${2:FUNCTION_NAME}\nCREATE.FUNCTION ${1:VOID} ${2:FUNCTION_NAME}(${3:})\n\t${4:}\nEND\n.ENDREGION$0'); + assertSelections(editor, new Selection(2, 17, 2, 21)); + + ctrl.next(); + assertSelections(editor, new Selection(1, 9, 1, 22), new Selection(2, 22, 2, 35)); + assertContextKeys(contextKeys, true, true, true); + + editor.setSelections([new Selection(1, 22, 1, 22), new Selection(2, 35, 2, 35)]); + assertContextKeys(contextKeys, true, true, true); + + ctrl.cancel(true); + assertContextKeys(contextKeys, false, false, false); + assertSelections(editor, new Selection(1, 22, 1, 22)); + }); + + test('A little confusing visual effect of highlighting for snippet tabstop #43270', async function () { + const ctrl = new SnippetController2(editor, logService, contextKeys); + model.setValue(''); + editor.setSelection(new Selection(1, 1, 1, 1)); + + ctrl.insert('background-color: ${1:fff};$0'); + assertSelections(editor, new Selection(1, 19, 1, 22)); + + editor.setSelection(new Selection(1, 22, 1, 22)); + assertContextKeys(contextKeys, true, false, true); + editor.trigger('', 'deleteRight', null); + + assert.equal(model.getValue(), 'background-color: fff'); + + await timeout(0); // this depends on re-scheduling of events... + + assertContextKeys(contextKeys, false, false, false); + }); }); diff --git a/src/vs/editor/contrib/snippet/test/snippetSession.test.ts b/src/vs/editor/contrib/snippet/test/snippetSession.test.ts index 0d2ec457b50..924f9a6d230 100644 --- a/src/vs/editor/contrib/snippet/test/snippetSession.test.ts +++ b/src/vs/editor/contrib/snippet/test/snippetSession.test.ts @@ -331,7 +331,7 @@ suite('SnippetSession', function () { // reset selection to placeholder session.next(); - assert.equal(session.isSelectionWithinPlaceholders(), true); + assert.equal(session.isSelectionWithinPlaceholders(), false); assert.equal(session.isAtLastPlaceholder, true); assertSelections(editor, new Selection(1, 13, 1, 13), new Selection(2, 17, 2, 17)); }); diff --git a/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts b/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts index 4931b38dac7..57853e2b59f 100644 --- a/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts +++ b/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts @@ -6,10 +6,11 @@ import * as assert from 'assert'; import { isWindows } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; import { Selection } from 'vs/editor/common/core/selection'; -import { SelectionBasedVariableResolver, CompositeSnippetVariableResolver, ModelBasedVariableResolver, ClipboardBasedVariableResolver, TimeBasedVariableResolver } from 'vs/editor/contrib/snippet/snippetVariables'; +import { SelectionBasedVariableResolver, CompositeSnippetVariableResolver, ModelBasedVariableResolver, ClipboardBasedVariableResolver, TimeBasedVariableResolver, WorkspaceBasedVariableResolver } from 'vs/editor/contrib/snippet/snippetVariables'; import { SnippetParser, Variable, VariableResolver } from 'vs/editor/contrib/snippet/snippetParser'; import { TextModel } from 'vs/editor/common/model/textModel'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { Workspace, toWorkspaceFolders, IWorkspace, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; suite('Snippet Variables Resolver', function () { @@ -301,4 +302,37 @@ suite('Snippet Variables Resolver', function () { snippet.resolveVariables({ resolve() { return '11'; } }); assert.equal(snippet.toString(), 'It is not line 10'); }); -}); + + test('Add workspace name variable for snippets #68261', function () { + + let workspace: IWorkspace; + let resolver: VariableResolver; + const workspaceService = new class implements IWorkspaceContextService { + _serviceBrand: any; + _throw = () => { throw new Error(); }; + onDidChangeWorkbenchState = this._throw; + onDidChangeWorkspaceName = this._throw; + onDidChangeWorkspaceFolders = this._throw; + getCompleteWorkspace = this._throw; + getWorkspace(): IWorkspace { return workspace; } + getWorkbenchState = this._throw; + getWorkspaceFolder = this._throw; + isCurrentWorkspace = this._throw; + isInsideWorkspace = this._throw; + }; + + resolver = new WorkspaceBasedVariableResolver(workspaceService); + + // empty workspace + workspace = new Workspace(''); + assertVariableResolve(resolver, 'WORKSPACE_NAME', undefined); + + // single folder workspace without config + workspace = new Workspace('', toWorkspaceFolders([{ path: '/folderName' }])); + assertVariableResolve(resolver, 'WORKSPACE_NAME', 'folderName'); + + // workspace with config + workspace = new Workspace('', toWorkspaceFolders([{ path: 'folderName' }]), URI.file('testWorkspace.code-workspace')); + assertVariableResolve(resolver, 'WORKSPACE_NAME', 'testWorkspace'); + }); +}); \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/suggestController.ts b/src/vs/editor/contrib/suggest/suggestController.ts index 30d7559131e..833cb4a4308 100644 --- a/src/vs/editor/contrib/suggest/suggestController.ts +++ b/src/vs/editor/contrib/suggest/suggestController.ts @@ -292,8 +292,10 @@ export class SuggestController implements IEditorContribution { } private _alertCompletionItem({ completion: suggestion }: CompletionItem): void { - let msg = nls.localize('arai.alert.snippet', "Accepting '{0}' did insert the following text: {1}", suggestion.label, suggestion.insertText); - alert(msg); + if (isNonEmptyArray(suggestion.additionalTextEdits)) { + let msg = nls.localize('arai.alert.snippet', "Accepting '{0}' made {1} additional edits", suggestion.label, suggestion.additionalTextEdits.length); + alert(msg); + } } triggerSuggest(onlyFrom?: CompletionItemProvider[]): void { diff --git a/src/vs/editor/contrib/suggest/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts index 48477274699..b24a90cc31f 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -547,23 +547,15 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate { this.onDidSelectEmitter.fire({ item, index, model: completionModel }); - alert(nls.localize('suggestionAriaAccepted', "{0}, accepted", item.completion.label)); this.editor.focus(); }); } private _getSuggestionAriaAlertLabel(item: CompletionItem): string { - const isSnippet = item.completion.kind === CompletionItemKind.Snippet; - - if (!canExpandCompletionItem(item)) { - return isSnippet ? nls.localize('ariaCurrentSnippetSuggestion', "{0}, snippet suggestion", item.completion.label) - : nls.localize('ariaCurrentSuggestion', "{0}, suggestion", item.completion.label); - } else if (this.expandDocsSettingFromStorage()) { - return isSnippet ? nls.localize('ariaCurrentSnippeSuggestionReadDetails', "{0}, snippet suggestion. Reading details. {1}", item.completion.label, this.details.getAriaLabel()) - : nls.localize('ariaCurrenttSuggestionReadDetails', "{0}, suggestion. Reading details. {1}", item.completion.label, this.details.getAriaLabel()); + if (this.expandDocsSettingFromStorage()) { + return nls.localize('ariaCurrenttSuggestionReadDetails', "Item {0}, docs: {1}", item.completion.label, this.details.getAriaLabel()); } else { - return isSnippet ? nls.localize('ariaCurrentSnippetSuggestionWithDetails', "{0}, snippet suggestion, has details", item.completion.label) - : nls.localize('ariaCurrentSuggestionWithDetails', "{0}, suggestion, has details", item.completion.label); + return item.completion.label; } } diff --git a/src/vs/editor/standalone/browser/colorizer.ts b/src/vs/editor/standalone/browser/colorizer.ts index e085b2e80ed..7c9a64ff411 100644 --- a/src/vs/editor/standalone/browser/colorizer.ts +++ b/src/vs/editor/standalone/browser/colorizer.ts @@ -13,6 +13,7 @@ import { IModeService } from 'vs/editor/common/services/modeService'; import { RenderLineInput, renderViewLine2 as renderViewLine } from 'vs/editor/common/viewLayout/viewLineRenderer'; import { ViewLineRenderingData } from 'vs/editor/common/viewModel/viewModel'; import { IStandaloneThemeService } from 'vs/editor/standalone/common/standaloneThemeService'; +import { MonarchTokenizer } from 'vs/editor/standalone/common/monarch/monarchLexer'; export interface IColorizerOptions { tabSize?: number; @@ -64,7 +65,17 @@ export class Colorizer { let tokenizationSupport = TokenizationRegistry.get(language); if (tokenizationSupport) { - return Promise.resolve(_colorize(lines, tabSize, tokenizationSupport)); + return _colorize(lines, tabSize, tokenizationSupport); + } + + let tokenizationSupportPromise = TokenizationRegistry.getPromise(language); + if (tokenizationSupportPromise) { + // A tokenizer will be registered soon + return new Promise((resolve, reject) => { + tokenizationSupportPromise!.then(tokenizationSupport => { + _colorize(lines, tabSize, tokenizationSupport).then(resolve, reject); + }, reject); + }); } return new Promise((resolve, reject) => { @@ -82,9 +93,10 @@ export class Colorizer { } const tokenizationSupport = TokenizationRegistry.get(language!); if (tokenizationSupport) { - return resolve(_colorize(lines, tabSize, tokenizationSupport)); + _colorize(lines, tabSize, tokenizationSupport).then(resolve, reject); + return; } - return resolve(_fakeColorize(lines, tabSize)); + resolve(_fakeColorize(lines, tabSize)); }; // wait 500ms for mode to load, then give up @@ -130,8 +142,21 @@ export class Colorizer { } } -function _colorize(lines: string[], tabSize: number, tokenizationSupport: ITokenizationSupport): string { - return _actualColorize(lines, tabSize, tokenizationSupport); +function _colorize(lines: string[], tabSize: number, tokenizationSupport: ITokenizationSupport): Promise { + return new Promise((c, e) => { + const execute = () => { + const result = _actualColorize(lines, tabSize, tokenizationSupport); + if (tokenizationSupport instanceof MonarchTokenizer) { + const status = tokenizationSupport.getLoadStatus(); + if (status.loaded === false) { + status.promise.then(execute, e); + return; + } + } + c(result); + }; + execute(); + }); } function _fakeColorize(lines: string[], tabSize: number): string { diff --git a/src/vs/editor/standalone/browser/simpleServices.ts b/src/vs/editor/standalone/browser/simpleServices.ts index ea099deb164..c76b38b22df 100644 --- a/src/vs/editor/standalone/browser/simpleServices.ts +++ b/src/vs/editor/standalone/browser/simpleServices.ts @@ -22,7 +22,7 @@ import * as editorCommon from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; import { TextEdit, WorkspaceEdit, isResourceTextEdit } from 'vs/editor/common/modes'; import { IModelService } from 'vs/editor/common/services/modelService'; -import { ITextEditorModel, ITextModelContentProvider, ITextModelService } from 'vs/editor/common/services/resolverService'; +import { IResolvedTextEditorModel, ITextModelContentProvider, ITextModelService } from 'vs/editor/common/services/resolverService'; import { ITextResourceConfigurationService, ITextResourcePropertiesService } from 'vs/editor/common/services/resourceConfiguration'; import { CommandsRegistry, ICommand, ICommandEvent, ICommandHandler, ICommandService } from 'vs/platform/commands/common/commands'; import { IConfigurationChangeEvent, IConfigurationData, IConfigurationOverrides, IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -44,7 +44,7 @@ import { IWorkspace, IWorkspaceContextService, IWorkspaceFolder, IWorkspaceFolde import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; -export class SimpleModel implements ITextEditorModel { +export class SimpleModel implements IResolvedTextEditorModel { private model: ITextModel; private readonly _onDispose: Emitter; @@ -98,7 +98,7 @@ export class SimpleEditorModelResolverService implements ITextModelService { this.editor = editor; } - public createModelReference(resource: URI): Promise> { + public createModelReference(resource: URI): Promise> { let model: ITextModel | null = withTypedEditor(this.editor, (editor) => this.findModel(editor, resource), (diffEditor) => this.findModel(diffEditor.getOriginalEditor(), resource) || this.findModel(diffEditor.getModifiedEditor(), resource) diff --git a/src/vs/editor/standalone/browser/standaloneEditor.ts b/src/vs/editor/standalone/browser/standaloneEditor.ts index bd5660f51e4..09d4b4bce46 100644 --- a/src/vs/editor/standalone/browser/standaloneEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneEditor.ts @@ -37,6 +37,7 @@ import { IMarker, IMarkerData } from 'vs/platform/markers/common/markers'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; +import { clearAllFontInfos } from 'vs/editor/browser/config/configuration'; type Omit = Pick>; @@ -311,6 +312,13 @@ export function setTheme(themeName: string): void { StaticServices.standaloneThemeService.get().setTheme(themeName); } +/** + * Clears all cached font measurements and triggers re-measurement. + */ +export function remeasureFonts(): void { + clearAllFontInfos(); +} + /** * @internal */ @@ -340,6 +348,7 @@ export function createMonacoEditorAPI(): typeof monaco.editor { tokenize: tokenize, defineTheme: defineTheme, setTheme: setTheme, + remeasureFonts: remeasureFonts, // enums ScrollbarVisibility: standaloneEnums.ScrollbarVisibility, diff --git a/src/vs/editor/standalone/browser/standaloneLanguages.ts b/src/vs/editor/standalone/browser/standaloneLanguages.ts index 238ae5df16e..8a1f4a9c183 100644 --- a/src/vs/editor/standalone/browser/standaloneLanguages.ts +++ b/src/vs/editor/standalone/browser/standaloneLanguages.ts @@ -292,31 +292,47 @@ export interface EncodedTokensProvider { function isEncodedTokensProvider(provider: TokensProvider | EncodedTokensProvider): provider is EncodedTokensProvider { return provider['tokenizeEncoded']; } + +function isThenable(obj: any): obj is Thenable { + if (typeof obj.then === 'function') { + return true; + } + return false; +} + /** * Set the tokens provider for a language (manual implementation). */ -export function setTokensProvider(languageId: string, provider: TokensProvider | EncodedTokensProvider): IDisposable { +export function setTokensProvider(languageId: string, provider: TokensProvider | EncodedTokensProvider | Thenable): IDisposable { let languageIdentifier = StaticServices.modeService.get().getLanguageIdentifier(languageId); if (!languageIdentifier) { throw new Error(`Cannot set tokens provider for unknown language ${languageId}`); } - let adapter: modes.ITokenizationSupport; - if (isEncodedTokensProvider(provider)) { - adapter = new EncodedTokenizationSupport2Adapter(provider); - } else { - adapter = new TokenizationSupport2Adapter(StaticServices.standaloneThemeService.get(), languageIdentifier, provider); + const create = (provider: TokensProvider | EncodedTokensProvider) => { + if (isEncodedTokensProvider(provider)) { + return new EncodedTokenizationSupport2Adapter(provider); + } else { + return new TokenizationSupport2Adapter(StaticServices.standaloneThemeService.get(), languageIdentifier!, provider); + } + }; + if (isThenable(provider)) { + return modes.TokenizationRegistry.registerPromise(languageId, provider.then(provider => create(provider))); } - return modes.TokenizationRegistry.register(languageId, adapter); + return modes.TokenizationRegistry.register(languageId, create(provider)); } /** * Set the tokens provider for a language (monarch implementation). */ -export function setMonarchTokensProvider(languageId: string, languageDef: IMonarchLanguage): IDisposable { - let lexer = compile(languageId, languageDef); - let adapter = createTokenizationSupport(StaticServices.modeService.get(), StaticServices.standaloneThemeService.get(), languageId, lexer); - return modes.TokenizationRegistry.register(languageId, adapter); +export function setMonarchTokensProvider(languageId: string, languageDef: IMonarchLanguage | Thenable): IDisposable { + const create = (languageDef: IMonarchLanguage) => { + return createTokenizationSupport(StaticServices.modeService.get(), StaticServices.standaloneThemeService.get(), languageId, compile(languageId, languageDef)); + }; + if (isThenable(languageDef)) { + return modes.TokenizationRegistry.registerPromise(languageId, languageDef.then(languageDef => create(languageDef))); + } + return modes.TokenizationRegistry.register(languageId, create(languageDef)); } /** diff --git a/src/vs/editor/standalone/common/monarch/monarchLexer.ts b/src/vs/editor/standalone/common/monarch/monarchLexer.ts index 0deb0ed169a..6d8b55352ab 100644 --- a/src/vs/editor/standalone/common/monarch/monarchLexer.ts +++ b/src/vs/editor/standalone/common/monarch/monarchLexer.ts @@ -374,13 +374,16 @@ class MonarchModernTokensCollector implements IMonarchTokensCollector { } } -class MonarchTokenizer implements modes.ITokenizationSupport { +export type ILoadStatus = { loaded: true; } | { loaded: false; promise: Promise; }; + +export class MonarchTokenizer implements modes.ITokenizationSupport { private readonly _modeService: IModeService; private readonly _standaloneThemeService: IStandaloneThemeService; private readonly _modeId: string; private readonly _lexer: monarchCommon.ILexer; private _embeddedModes: { [modeId: string]: boolean; }; + public embeddedLoaded: Promise; private _tokenizationRegistryListener: IDisposable; constructor(modeService: IModeService, standaloneThemeService: IStandaloneThemeService, modeId: string, lexer: monarchCommon.ILexer) { @@ -389,6 +392,7 @@ class MonarchTokenizer implements modes.ITokenizationSupport { this._modeId = modeId; this._lexer = lexer; this._embeddedModes = Object.create(null); + this.embeddedLoaded = Promise.resolve(undefined); // Set up listening for embedded modes let emitting = false; @@ -416,6 +420,39 @@ class MonarchTokenizer implements modes.ITokenizationSupport { this._tokenizationRegistryListener.dispose(); } + public getLoadStatus(): ILoadStatus { + let promises: Thenable[] = []; + for (let nestedModeId in this._embeddedModes) { + const tokenizationSupport = modes.TokenizationRegistry.get(nestedModeId); + if (tokenizationSupport) { + // The nested mode is already loaded + if (tokenizationSupport instanceof MonarchTokenizer) { + const nestedModeStatus = tokenizationSupport.getLoadStatus(); + if (nestedModeStatus.loaded === false) { + promises.push(nestedModeStatus.promise); + } + } + continue; + } + + const tokenizationSupportPromise = modes.TokenizationRegistry.getPromise(nestedModeId); + if (tokenizationSupportPromise) { + // The nested mode is in the process of being loaded + promises.push(tokenizationSupportPromise); + } + } + + if (promises.length === 0) { + return { + loaded: true + }; + } + return { + loaded: false, + promise: Promise.all(promises).then(_ => undefined) + }; + } + public getInitialState(): modes.IState { let rootState = MonarchStackElementFactory.create(null, this._lexer.start!); return MonarchLineStateFactory.create(rootState, null); diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 8084ebff346..af4d3f7f0d6 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -923,6 +923,11 @@ declare namespace monaco.editor { */ export function setTheme(themeName: string): void; + /** + * Clears all cached font measurements and triggers re-measurement. + */ + export function remeasureFonts(): void; + export type BuiltinTheme = 'vs' | 'vs-dark' | 'hc-black'; export interface IStandaloneThemeData { @@ -1382,7 +1387,7 @@ declare namespace monaco.editor { /** * The text to replace with. This can be null to emulate a simple delete. */ - text: string; + text: string | null; /** * This indicates that this operation has "insert" semantics. * i.e. forceMoveMarkers = true => if `range` is collapsed, all markers at the position will be moved. @@ -4279,12 +4284,12 @@ declare namespace monaco.languages { /** * Set the tokens provider for a language (manual implementation). */ - export function setTokensProvider(languageId: string, provider: TokensProvider | EncodedTokensProvider): IDisposable; + export function setTokensProvider(languageId: string, provider: TokensProvider | EncodedTokensProvider | Thenable): IDisposable; /** * Set the tokens provider for a language (monarch implementation). */ - export function setMonarchTokensProvider(languageId: string, languageDef: IMonarchLanguage): IDisposable; + export function setMonarchTokensProvider(languageId: string, languageDef: IMonarchLanguage | Thenable): IDisposable; /** * Register a reference provider (used by e.g. reference search). diff --git a/src/vs/platform/actions/browser/menuItemActionItem.ts b/src/vs/platform/actions/browser/menuItemActionItem.ts index 872b66f8e5a..8b0275c16cc 100644 --- a/src/vs/platform/actions/browser/menuItemActionItem.ts +++ b/src/vs/platform/actions/browser/menuItemActionItem.ts @@ -76,7 +76,7 @@ class AlternativeKeyEmitter extends Emitter { } } -export function fillInContextMenuActions(menu: IMenu, options: IMenuActionOptions, target: IAction[] | { primary: IAction[]; secondary: IAction[]; }, contextMenuService: IContextMenuService, isPrimaryGroup?: (group: string) => boolean): void { +export function fillInContextMenuActions(menu: IMenu, options: IMenuActionOptions | undefined, target: IAction[] | { primary: IAction[]; secondary: IAction[]; }, contextMenuService: IContextMenuService, isPrimaryGroup?: (group: string) => boolean): void { const groups = menu.getActions(options); const getAlternativeActions = AlternativeKeyEmitter.getInstance(contextMenuService).isPressed; diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index ead87925df7..b3bc081c9c5 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -6,9 +6,9 @@ import { Action } from 'vs/base/common/actions'; import { SyncDescriptor0, createSyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { IConstructorSignature2, createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IKeybindings } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { IKeybindings, KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { ICommandService } from 'vs/platform/commands/common/commands'; +import { ICommandService, ICommandHandler, CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IDisposable } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; import { URI, UriComponents } from 'vs/base/common/uri'; @@ -281,13 +281,13 @@ export class SyncActionDescriptor { private _descriptor: SyncDescriptor0; private _id: string; - private _label: string; + private _label?: string; private _keybindings: IKeybindings | undefined; private _keybindingContext: ContextKeyExpr | undefined; private _keybindingWeight: number | undefined; constructor(ctor: IConstructorSignature2, - id: string, label: string, keybindings?: IKeybindings, keybindingContext?: ContextKeyExpr, keybindingWeight?: number + id: string, label: string | undefined, keybindings?: IKeybindings, keybindingContext?: ContextKeyExpr, keybindingWeight?: number ) { this._id = id; this._label = label; @@ -305,7 +305,7 @@ export class SyncActionDescriptor { return this._id; } - public get label(): string { + public get label(): string | undefined { return this._label; } @@ -321,3 +321,63 @@ export class SyncActionDescriptor { return this._keybindingWeight; } } + + +export interface IActionDescriptor { + id: string; + handler: ICommandHandler; + + // ICommandUI + title?: ILocalizedString; + category?: string; + f1?: boolean; + + // + menu?: { + menuId: MenuId, + when?: ContextKeyExpr; + group?: string; + }; + + // + keybinding?: { + when?: ContextKeyExpr; + weight?: number; + keys: IKeybindings; + }; +} + + +export function registerAction(desc: IActionDescriptor) { + + const { id, handler, title, category, menu, keybinding } = desc; + + // 1) register as command + CommandsRegistry.registerCommand(id, handler); + + // 2) menus + if (menu && title) { + let command = { id, title, category }; + let { menuId, when, group } = menu; + MenuRegistry.appendMenuItem(menuId, { + command, + when, + group + }); + } + + // 3) keybindings + if (keybinding) { + let { when, weight, keys } = keybinding; + KeybindingsRegistry.registerKeybindingRule({ + id, + when, + weight: weight || 0, + primary: keys.primary, + secondary: keys.secondary, + linux: keys.linux, + mac: keys.mac, + win: keys.win + }); + } +} diff --git a/src/vs/platform/contextkey/common/contextkey.ts b/src/vs/platform/contextkey/common/contextkey.ts index 6617af20f86..11d8132fe05 100644 --- a/src/vs/platform/contextkey/common/contextkey.ts +++ b/src/vs/platform/contextkey/common/contextkey.ts @@ -16,6 +16,14 @@ export const enum ContextKeyExprType { Regex = 6 } +export interface IContextKeyExprMapper { + mapDefined(key: string): ContextKeyDefinedExpr; + mapNot(key: string): ContextKeyNotExpr; + mapEquals(key: string, value: any): ContextKeyEqualsExpr; + mapNotEquals(key: string, value: any): ContextKeyNotEqualsExpr; + mapRegex(key: string, regexp: RegExp | null): ContextKeyRegexExpr; +} + export abstract class ContextKeyExpr { public static has(key: string): ContextKeyExpr { @@ -138,6 +146,7 @@ export abstract class ContextKeyExpr { public abstract normalize(): ContextKeyExpr | null; public abstract serialize(): string; public abstract keys(): string[]; + public abstract map(mapFnc: IContextKeyExprMapper): ContextKeyExpr; } function cmp(a: ContextKeyExpr, b: ContextKeyExpr): number { @@ -202,10 +211,14 @@ export class ContextKeyDefinedExpr implements ContextKeyExpr { public keys(): string[] { return [this.key]; } + + public map(mapFnc: IContextKeyExprMapper): ContextKeyExpr { + return mapFnc.mapDefined(this.key); + } } export class ContextKeyEqualsExpr implements ContextKeyExpr { - constructor(private key: string, private value: any) { + constructor(private readonly key: string, private readonly value: any) { } public getType(): ContextKeyExprType { @@ -263,6 +276,10 @@ export class ContextKeyEqualsExpr implements ContextKeyExpr { public keys(): string[] { return [this.key]; } + + public map(mapFnc: IContextKeyExprMapper): ContextKeyExpr { + return mapFnc.mapEquals(this.key, this.value); + } } export class ContextKeyNotEqualsExpr implements ContextKeyExpr { @@ -324,6 +341,10 @@ export class ContextKeyNotEqualsExpr implements ContextKeyExpr { public keys(): string[] { return [this.key]; } + + public map(mapFnc: IContextKeyExprMapper): ContextKeyExpr { + return mapFnc.mapNotEquals(this.key, this.value); + } } export class ContextKeyNotExpr implements ContextKeyExpr { @@ -366,6 +387,10 @@ export class ContextKeyNotExpr implements ContextKeyExpr { public keys(): string[] { return [this.key]; } + + public map(mapFnc: IContextKeyExprMapper): ContextKeyExpr { + return mapFnc.mapNot(this.key); + } } export class ContextKeyRegexExpr implements ContextKeyExpr { @@ -424,6 +449,10 @@ export class ContextKeyRegexExpr implements ContextKeyExpr { public keys(): string[] { return [this.key]; } + + public map(mapFnc: IContextKeyExprMapper): ContextKeyExpr { + return mapFnc.mapRegex(this.key, this.regexp); + } } export class ContextKeyAndExpr implements ContextKeyExpr { @@ -523,6 +552,10 @@ export class ContextKeyAndExpr implements ContextKeyExpr { } return result; } + + public map(mapFnc: IContextKeyExprMapper): ContextKeyExpr { + return new ContextKeyAndExpr(this.expr.map(expr => expr.map(mapFnc))); + } } export class RawContextKey extends ContextKeyDefinedExpr { diff --git a/src/vs/platform/download/common/download.ts b/src/vs/platform/download/common/download.ts index 9856da753ee..83bd30bdaa5 100644 --- a/src/vs/platform/download/common/download.ts +++ b/src/vs/platform/download/common/download.ts @@ -13,6 +13,6 @@ export interface IDownloadService { _serviceBrand: any; - download(uri: URI, to: string, cancellationToken?: CancellationToken): Promise; + download(uri: URI, to?: string, cancellationToken?: CancellationToken): Promise; -} \ No newline at end of file +} diff --git a/src/vs/platform/download/node/downloadIpc.ts b/src/vs/platform/download/node/downloadIpc.ts index 53144c3f1db..52ca30256fe 100644 --- a/src/vs/platform/download/node/downloadIpc.ts +++ b/src/vs/platform/download/node/downloadIpc.ts @@ -11,6 +11,8 @@ import { Event, Emitter } from 'vs/base/common/event'; import { IDownloadService } from 'vs/platform/download/common/download'; import { mkdirp } from 'vs/base/node/pfs'; import { IURITransformer } from 'vs/base/common/uriIpc'; +import { tmpdir } from 'os'; +import { generateUuid } from 'vs/base/common/uuid'; export type UploadResponse = Buffer | string | undefined; @@ -46,21 +48,21 @@ export class DownloadServiceChannelClient implements IDownloadService { constructor(private channel: IChannel, private getUriTransformer: () => IURITransformer) { } - download(from: URI, to: string): Promise { + download(from: URI, to: string = path.join(tmpdir(), generateUuid())): Promise { from = this.getUriTransformer().transformOutgoingURI(from); const dirName = path.dirname(to); let out: fs.WriteStream; - return new Promise((c, e) => { + return new Promise((c, e) => { return mkdirp(dirName) .then(() => { out = fs.createWriteStream(to); - out.once('close', () => c()); + out.once('close', () => c(to)); out.once('error', e); const uploadStream = this.channel.listen('upload', from); const disposable = uploadStream(result => { if (result === undefined) { disposable.dispose(); - out.end(c); + out.end(() => c(to)); } else if (Buffer.isBuffer(result)) { out.write(result); } else if (typeof result === 'string') { @@ -71,4 +73,4 @@ export class DownloadServiceChannelClient implements IDownloadService { }); }); } -} \ No newline at end of file +} diff --git a/src/vs/platform/download/node/downloadService.ts b/src/vs/platform/download/node/downloadService.ts index c20cbd1a8d5..7822ed2253d 100644 --- a/src/vs/platform/download/node/downloadService.ts +++ b/src/vs/platform/download/node/downloadService.ts @@ -10,6 +10,9 @@ import { copy } from 'vs/base/node/pfs'; import { IRequestService } from 'vs/platform/request/node/request'; import { asText, download } from 'vs/base/node/request'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { join } from 'vs/base/common/path'; +import { tmpdir } from 'os'; +import { generateUuid } from 'vs/base/common/uuid'; export class DownloadService implements IDownloadService { @@ -19,18 +22,18 @@ export class DownloadService implements IDownloadService { @IRequestService private readonly requestService: IRequestService ) { } - download(uri: URI, target: string, cancellationToken: CancellationToken = CancellationToken.None): Promise { + download(uri: URI, target: string = join(tmpdir(), generateUuid()), cancellationToken: CancellationToken = CancellationToken.None): Promise { if (uri.scheme === Schemas.file) { - return copy(uri.fsPath, target); + return copy(uri.fsPath, target).then(() => target); } const options = { type: 'GET', url: uri.toString() }; return this.requestService.request(options, cancellationToken) .then(context => { if (context.res.statusCode === 200) { - return download(target, context); + return download(target, context).then(() => target); } return asText(context) .then(message => Promise.reject(new Error(`Expected 200, got back ${context.res.statusCode} instead.\n\n${message}`))); }); } -} \ No newline at end of file +} diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index 7a304566aef..25b3fae01f6 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -239,3 +239,17 @@ export function hasArgs(arg: string | string[] | undefined): boolean { } return false; } + +export function addArg(argv: string[], ...args: string[]): string[] { + const endOfArgsMarkerIndex = argv.indexOf('--'); + if (endOfArgsMarkerIndex === -1) { + argv.push(...args); + } else { + // if the we have an argument "--" (end of argument marker) + // we cannot add arguments at the end. rather, we add + // arguments before the "--" marker. + argv.splice(endOfArgsMarkerIndex, 0, ...args); + } + + return argv; +} \ No newline at end of file diff --git a/src/vs/platform/environment/node/argvHelper.ts b/src/vs/platform/environment/node/argvHelper.ts index b31e7659e97..7e0819f77ab 100644 --- a/src/vs/platform/environment/node/argvHelper.ts +++ b/src/vs/platform/environment/node/argvHelper.ts @@ -4,12 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; +import { tmpdir } from 'os'; import { firstIndex } from 'vs/base/common/arrays'; import { localize } from 'vs/nls'; import { ParsedArgs } from '../common/environment'; import { MIN_MAX_MEMORY_SIZE_MB } from 'vs/platform/files/common/files'; import { parseArgs } from 'vs/platform/environment/node/argv'; - +import { join } from 'vs/base/common/path'; +import { writeFile } from 'vs/base/node/pfs'; function validate(args: ParsedArgs): ParsedArgs { if (args.goto) { @@ -57,4 +59,22 @@ export function parseCLIProcessArgv(processArgv: string[]): ParsedArgs { } return validate(parseArgs(args)); -} \ No newline at end of file +} + +export function createWaitMarkerFile(verbose?: boolean): Promise { + const randomWaitMarkerPath = join(tmpdir(), Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 10)); + + return writeFile(randomWaitMarkerPath, '').then(() => { + if (verbose) { + console.log(`Marker file for --wait created: ${randomWaitMarkerPath}`); + } + + return randomWaitMarkerPath; + }, error => { + if (verbose) { + console.error(`Failed to create marker file for --wait: ${error}`); + } + + return Promise.resolve(undefined); + }); +} diff --git a/src/vs/platform/files/common/files.ts b/src/vs/platform/files/common/files.ts index 069e926bb47..399a6001e09 100644 --- a/src/vs/platform/files/common/files.ts +++ b/src/vs/platform/files/common/files.ts @@ -8,68 +8,14 @@ import { URI } from 'vs/base/common/uri'; import * as glob from 'vs/base/common/glob'; import { isLinux } from 'vs/base/common/platform'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { Event, Emitter } from 'vs/base/common/event'; +import { Event } from 'vs/base/common/event'; import { startsWithIgnoreCase } from 'vs/base/common/strings'; -import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; +import { IDisposable } from 'vs/base/common/lifecycle'; import { isEqualOrParent, isEqual } from 'vs/base/common/resources'; import { isUndefinedOrNull } from 'vs/base/common/types'; -import { ThrottledDelayer } from 'vs/base/common/async'; export const IFileService = createDecorator('fileService'); -export class FileListener extends Disposable { - - private readonly _onDidContentChange = new Emitter(); - readonly onDidContentChange: Event = this._onDidContentChange.event; - - private watching: boolean = false; - private delayer: ThrottledDelayer; - private etag: string | undefined; - - constructor( - private readonly file: URI, - private readonly fileService: IFileService - ) { - super(); - this.delayer = new ThrottledDelayer(500); - } - - watch(eTag?: string): void { - if (!this.watching) { - this.etag = eTag; - this.poll(); - this.watching = true; - } - } - - private poll(): void { - const loop = () => this.doWatch().then(() => this.poll()); - this.delayer.trigger(loop); - } - - private doWatch(): Promise { - return this.fileService.resolveFile(this.file) - .then(stat => { - if (stat.etag !== this.etag) { - this.etag = stat.etag; - this._onDidContentChange.fire(stat); - } - }); - } - - unwatch(): void { - if (this.watching) { - this.delayer.cancel(); - this.watching = false; - } - } - - dispose(): void { - this.unwatch(); - super.dispose(); - } -} - export interface IResourceEncodings { getWriteEncoding(resource: URI, preferredEncoding?: string): string; } @@ -509,7 +455,7 @@ export interface IFileStat extends IBaseStat { } export interface IResolveFileResult { - stat: IFileStat; + stat?: IFileStat; success: boolean; } diff --git a/src/vs/platform/instantiation/common/instantiationService.ts b/src/vs/platform/instantiation/common/instantiationService.ts index 231666949c4..a0141e4853f 100644 --- a/src/vs/platform/instantiation/common/instantiationService.ts +++ b/src/vs/platform/instantiation/common/instantiationService.ts @@ -9,10 +9,17 @@ import { Graph } from 'vs/platform/instantiation/common/graph'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { ServiceIdentifier, IInstantiationService, ServicesAccessor, _util, optional } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { IdleValue } from 'vs/base/common/async'; // TRACING const _enableTracing = false; +// PROXY +// Ghetto-declare of the global Proxy object. This isn't the proper way +// but allows us to run this code in the browser without IE11. +declare var Proxy: any; +const _canUseProxy = typeof Proxy === 'function'; + export class InstantiationService implements IInstantiationService { _serviceBrand: any; @@ -205,8 +212,26 @@ export class InstantiationService implements IInstantiationService { } } - protected _createServiceInstance(ctor: any, args: any[] = [], _supportsDelayedInstantiation: boolean, _trace: Trace): T { - return this._createInstance(ctor, args, _trace); + private _createServiceInstance(ctor: any, args: any[] = [], _supportsDelayedInstantiation: boolean, _trace: Trace): T { + if (!_supportsDelayedInstantiation || !_canUseProxy) { + // eager instantiation or no support JS proxies (e.g. IE11) + return this._createInstance(ctor, args, _trace); + + } else { + // Return a proxy object that's backed by an idle value. That + // strategy is to instantiate services in our idle time or when actually + // needed but not when injected into a consumer + const idle = new IdleValue(() => this._createInstance(ctor, args, _trace)); + return new Proxy(Object.create(null), { + get(_target: T, prop: PropertyKey): any { + return idle.getValue()[prop]; + }, + set(_target: T, p: PropertyKey, value: any): boolean { + idle.getValue()[p] = value; + return true; + } + }); + } } } diff --git a/src/vs/platform/instantiation/node/instantiationService.ts b/src/vs/platform/instantiation/node/instantiationService.ts deleted file mode 100644 index c888491b365..00000000000 --- a/src/vs/platform/instantiation/node/instantiationService.ts +++ /dev/null @@ -1,39 +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 { IdleValue } from 'vs/base/common/async'; -import { InstantiationService as BaseInstantiationService } from 'vs/platform/instantiation/common/instantiationService'; -import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; - -// this is in the /node/-layer because it depends on Proxy which isn't available -// in IE11 and therefore not in the /common/-layer - -export class InstantiationService extends BaseInstantiationService { - - createChild(services: ServiceCollection): IInstantiationService { - return new InstantiationService(services, this._strict, this); - } - - protected _createServiceInstance(ctor: any, args: any[] = [], supportsDelayedInstantiation: boolean, _trace): T { - if (supportsDelayedInstantiation) { - return InstantiationService._newIdleProxyService(() => super._createServiceInstance(ctor, args, supportsDelayedInstantiation, _trace)); - } else { - return super._createServiceInstance(ctor, args, supportsDelayedInstantiation, _trace); - } - } - - private static _newIdleProxyService(executor: () => T): T { - const idle = new IdleValue(executor); - return new Proxy(Object.create(null), { - get(_target: T, prop: PropertyKey): any { - return idle.getValue()[prop]; - }, - set(_target: T, p: PropertyKey, value: any): boolean { - idle.getValue()[p] = value; - return true; - } - }); - } -} diff --git a/src/vs/platform/jsonschemas/common/jsonContributionRegistry.ts b/src/vs/platform/jsonschemas/common/jsonContributionRegistry.ts index 3206c7ba3de..972566d2b48 100644 --- a/src/vs/platform/jsonschemas/common/jsonContributionRegistry.ts +++ b/src/vs/platform/jsonschemas/common/jsonContributionRegistry.ts @@ -12,7 +12,7 @@ export const Extensions = { }; export interface ISchemaContributions { - schemas?: { [id: string]: IJSONSchema }; + schemas: { [id: string]: IJSONSchema }; } export interface IJSONContributionRegistry { diff --git a/src/vs/platform/keybinding/common/keybindingsRegistry.ts b/src/vs/platform/keybinding/common/keybindingsRegistry.ts index bfa00350ebd..145508abb2a 100644 --- a/src/vs/platform/keybinding/common/keybindingsRegistry.ts +++ b/src/vs/platform/keybinding/common/keybindingsRegistry.ts @@ -19,7 +19,7 @@ export interface IKeybindingItem { } export interface IKeybindings { - primary: number; + primary?: number; secondary?: number[]; win?: { primary: number; diff --git a/src/vs/platform/lifecycle/common/lifecycle.ts b/src/vs/platform/lifecycle/common/lifecycle.ts index 4879ee13e8d..18782a0d0b3 100644 --- a/src/vs/platform/lifecycle/common/lifecycle.ts +++ b/src/vs/platform/lifecycle/common/lifecycle.ts @@ -133,7 +133,7 @@ export interface ILifecycleService { /** * A flag indicating in what phase of the lifecycle we currently are. */ - readonly phase: LifecyclePhase; + phase: LifecyclePhase; /** * Fired before shutdown happens. Allows listeners to veto against the diff --git a/src/vs/platform/lifecycle/electron-browser/lifecycleService.ts b/src/vs/platform/lifecycle/electron-browser/lifecycleService.ts index d073ecdeb60..7e74fc62cde 100644 --- a/src/vs/platform/lifecycle/electron-browser/lifecycleService.ts +++ b/src/vs/platform/lifecycle/electron-browser/lifecycleService.ts @@ -57,7 +57,7 @@ export class LifecycleService extends Disposable implements ILifecycleService { } private resolveStartupKind(): StartupKind { - const lastShutdownReason = this.storageService.getInteger(LifecycleService.LAST_SHUTDOWN_REASON_KEY, StorageScope.WORKSPACE); + const lastShutdownReason = this.storageService.getNumber(LifecycleService.LAST_SHUTDOWN_REASON_KEY, StorageScope.WORKSPACE); this.storageService.remove(LifecycleService.LAST_SHUTDOWN_REASON_KEY, StorageScope.WORKSPACE); let startupKind: StartupKind; diff --git a/src/vs/platform/menubar/common/menubar.ts b/src/vs/platform/menubar/common/menubar.ts index 84ceaf14ec2..27294cbb3aa 100644 --- a/src/vs/platform/menubar/common/menubar.ts +++ b/src/vs/platform/menubar/common/menubar.ts @@ -25,7 +25,7 @@ export interface IMenubarMenu { export interface IMenubarKeybinding { label: string; - userSettingsLabel: string; + userSettingsLabel?: string; isNative?: boolean; // Assumed true if missing } diff --git a/src/vs/platform/storage/common/storage.ts b/src/vs/platform/storage/common/storage.ts index 13089daf59f..1861da449bd 100644 --- a/src/vs/platform/storage/common/storage.ts +++ b/src/vs/platform/storage/common/storage.ts @@ -67,8 +67,8 @@ export interface IStorageService { * The scope argument allows to define the scope of the storage * operation to either the current workspace only or all workspaces. */ - getInteger(key: string, scope: StorageScope, fallbackValue: number): number; - getInteger(key: string, scope: StorageScope, fallbackValue?: number): number | undefined; + getNumber(key: string, scope: StorageScope, fallbackValue: number): number; + getNumber(key: string, scope: StorageScope, fallbackValue?: number): number | undefined; /** * Store a value under the given key to storage. The value will be converted to a string. @@ -142,8 +142,8 @@ export class InMemoryStorageService extends Disposable implements IStorageServic return value === 'true'; } - getInteger(key: string, scope: StorageScope, fallbackValue: number): number; - getInteger(key: string, scope: StorageScope, fallbackValue?: number): number | undefined { + getNumber(key: string, scope: StorageScope, fallbackValue: number): number; + getNumber(key: string, scope: StorageScope, fallbackValue?: number): number | undefined { const value = this.getCache(scope).get(key); if (isUndefinedOrNull(value)) { diff --git a/src/vs/platform/storage/node/storageMainService.ts b/src/vs/platform/storage/node/storageMainService.ts index fdd88608429..eacff97fbc1 100644 --- a/src/vs/platform/storage/node/storageMainService.ts +++ b/src/vs/platform/storage/node/storageMainService.ts @@ -52,8 +52,8 @@ export interface IStorageMainService { * the provided defaultValue if the element is null or undefined. The element * will be converted to a number using parseInt with a base of 10. */ - getInteger(key: string, fallbackValue: number): number; - getInteger(key: string, fallbackValue?: number): number | undefined; + getNumber(key: string, fallbackValue: number): number; + getNumber(key: string, fallbackValue?: number): number | undefined; /** * Store a string value under the given key to storage. The value will @@ -362,10 +362,10 @@ export class StorageMainService extends Disposable implements IStorageMainServic return this.storage.getBoolean(key, fallbackValue); } - getInteger(key: string, fallbackValue: number): number; - getInteger(key: string, fallbackValue?: number): number | undefined; - getInteger(key: string, fallbackValue?: number): number | undefined { - return this.storage.getInteger(key, fallbackValue); + getNumber(key: string, fallbackValue: number): number; + getNumber(key: string, fallbackValue?: number): number | undefined; + getNumber(key: string, fallbackValue?: number): number | undefined { + return this.storage.getNumber(key, fallbackValue); } store(key: string, value: any): Promise { diff --git a/src/vs/platform/storage/node/storageService.ts b/src/vs/platform/storage/node/storageService.ts index 7e05b32576d..06870260f32 100644 --- a/src/vs/platform/storage/node/storageService.ts +++ b/src/vs/platform/storage/node/storageService.ts @@ -170,10 +170,10 @@ export class StorageService extends Disposable implements IStorageService { return this.getStorage(scope).getBoolean(key, fallbackValue); } - getInteger(key: string, scope: StorageScope, fallbackValue: number): number; - getInteger(key: string, scope: StorageScope): number | undefined; - getInteger(key: string, scope: StorageScope, fallbackValue?: number): number | undefined { - return this.getStorage(scope).getInteger(key, fallbackValue); + getNumber(key: string, scope: StorageScope, fallbackValue: number): number; + getNumber(key: string, scope: StorageScope): number | undefined; + getNumber(key: string, scope: StorageScope, fallbackValue?: number): number | undefined { + return this.getStorage(scope).getNumber(key, fallbackValue); } store(key: string, value: string | boolean | number, scope: StorageScope): void { diff --git a/src/vs/platform/storage/test/node/storageService.test.ts b/src/vs/platform/storage/test/node/storageService.test.ts index 634967a79a7..91478086ce4 100644 --- a/src/vs/platform/storage/test/node/storageService.test.ts +++ b/src/vs/platform/storage/test/node/storageService.test.ts @@ -48,8 +48,8 @@ suite('StorageService', () => { strictEqual(storage.get('Monaco.IDE.Core.Storage.Test.get', scope, 'foobar'), 'foobar'); strictEqual(storage.get('Monaco.IDE.Core.Storage.Test.get', scope, ''), ''); - strictEqual(storage.getInteger('Monaco.IDE.Core.Storage.Test.getInteger', scope, 5), 5); - strictEqual(storage.getInteger('Monaco.IDE.Core.Storage.Test.getInteger', scope, 0), 0); + strictEqual(storage.getNumber('Monaco.IDE.Core.Storage.Test.getNumber', scope, 5), 5); + strictEqual(storage.getNumber('Monaco.IDE.Core.Storage.Test.getNumber', scope, 0), 0); strictEqual(storage.getBoolean('Monaco.IDE.Core.Storage.Test.getBoolean', scope, true), true); strictEqual(storage.getBoolean('Monaco.IDE.Core.Storage.Test.getBoolean', scope, false), false); @@ -59,11 +59,11 @@ suite('StorageService', () => { storage.store('Monaco.IDE.Core.Storage.Test.get', '', scope); strictEqual(storage.get('Monaco.IDE.Core.Storage.Test.get', scope, (undefined)!), ''); - storage.store('Monaco.IDE.Core.Storage.Test.getInteger', 5, scope); - strictEqual(storage.getInteger('Monaco.IDE.Core.Storage.Test.getInteger', scope, (undefined)!), 5); + storage.store('Monaco.IDE.Core.Storage.Test.getNumber', 5, scope); + strictEqual(storage.getNumber('Monaco.IDE.Core.Storage.Test.getNumber', scope, (undefined)!), 5); - storage.store('Monaco.IDE.Core.Storage.Test.getInteger', 0, scope); - strictEqual(storage.getInteger('Monaco.IDE.Core.Storage.Test.getInteger', scope, (undefined)!), 0); + storage.store('Monaco.IDE.Core.Storage.Test.getNumber', 0, scope); + strictEqual(storage.getNumber('Monaco.IDE.Core.Storage.Test.getNumber', scope, (undefined)!), 0); storage.store('Monaco.IDE.Core.Storage.Test.getBoolean', true, scope); strictEqual(storage.getBoolean('Monaco.IDE.Core.Storage.Test.getBoolean', scope, (undefined)!), true); @@ -72,7 +72,7 @@ suite('StorageService', () => { strictEqual(storage.getBoolean('Monaco.IDE.Core.Storage.Test.getBoolean', scope, (undefined)!), false); strictEqual(storage.get('Monaco.IDE.Core.Storage.Test.getDefault', scope, 'getDefault'), 'getDefault'); - strictEqual(storage.getInteger('Monaco.IDE.Core.Storage.Test.getIntegerDefault', scope, 5), 5); + strictEqual(storage.getNumber('Monaco.IDE.Core.Storage.Test.getNumberDefault', scope, 5), 5); strictEqual(storage.getBoolean('Monaco.IDE.Core.Storage.Test.getBooleanDefault', scope, true), true); } @@ -111,7 +111,7 @@ suite('StorageService', () => { await storage.migrate({ id: String(Date.now() + 100) }); equal(storage.get('bar', StorageScope.WORKSPACE), 'foo'); - equal(storage.getInteger('barNumber', StorageScope.WORKSPACE), 55); + equal(storage.getNumber('barNumber', StorageScope.WORKSPACE), 55); equal(storage.getBoolean('barBoolean', StorageScope.GLOBAL), true); await storage.close(); diff --git a/src/vs/platform/theme/common/colorRegistry.ts b/src/vs/platform/theme/common/colorRegistry.ts index 0f0914915ba..6a74d3719ec 100644 --- a/src/vs/platform/theme/common/colorRegistry.ts +++ b/src/vs/platform/theme/common/colorRegistry.ts @@ -227,7 +227,7 @@ export const listActiveSelectionBackground = registerColor('list.activeSelection 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.")); -export const listInactiveFocusBackground = registerColor('list.inactiveFocusBackground', { dark: '#313135', light: '#d8dae6', hc: null }, nls.localize('listInactiveFocusBackground', "List/Tree background color for the focused item when the list/tree is inactive. An active list/tree has keyboard focus, an inactive does not.")); +export const listInactiveFocusBackground = registerColor('list.inactiveFocusBackground', { dark: null, light: null, hc: null }, nls.localize('listInactiveFocusBackground', "List/Tree background color for the focused item when the list/tree is inactive. An active list/tree has keyboard focus, an inactive does not.")); export const listHoverBackground = registerColor('list.hoverBackground', { dark: '#2A2D2E', light: '#F0F0F0', hc: null }, nls.localize('listHoverBackground', "List/Tree background when hovering over items using the mouse.")); export const listHoverForeground = registerColor('list.hoverForeground', { dark: null, light: null, hc: null }, nls.localize('listHoverForeground', "List/Tree foreground when hovering over items using the mouse.")); export const listDropBackground = registerColor('list.dropBackground', { dark: listFocusBackground, light: listFocusBackground, hc: null }, nls.localize('listDropBackground', "List/Tree drag and drop background when moving items around using the mouse.")); diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index a39720ec134..1be5b5e4a63 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -155,7 +155,7 @@ export interface IWindowsService { getWindows(): Promise<{ id: number; workspace?: IWorkspaceIdentifier; folderUri?: ISingleFolderWorkspaceIdentifier; title: string; filename?: string; }[]>; getWindowCount(): Promise; log(severity: string, ...messages: string[]): Promise; - showItemInFolder(path: string): Promise; + showItemInFolder(path: URI): Promise; getActiveWindowId(): Promise; // This needs to be handled from browser process to prevent diff --git a/src/vs/platform/windows/electron-main/windowsService.ts b/src/vs/platform/windows/electron-main/windowsService.ts index 9223005e842..d832fe25a4a 100644 --- a/src/vs/platform/windows/electron-main/windowsService.ts +++ b/src/vs/platform/windows/electron-main/windowsService.ts @@ -326,10 +326,12 @@ export class WindowsService implements IWindowsService, IURLHandler, IDisposable console[severity].apply(console, ...messages); } - async showItemInFolder(path: string): Promise { + async showItemInFolder(path: URI): Promise { this.logService.trace('windowsService#showItemInFolder'); - shell.showItemInFolder(path); + if (path.scheme === Schemas.file) { + shell.showItemInFolder(path.fsPath); + } } async getActiveWindowId(): Promise { diff --git a/src/vs/platform/windows/node/windowsIpc.ts b/src/vs/platform/windows/node/windowsIpc.ts index cece368e7a0..81314da6c66 100644 --- a/src/vs/platform/windows/node/windowsIpc.ts +++ b/src/vs/platform/windows/node/windowsIpc.ts @@ -95,7 +95,7 @@ export class WindowsChannel implements IServerChannel { case 'toggleSharedProcess': return this.service.toggleSharedProcess(); case 'quit': return this.service.quit(); case 'log': return this.service.log(arg[0], arg[1]); - case 'showItemInFolder': return this.service.showItemInFolder(arg); + case 'showItemInFolder': return this.service.showItemInFolder(URI.revive(arg)); case 'getActiveWindowId': return this.service.getActiveWindowId(); case 'openExternal': return this.service.openExternal(arg); case 'startCrashReporter': return this.service.startCrashReporter(arg); @@ -309,7 +309,7 @@ export class WindowsChannelClient implements IWindowsService { return this.channel.call('log', [severity, messages]); } - showItemInFolder(path: string): Promise { + showItemInFolder(path: URI): Promise { return this.channel.call('showItemInFolder', path); } diff --git a/src/vs/platform/workspaces/common/workspaces.ts b/src/vs/platform/workspaces/common/workspaces.ts index b72fe85d629..0f745aea0da 100644 --- a/src/vs/platform/workspaces/common/workspaces.ts +++ b/src/vs/platform/workspaces/common/workspaces.ts @@ -70,6 +70,7 @@ export type IStoredWorkspaceFolder = IRawFileWorkspaceFolder | IRawUriWorkspaceF export interface IResolvedWorkspace extends IWorkspaceIdentifier { folders: IWorkspaceFolder[]; + remoteAuthority?: string; } export interface IStoredWorkspace { @@ -112,7 +113,7 @@ export interface IWorkspacesMainService extends IWorkspacesService { export interface IWorkspacesService { _serviceBrand: any; - createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[]): Promise; + createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[], remoteAuthority?: string): Promise; deleteUntitledWorkspace(workspace: IWorkspaceIdentifier): Promise; } diff --git a/src/vs/platform/workspaces/electron-main/workspacesMainService.ts b/src/vs/platform/workspaces/electron-main/workspacesMainService.ts index d0d80e3b846..b257f0720c5 100644 --- a/src/vs/platform/workspaces/electron-main/workspacesMainService.ts +++ b/src/vs/platform/workspaces/electron-main/workspacesMainService.ts @@ -19,10 +19,10 @@ import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; import { Disposable } from 'vs/base/common/lifecycle'; import { originalFSPath, dirname as resourcesDirname, isEqualOrParent, joinPath } from 'vs/base/common/resources'; -import { getRemoteAuthority } from 'vs/platform/remote/common/remoteHosts'; export interface IStoredWorkspace { folders: IStoredWorkspaceFolder[]; + remoteAuthority?: string; } export class WorkspacesMainService extends Disposable implements IWorkspacesMainService { @@ -72,7 +72,8 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain return { id: workspaceIdentifier.id, configPath: workspaceIdentifier.configPath, - folders: toWorkspaceFolders(workspace.folders, resourcesDirname(path)) + folders: toWorkspaceFolders(workspace.folders, resourcesDirname(path)), + remoteAuthority: workspace.remoteAuthority }; } catch (error) { this.logService.warn(error.toString()); @@ -103,8 +104,8 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain return isEqualOrParent(path, this.environmentService.untitledWorkspacesHome); } - createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[]): Promise { - const { workspace, storedWorkspace } = this.newUntitledWorkspace(folders); + createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[], remoteAuthority?: string): Promise { + const { workspace, storedWorkspace } = this.newUntitledWorkspace(folders, remoteAuthority); const configPath = workspace.configPath.fsPath; return mkdirp(dirname(configPath)).then(() => { @@ -112,8 +113,8 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain }); } - createUntitledWorkspaceSync(folders?: IWorkspaceFolderCreationData[]): IWorkspaceIdentifier { - const { workspace, storedWorkspace } = this.newUntitledWorkspace(folders); + createUntitledWorkspaceSync(folders?: IWorkspaceFolderCreationData[], remoteAuthority?: string): IWorkspaceIdentifier { + const { workspace, storedWorkspace } = this.newUntitledWorkspace(folders, remoteAuthority); const configPath = workspace.configPath.fsPath; const configPathDir = dirname(configPath); @@ -130,7 +131,7 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain return workspace; } - private newUntitledWorkspace(folders: IWorkspaceFolderCreationData[] = []): { workspace: IWorkspaceIdentifier, storedWorkspace: IStoredWorkspace } { + private newUntitledWorkspace(folders: IWorkspaceFolderCreationData[] = [], remoteAuthority?: string): { workspace: IWorkspaceIdentifier, storedWorkspace: IStoredWorkspace } { const randomId = (Date.now() + Math.round(Math.random() * 1000)).toString(); const untitledWorkspaceConfigFolder = joinPath(this.untitledWorkspacesHome, randomId); const untitledWorkspaceConfigPath = joinPath(untitledWorkspaceConfigFolder, UNTITLED_WORKSPACE_NAME); @@ -143,7 +144,7 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain return { workspace: this.getWorkspaceIdentifier(untitledWorkspaceConfigPath), - storedWorkspace: { folders: storedWorkspaceFolder } + storedWorkspace: { folders: storedWorkspaceFolder, remoteAuthority } }; } @@ -210,8 +211,7 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain if (!resolvedWorkspace) { this.doDeleteUntitledWorkspaceSync(workspace); } else { - const remoteAuthority = resolvedWorkspace.folders.length ? getRemoteAuthority(resolvedWorkspace.folders[0].uri) : undefined; - untitledWorkspaces.push({ workspace, remoteAuthority }); + untitledWorkspaces.push({ workspace, remoteAuthority: resolvedWorkspace.remoteAuthority }); } } } catch (error) { diff --git a/src/vs/platform/workspaces/node/workspacesIpc.ts b/src/vs/platform/workspaces/node/workspacesIpc.ts index 6d1af4e5457..ba831722e79 100644 --- a/src/vs/platform/workspaces/node/workspacesIpc.ts +++ b/src/vs/platform/workspaces/node/workspacesIpc.ts @@ -19,7 +19,8 @@ export class WorkspacesChannel implements IServerChannel { call(_, command: string, arg?: any): Promise { switch (command) { case 'createUntitledWorkspace': { - const rawFolders: IWorkspaceFolderCreationData[] = arg; + const rawFolders: IWorkspaceFolderCreationData[] = arg[0]; + const remoteAuthority: string = arg[1]; let folders: IWorkspaceFolderCreationData[] | undefined = undefined; if (Array.isArray(rawFolders)) { folders = rawFolders.map(rawFolder => { @@ -30,7 +31,7 @@ export class WorkspacesChannel implements IServerChannel { }); } - return this.service.createUntitledWorkspace(folders); + return this.service.createUntitledWorkspace(folders, remoteAuthority); } case 'deleteUntitledWorkspace': { const w: IWorkspaceIdentifier = arg; @@ -48,8 +49,8 @@ export class WorkspacesChannelClient implements IWorkspacesService { constructor(private channel: IChannel) { } - createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[]): Promise { - return this.channel.call('createUntitledWorkspace', folders).then(reviveWorkspaceIdentifier); + createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[], remoteAuthority?: string): Promise { + return this.channel.call('createUntitledWorkspace', [folders, remoteAuthority]).then(reviveWorkspaceIdentifier); } deleteUntitledWorkspace(workspaceIdentifier: IWorkspaceIdentifier): Promise { diff --git a/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts b/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts index 272b810d1bf..a74f525c7f3 100644 --- a/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts +++ b/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts @@ -113,7 +113,7 @@ suite('WorkspacesMainService', () => { const folder1URI = URI.parse('myscheme://server/work/p/f1'); const folder2URI = URI.parse('myscheme://server/work/o/f3'); - return service.createUntitledWorkspace([{ uri: folder1URI }, { uri: folder2URI }]).then(workspace => { + return service.createUntitledWorkspace([{ uri: folder1URI }, { uri: folder2URI }], 'server').then(workspace => { assert.ok(workspace); assert.ok(fs.existsSync(workspace.configPath.fsPath)); assert.ok(service.isUntitledWorkspace(workspace)); @@ -125,6 +125,8 @@ suite('WorkspacesMainService', () => { assert.ok(!(ws.folders[0]).name); assert.ok(!(ws.folders[1]).name); + + assert.equal(ws.remoteAuthority, 'server'); }); }); diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index a00d67ec56b..1b773f2da31 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -641,22 +641,13 @@ declare module 'vscode' { /** * The size in spaces a tab takes. This is used for two purposes: * - the rendering width of a tab character; - * - the number of spaces to insert when [insertSpaces](#TextEditorOptions.insertSpaces) is true - * and `indentSize` is set to `"tab"`. + * - the number of spaces to insert when [insertSpaces](#TextEditorOptions.insertSpaces) is true. * * When getting a text editor's options, this property will always be a number (resolved). * When setting a text editor's options, this property is optional and it can be a number or `"auto"`. */ tabSize?: number | string; - /** - * The number of spaces to insert when [insertSpaces](#TextEditorOptions.insertSpaces) is true. - * - * When getting a text editor's options, this property will always be a number (resolved). - * When setting a text editor's options, this property is optional and it can be a number or `"tabSize"`. - */ - indentSize?: number | string; - /** * When pressing Tab insert [n](#TextEditorOptions.tabSize) spaces. * When getting a text editor's options, this property will always be a boolean (resolved). @@ -1244,11 +1235,16 @@ declare module 'vscode' { * Create an URI from a string, e.g. `http://www.msft.com/some/path`, * `file:///usr/home`, or `scheme:with/path`. * + * *Note* that for a while uris without a `scheme` were accepted. That is not correct + * as all uris should have a scheme. To avoid breakage of existing code the optional + * `strict`-argument has been added. We *strongly* advise to use it, e.g. `Uri.parse('my:uri', true)` + * * @see [Uri.toString](#Uri.toString) * @param value The string value of an Uri. + * @param strict Throw an error when `value` is empty or when no `scheme` can be parsed. * @return A new Uri instance. */ - static parse(value: string): Uri; + static parse(value: string, strict?: boolean): Uri; /** * Create an URI from a file system path. The [scheme](#Uri.scheme) @@ -3710,7 +3706,7 @@ declare module 'vscode' { } /** - * A line based folding range. To be valid, start and end line must a zero or larger and smaller than the number of lines in the document. + * A line based folding range. To be valid, start and end line must be bigger than zero and smaller than the number of lines in the document. * Invalid ranges will be ignored. */ export class FoldingRange { diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts index 03232b7c297..07aabc3588e 100644 --- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts @@ -377,7 +377,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution { const viewDescriptor = { id: item.id, name: item.name, - ctor: CustomTreeViewPanel, + ctorDescriptor: { ctor: CustomTreeViewPanel }, when: ContextKeyExpr.deserialize(item.when), canToggleVisibility: true, collapsed: this.showCollapsed(container), diff --git a/src/vs/workbench/api/electron-browser/mainThreadCommands.ts b/src/vs/workbench/api/electron-browser/mainThreadCommands.ts index 061034039c1..4e8d426c589 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadCommands.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadCommands.ts @@ -89,7 +89,7 @@ function _generateMarkdown(description: string | ICommandHandlerDescription): st if (typeof description === 'string') { return description; } else { - let parts = [description.description]; + const parts = [description.description]; parts.push('\n\n'); if (description.args) { for (let arg of description.args) { diff --git a/src/vs/workbench/api/electron-browser/mainThreadComments.ts b/src/vs/workbench/api/electron-browser/mainThreadComments.ts index ebbe7023a9a..0c50eeb40ea 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadComments.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadComments.ts @@ -26,9 +26,9 @@ import { IRange } from 'vs/editor/common/core/range'; import { Emitter, Event } from 'vs/base/common/event'; export class MainThreadDocumentCommentProvider implements modes.DocumentCommentProvider { - private _proxy: ExtHostCommentsShape; - private _handle: number; - private _features: CommentProviderFeatures; + private readonly _proxy: ExtHostCommentsShape; + private readonly _handle: number; + private readonly _features: CommentProviderFeatures; get startDraftLabel(): string | undefined { return this._features.startDraftLabel; } get deleteDraftLabel(): string | undefined { return this._features.deleteDraftLabel; } get finishDraftLabel(): string | undefined { return this._features.finishDraftLabel; } @@ -197,14 +197,14 @@ export class MainThreadCommentControl { return this._label; } - private _threads: Map = new Map(); - private _commentingRanges: Map = new Map(); + private readonly _threads: Map = new Map(); + private readonly _commentingRanges: Map = new Map(); constructor( - private _proxy: ExtHostCommentsShape, - private _commentService: ICommentService, - private _handle: number, - private _id: string, - private _label: string + private readonly _proxy: ExtHostCommentsShape, + private readonly _commentService: ICommentService, + private readonly _handle: number, + private readonly _id: string, + private readonly _label: string ) { } createCommentThread(commentThreadHandle: number, threadId: string, resource: UriComponents, range: IRange, comments: modes.Comment[], commands: modes.Command[], collapseState: modes.CommentThreadCollapsibleState): modes.CommentThread2 { @@ -331,7 +331,7 @@ export class MainThreadCommentControl { export class MainThreadComments extends Disposable implements MainThreadCommentsShape { private _disposables: IDisposable[]; private _activeCommentThreadDisposables: IDisposable[]; - private _proxy: ExtHostCommentsShape; + private readonly _proxy: ExtHostCommentsShape; private _documentProviders = new Map(); private _workspaceProviders = new Map(); private _handlers = new Map(); diff --git a/src/vs/workbench/api/electron-browser/mainThreadConfiguration.ts b/src/vs/workbench/api/electron-browser/mainThreadConfiguration.ts index 495b36864ad..4947445ffe3 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadConfiguration.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadConfiguration.ts @@ -8,10 +8,9 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope, getScopes } from 'vs/platform/configuration/common/configurationRegistry'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; -import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { MainThreadConfigurationShape, MainContext, ExtHostContext, IExtHostContext, IWorkspaceConfigurationChangeEventData, IConfigurationInitData } from '../node/extHost.protocol'; import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; -import { ConfigurationTarget, IConfigurationChangeEvent, IConfigurationModel } from 'vs/platform/configuration/common/configuration'; +import { ConfigurationTarget, IConfigurationChangeEvent, IConfigurationModel, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; @extHostNamedCustomer(MainContext.MainThreadConfiguration) @@ -22,7 +21,7 @@ export class MainThreadConfiguration implements MainThreadConfigurationShape { constructor( extHostContext: IExtHostContext, @IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService, - @IWorkspaceConfigurationService private readonly configurationService: IWorkspaceConfigurationService, + @IConfigurationService private readonly configurationService: IConfigurationService, @IEnvironmentService private readonly _environmentService: IEnvironmentService, ) { const proxy = extHostContext.getProxy(ExtHostContext.ExtHostConfiguration); diff --git a/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts b/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts index 395c6b87b0e..fe7631d0a0e 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts @@ -19,15 +19,15 @@ import { convertToVSCPaths, convertToDAPaths } from 'vs/workbench/contrib/debug/ @extHostNamedCustomer(MainContext.MainThreadDebugService) export class MainThreadDebugService implements MainThreadDebugServiceShape, IDebugAdapterFactory { - private _proxy: ExtHostDebugServiceShape; + private readonly _proxy: ExtHostDebugServiceShape; private _toDispose: IDisposable[]; private _breakpointEventsActive: boolean; - private _debugAdapters: Map; + private readonly _debugAdapters: Map; private _debugAdaptersHandleCounter = 1; - private _debugConfigurationProviders: Map; - private _debugAdapterDescriptorFactories: Map; - private _debugAdapterTrackerFactories: Map; - private _sessions: Set; + private readonly _debugConfigurationProviders: Map; + private readonly _debugAdapterDescriptorFactories: Map; + private readonly _debugAdapterTrackerFactories: Map; + private readonly _sessions: Set; constructor( extHostContext: IExtHostContext, @@ -343,7 +343,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb */ class ExtensionHostDebugAdapter extends AbstractDebugAdapter { - constructor(private _ds: MainThreadDebugService, private _handle: number, private _proxy: ExtHostDebugServiceShape, private _session: IDebugSession) { + constructor(private readonly _ds: MainThreadDebugService, private _handle: number, private _proxy: ExtHostDebugServiceShape, private _session: IDebugSession) { super(); } diff --git a/src/vs/workbench/api/electron-browser/mainThreadDecorations.ts b/src/vs/workbench/api/electron-browser/mainThreadDecorations.ts index 8847d97283a..84281c3a6c2 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadDecorations.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadDecorations.ts @@ -21,7 +21,7 @@ class DecorationRequestsQueue { private _timer: any; constructor( - private _proxy: ExtHostDecorationsShape + private readonly _proxy: ExtHostDecorationsShape ) { // } diff --git a/src/vs/workbench/api/electron-browser/mainThreadDocuments.ts b/src/vs/workbench/api/electron-browser/mainThreadDocuments.ts index 7780708d837..651d521c3a8 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadDocuments.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadDocuments.ts @@ -24,8 +24,8 @@ export class BoundModelReferenceCollection { private _length = 0; constructor( - private _maxAge: number = 1000 * 60 * 3, - private _maxLength: number = 1024 * 1024 * 80 + private readonly _maxAge: number = 1000 * 60 * 3, + private readonly _maxLength: number = 1024 * 1024 * 80 ) { // } @@ -35,11 +35,11 @@ export class BoundModelReferenceCollection { } add(ref: IReference): void { - let length = ref.object.textEditorModel.getValueLength(); + const length = ref.object.textEditorModel.getValueLength(); let handle: any; let entry: { length: number, dispose(): void }; const dispose = () => { - let idx = this._data.indexOf(entry); + const idx = this._data.indexOf(entry); if (idx >= 0) { this._length -= length; ref.dispose(); @@ -64,16 +64,16 @@ export class BoundModelReferenceCollection { export class MainThreadDocuments implements MainThreadDocumentsShape { - private _modelService: IModelService; - private _textModelResolverService: ITextModelService; - private _textFileService: ITextFileService; - private _fileService: IFileService; - private _untitledEditorService: IUntitledEditorService; + private readonly _modelService: IModelService; + private readonly _textModelResolverService: ITextModelService; + private readonly _textFileService: ITextFileService; + private readonly _fileService: IFileService; + private readonly _untitledEditorService: IUntitledEditorService; private _toDispose: IDisposable[]; private _modelToDisposeMap: { [modelUrl: string]: IDisposable; }; - private _proxy: ExtHostDocumentsShape; - private _modelIsSynced: { [modelId: string]: boolean; }; + private readonly _proxy: ExtHostDocumentsShape; + private readonly _modelIsSynced: { [modelId: string]: boolean; }; private _modelReferenceCollection = new BoundModelReferenceCollection(); constructor( @@ -139,7 +139,7 @@ export class MainThreadDocuments implements MainThreadDocumentsShape { // don't synchronize too large models return; } - let modelUrl = model.uri; + const modelUrl = model.uri; this._modelIsSynced[modelUrl.toString()] = true; this._modelToDisposeMap[modelUrl.toString()] = model.onDidChangeContent((e) => { this._proxy.$acceptModelChanged(modelUrl, e, this._textFileService.isDirty(modelUrl)); @@ -148,7 +148,7 @@ export class MainThreadDocuments implements MainThreadDocumentsShape { private _onModelModeChanged(event: { model: ITextModel; oldModeId: string; }): void { let { model, oldModeId } = event; - let modelUrl = model.uri; + const modelUrl = model.uri; if (!this._modelIsSynced[modelUrl.toString()]) { return; } @@ -156,7 +156,7 @@ export class MainThreadDocuments implements MainThreadDocumentsShape { } private _onModelRemoved(modelUrl: URI): void { - let strModelUrl = modelUrl.toString(); + const strModelUrl = modelUrl.toString(); if (!this._modelIsSynced[strModelUrl]) { return; } @@ -214,7 +214,7 @@ export class MainThreadDocuments implements MainThreadDocumentsShape { } private _handleUntitledScheme(uri: URI): Promise { - let asFileUri = uri.with({ scheme: Schemas.file }); + const asFileUri = uri.with({ scheme: Schemas.file }); return this._fileService.resolveFile(asFileUri).then(stats => { // don't create a new file ontop of an existing file return Promise.reject(new Error('file already exists on disk')); diff --git a/src/vs/workbench/api/electron-browser/mainThreadDocumentsAndEditors.ts b/src/vs/workbench/api/electron-browser/mainThreadDocumentsAndEditors.ts index dd73c36be3f..18e432e32de 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadDocumentsAndEditors.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadDocumentsAndEditors.ts @@ -283,7 +283,7 @@ class MainThreadDocumentAndEditorStateComputer { } private _getActiveEditorFromPanel(): IEditor | undefined { - let panel = this._panelService.getActivePanel(); + const panel = this._panelService.getActivePanel(); if (panel instanceof BaseTextEditor && isCodeEditor(panel.getControl())) { return panel.getControl(); } else { @@ -304,8 +304,8 @@ class MainThreadDocumentAndEditorStateComputer { export class MainThreadDocumentsAndEditors { private _toDispose: IDisposable[]; - private _proxy: ExtHostDocumentsAndEditorsShape; - private _stateComputer: MainThreadDocumentAndEditorStateComputer; + private readonly _proxy: ExtHostDocumentsAndEditorsShape; + private readonly _stateComputer: MainThreadDocumentAndEditorStateComputer; private _textEditors = <{ [id: string]: MainThreadTextEditor }>Object.create(null); private _onTextEditorAdd = new Emitter(); @@ -362,8 +362,8 @@ export class MainThreadDocumentsAndEditors { private _onDelta(delta: DocumentAndEditorStateDelta): void { let removedDocuments: URI[]; - let removedEditors: string[] = []; - let addedEditors: MainThreadTextEditor[] = []; + const removedEditors: string[] = []; + const addedEditors: MainThreadTextEditor[] = []; // removed models removedDocuments = delta.removedDocuments.map(m => m.uri); @@ -387,7 +387,7 @@ export class MainThreadDocumentsAndEditors { } } - let extHostDelta: IDocumentsAndEditorsDelta = Object.create(null); + const extHostDelta: IDocumentsAndEditorsDelta = Object.create(null); let empty = true; if (delta.newActiveEditor !== undefined) { empty = false; diff --git a/src/vs/workbench/api/electron-browser/mainThreadEditor.ts b/src/vs/workbench/api/electron-browser/mainThreadEditor.ts index 30639a78a94..1ae5dcb8ee7 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadEditor.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadEditor.ts @@ -105,7 +105,7 @@ export class MainThreadTextEditorProperties { } public generateDelta(oldProps: MainThreadTextEditorProperties | null, selectionChangeSource: string | null): IEditorPropertiesChangeData | null { - let delta: IEditorPropertiesChangeData = { + const delta: IEditorPropertiesChangeData = { options: null, selections: null, visibleRanges: null @@ -181,12 +181,12 @@ export class MainThreadTextEditorProperties { */ export class MainThreadTextEditor { - private _id: string; + private readonly _id: string; private _model: ITextModel; - private _modelService: IModelService; + private readonly _modelService: IModelService; private _modelListeners: IDisposable[]; private _codeEditor: ICodeEditor | null; - private _focusTracker: IFocusTracker; + private readonly _focusTracker: IFocusTracker; private _codeEditorListeners: IDisposable[]; private _properties: MainThreadTextEditorProperties; @@ -323,7 +323,7 @@ export class MainThreadTextEditor { } private _setIndentConfiguration(newConfiguration: ITextEditorConfigurationUpdate): void { - let creationOpts = this._modelService.getCreationOptions(this._model.getLanguageIdentifier().language, this._model.uri, this._model.isForSimpleWidget); + const creationOpts = this._modelService.getCreationOptions(this._model.getLanguageIdentifier().language, this._model.uri, this._model.isForSimpleWidget); if (newConfiguration.tabSize === 'auto' || newConfiguration.insertSpaces === 'auto') { // one of the options was set to 'auto' => detect indentation @@ -342,7 +342,7 @@ export class MainThreadTextEditor { return; } - let newOpts: ITextModelUpdateOptions = {}; + const newOpts: ITextModelUpdateOptions = {}; if (typeof newConfiguration.insertSpaces !== 'undefined') { newOpts.insertSpaces = newConfiguration.insertSpaces; } @@ -367,7 +367,7 @@ export class MainThreadTextEditor { } if (newConfiguration.cursorStyle) { - let newCursorStyle = cursorStyleToString(newConfiguration.cursorStyle); + const newCursorStyle = cursorStyleToString(newConfiguration.cursorStyle); this._codeEditor.updateOptions({ cursorStyle: newCursorStyle }); @@ -402,7 +402,7 @@ export class MainThreadTextEditor { if (!this._codeEditor) { return; } - let ranges: Range[] = []; + const ranges: Range[] = []; for (let i = 0, len = Math.floor(_ranges.length / 4); i < len; i++) { ranges[i] = new Range(_ranges[4 * i], _ranges[4 * i + 1], _ranges[4 * i + 2], _ranges[4 * i + 3]); } @@ -464,7 +464,7 @@ export class MainThreadTextEditor { this._model.pushEOL(EndOfLineSequence.LF); } - let transformedEdits = edits.map((edit): IIdentifiedSingleEditOperation => { + const transformedEdits = edits.map((edit): IIdentifiedSingleEditOperation => { return { range: Range.lift(edit.range), text: edit.text, diff --git a/src/vs/workbench/api/electron-browser/mainThreadEditors.ts b/src/vs/workbench/api/electron-browser/mainThreadEditors.ts index bd1a7845c05..d1d7e1cd812 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadEditors.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadEditors.ts @@ -31,9 +31,9 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape { private static INSTANCE_COUNT: number = 0; - private _instanceId: string; - private _proxy: ExtHostEditorsShape; - private _documentsAndEditors: MainThreadDocumentsAndEditors; + private readonly _instanceId: string; + private readonly _proxy: ExtHostEditorsShape; + private readonly _documentsAndEditors: MainThreadDocumentsAndEditors; private _toDispose: IDisposable[]; private _textEditorsListenersMap: { [editorId: string]: IDisposable[]; }; private _editorPositionData: ITextEditorPositionData | null; @@ -77,8 +77,8 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape { } private _onTextEditorAdd(textEditor: MainThreadTextEditor): void { - let id = textEditor.getId(); - let toDispose: IDisposable[] = []; + const id = textEditor.getId(); + const toDispose: IDisposable[] = []; toDispose.push(textEditor.onPropertiesChanged((data) => { this._proxy.$acceptEditorPropertiesChanged(id, data); })); @@ -94,7 +94,7 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape { private _updateActiveAndVisibleTextEditors(): void { // editor columns - let editorPositionData = this._getTextEditorPositionData(); + const editorPositionData = this._getTextEditorPositionData(); if (!objectEquals(this._editorPositionData, editorPositionData)) { this._editorPositionData = editorPositionData; this._proxy.$acceptEditorPositionData(this._editorPositionData); @@ -102,7 +102,7 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape { } private _getTextEditorPositionData(): ITextEditorPositionData { - let result: ITextEditorPositionData = Object.create(null); + const result: ITextEditorPositionData = Object.create(null); for (let workbenchEditor of this._editorService.visibleControls) { const id = this._documentsAndEditors.findTextEditorIdFor(workbenchEditor); if (id) { @@ -137,9 +137,9 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape { } $tryShowEditor(id: string, position?: EditorViewColumn): Promise { - let mainThreadEditor = this._documentsAndEditors.getEditor(id); + const mainThreadEditor = this._documentsAndEditors.getEditor(id); if (mainThreadEditor) { - let model = mainThreadEditor.getModel(); + const model = mainThreadEditor.getModel(); return this._editorService.openEditor({ resource: model.uri, options: { preserveFocus: false } @@ -149,9 +149,9 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape { } $tryHideEditor(id: string): Promise { - let mainThreadEditor = this._documentsAndEditors.getEditor(id); + const mainThreadEditor = this._documentsAndEditors.getEditor(id); if (mainThreadEditor) { - let editors = this._editorService.visibleControls; + const editors = this._editorService.visibleControls; for (let editor of editors) { if (mainThreadEditor.matches(editor)) { return editor.group.closeEditor(editor.input).then(() => { return; }); diff --git a/src/vs/workbench/api/electron-browser/mainThreadFileSystemEventService.ts b/src/vs/workbench/api/electron-browser/mainThreadFileSystemEventService.ts index 1e9d568863e..351edd07f6d 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadFileSystemEventService.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadFileSystemEventService.ts @@ -57,7 +57,7 @@ export class MainThreadFileSystemEventService { }, undefined, this._listener); textfileService.onWillMove(e => { - let promise = proxy.$onWillRename(e.oldResource, e.newResource); + const promise = proxy.$onWillRename(e.oldResource, e.newResource); e.waitUntil(promise); }, undefined, this._listener); } diff --git a/src/vs/workbench/api/electron-browser/mainThreadHeapService.ts b/src/vs/workbench/api/electron-browser/mainThreadHeapService.ts index 1b5d15ee21c..486a290b705 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadHeapService.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadHeapService.ts @@ -96,7 +96,7 @@ export class HeapService implements IHeapService { @extHostCustomer export class MainThreadHeapService { - private _toDispose: IDisposable; + private readonly _toDispose: IDisposable; constructor( extHostContext: IExtHostContext, diff --git a/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts index 0be7720a10f..3de39e6a654 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts @@ -26,10 +26,10 @@ import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; @extHostNamedCustomer(MainContext.MainThreadLanguageFeatures) export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesShape { - private _proxy: ExtHostLanguageFeaturesShape; - private _heapService: IHeapService; - private _modeService: IModeService; - private _registrations: { [handle: number]: IDisposable; } = Object.create(null); + private readonly _proxy: ExtHostLanguageFeaturesShape; + private readonly _heapService: IHeapService; + private readonly _modeService: IModeService; + private readonly _registrations: { [handle: number]: IDisposable; } = Object.create(null); constructor( extHostContext: IExtHostContext, @@ -48,7 +48,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha } $unregister(handle: number): void { - let registration = this._registrations[handle]; + const registration = this._registrations[handle]; if (registration) { registration.dispose(); delete this._registrations[handle]; @@ -506,7 +506,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha $setLanguageConfiguration(handle: number, languageId: string, _configuration: ISerializedLanguageConfiguration): void { - let configuration: LanguageConfiguration = { + const configuration: LanguageConfiguration = { comments: _configuration.comments, brackets: _configuration.brackets, wordPattern: MainThreadLanguageFeatures._reviveRegExp(_configuration.wordPattern), @@ -532,7 +532,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha }; } - let languageIdentifier = this._modeService.getLanguageIdentifier(languageId); + const languageIdentifier = this._modeService.getLanguageIdentifier(languageId); if (languageIdentifier) { this._registrations[handle] = LanguageConfigurationRegistry.register(languageIdentifier, configuration); } diff --git a/src/vs/workbench/api/electron-browser/mainThreadLanguages.ts b/src/vs/workbench/api/electron-browser/mainThreadLanguages.ts index 88925d6bceb..392903a9e16 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadLanguages.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadLanguages.ts @@ -29,7 +29,7 @@ export class MainThreadLanguages implements MainThreadLanguagesShape { $changeLanguage(resource: UriComponents, languageId: string): Promise { const uri = URI.revive(resource); - let model = this._modelService.getModel(uri); + const model = this._modelService.getModel(uri); if (!model) { return Promise.reject(new Error('Invalid uri')); } diff --git a/src/vs/workbench/api/electron-browser/mainThreadMessageService.ts b/src/vs/workbench/api/electron-browser/mainThreadMessageService.ts index 68cf1ff4c04..be0157d8b5f 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadMessageService.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadMessageService.ts @@ -44,7 +44,7 @@ export class MainThreadMessageService implements MainThreadMessageServiceShape { return new Promise(resolve => { - let primaryActions: MessageItemAction[] = []; + const primaryActions: MessageItemAction[] = []; class MessageItemAction extends Action { constructor(id: string, label: string, handle: number) { diff --git a/src/vs/workbench/api/electron-browser/mainThreadOutputService.ts b/src/vs/workbench/api/electron-browser/mainThreadOutputService.ts index 7f9f642dafd..7c0308b8bdb 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadOutputService.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadOutputService.ts @@ -18,7 +18,7 @@ export class MainThreadOutputService extends Disposable implements MainThreadOut private static _idPool = 1; - private _proxy: ExtHostOutputServiceShape; + private readonly _proxy: ExtHostOutputServiceShape; private readonly _outputService: IOutputService; private readonly _partService: IPartService; private readonly _panelService: IPanelService; @@ -86,8 +86,11 @@ export class MainThreadOutputService extends Disposable implements MainThreadOut public $close(channelId: string): Promise | undefined { const panel = this._panelService.getActivePanel(); - if (panel && panel.getId() === OUTPUT_PANEL_ID && channelId === this._outputService.getActiveChannel().id) { - this._partService.setPanelHidden(true); + if (panel && panel.getId() === OUTPUT_PANEL_ID) { + const activeChannel = this._outputService.getActiveChannel(); + if (activeChannel && channelId === activeChannel.id) { + this._partService.setPanelHidden(true); + } } return undefined; @@ -101,7 +104,7 @@ export class MainThreadOutputService extends Disposable implements MainThreadOut return undefined; } - private _getChannel(channelId: string): IOutputChannel { + private _getChannel(channelId: string): IOutputChannel | null { return this._outputService.getChannel(channelId); } } diff --git a/src/vs/workbench/api/electron-browser/mainThreadProgress.ts b/src/vs/workbench/api/electron-browser/mainThreadProgress.ts index 5521d8f3854..80944a23128 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadProgress.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadProgress.ts @@ -10,9 +10,9 @@ import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostC @extHostNamedCustomer(MainContext.MainThreadProgress) export class MainThreadProgress implements MainThreadProgressShape { - private _progressService: IProgressService2; + private readonly _progressService: IProgressService2; private _progress = new Map void, progress: IProgress }>(); - private _proxy: ExtHostProgressShape; + private readonly _proxy: ExtHostProgressShape; constructor( extHostContext: IExtHostContext, diff --git a/src/vs/workbench/api/electron-browser/mainThreadQuickOpen.ts b/src/vs/workbench/api/electron-browser/mainThreadQuickOpen.ts index ab2865d0b32..976b27dc76d 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadQuickOpen.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadQuickOpen.ts @@ -18,9 +18,9 @@ interface QuickInputSession { @extHostNamedCustomer(MainContext.MainThreadQuickOpen) export class MainThreadQuickOpen implements MainThreadQuickOpenShape { - private _proxy: ExtHostQuickOpenShape; - private _quickInputService: IQuickInputService; - private _items: Record = {}; @@ -85,7 +85,7 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape { // ---- input - $input(options: InputBoxOptions, validateInput: boolean, token: CancellationToken): Promise { + $input(options: InputBoxOptions | undefined, validateInput: boolean, token: CancellationToken): Promise { const inputOptions: IInputOptions = Object.create(null); if (options) { diff --git a/src/vs/workbench/api/electron-browser/mainThreadSCM.ts b/src/vs/workbench/api/electron-browser/mainThreadSCM.ts index 667110d3612..094700da329 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadSCM.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadSCM.ts @@ -27,8 +27,8 @@ class MainThreadSCMResourceGroup implements ISCMResourceGroup { get onDidChange(): Event { return this._onDidChange.event; } constructor( - private sourceControlHandle: number, - private handle: number, + private readonly sourceControlHandle: number, + private readonly handle: number, public provider: ISCMProvider, public features: SCMGroupFeatures, public label: string, @@ -62,10 +62,10 @@ class MainThreadSCMResourceGroup implements ISCMResourceGroup { class MainThreadSCMResource implements ISCMResource { constructor( - private proxy: ExtHostSCMShape, - private sourceControlHandle: number, - private groupHandle: number, - private handle: number, + private readonly proxy: ExtHostSCMShape, + private readonly sourceControlHandle: number, + private readonly groupHandle: number, + private readonly handle: number, public sourceUri: URI, public resourceGroup: ISCMResourceGroup, public decorations: ISCMResourceDecorations @@ -92,7 +92,7 @@ class MainThreadSCMProvider implements ISCMProvider { get id(): string { return this._id; } readonly groups = new Sequence(); - private _groupsByHandle: { [handle: number]: MainThreadSCMResourceGroup; } = Object.create(null); + private readonly _groupsByHandle: { [handle: number]: MainThreadSCMResourceGroup; } = Object.create(null); // get groups(): ISequence { // return { @@ -129,11 +129,11 @@ class MainThreadSCMProvider implements ISCMProvider { get onDidChange(): Event { return this._onDidChange.event; } constructor( - private proxy: ExtHostSCMShape, - private _handle: number, - private _contextValue: string, - private _label: string, - private _rootUri: URI | undefined, + private readonly proxy: ExtHostSCMShape, + private readonly _handle: number, + private readonly _contextValue: string, + private readonly _label: string, + private readonly _rootUri: URI | undefined, @ISCMService scmService: ISCMService ) { } @@ -265,7 +265,7 @@ class MainThreadSCMProvider implements ISCMProvider { @extHostNamedCustomer(MainContext.MainThreadSCM) export class MainThreadSCM implements MainThreadSCMShape { - private _proxy: ExtHostSCMShape; + private readonly _proxy: ExtHostSCMShape; private _repositories: { [handle: number]: ISCMRepository; } = Object.create(null); private _inputDisposables: { [handle: number]: IDisposable; } = Object.create(null); private _disposables: IDisposable[] = []; diff --git a/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts b/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts index eb965620574..fd647ecfe68 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts @@ -16,8 +16,8 @@ import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; -import { IIdentifiedSingleEditOperation, ISingleEditOperation, ITextModel } from 'vs/editor/common/model'; -import { CodeAction } from 'vs/editor/common/modes'; +import { IIdentifiedSingleEditOperation, ITextModel } from 'vs/editor/common/model'; +import { CodeAction, TextEdit } from 'vs/editor/common/modes'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; import { shouldSynchronizeModel } from 'vs/editor/common/services/modelService'; import { getCodeActions } from 'vs/editor/contrib/codeAction/codeAction'; @@ -34,7 +34,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { IProgressService2, ProgressLocation } from 'vs/platform/progress/common/progress'; import { extHostCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; -import { ISaveParticipant, ITextFileEditorModel, SaveReason } from 'vs/workbench/services/textfile/common/textfiles'; +import { ISaveParticipant, SaveReason, IResolvedTextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles'; import { ExtHostContext, ExtHostDocumentSaveParticipantShape, IExtHostContext } from '../node/extHost.protocol'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -51,7 +51,7 @@ class TrimWhitespaceParticipant implements ISaveParticipantParticipant { // Nothing } - async participate(model: ITextFileEditorModel, env: { reason: SaveReason }): Promise { + async participate(model: IResolvedTextFileEditorModel, env: { reason: SaveReason }): Promise { if (this.configurationService.getValue('files.trimTrailingWhitespace', { overrideIdentifier: model.textEditorModel.getLanguageIdentifier().language, resource: model.getResource() })) { this.doTrimTrailingWhitespace(model.textEditorModel, env.reason === SaveReason.AUTO); } @@ -113,7 +113,7 @@ export class FinalNewLineParticipant implements ISaveParticipantParticipant { // Nothing } - async participate(model: ITextFileEditorModel, env: { reason: SaveReason }): Promise { + async participate(model: IResolvedTextFileEditorModel, env: { reason: SaveReason }): Promise { if (this.configurationService.getValue('files.insertFinalNewline', { overrideIdentifier: model.textEditorModel.getLanguageIdentifier().language, resource: model.getResource() })) { this.doInsertFinalNewLine(model.textEditorModel); } @@ -151,7 +151,7 @@ export class TrimFinalNewLinesParticipant implements ISaveParticipantParticipant // Nothing } - async participate(model: ITextFileEditorModel, env: { reason: SaveReason }): Promise { + async participate(model: IResolvedTextFileEditorModel, env: { reason: SaveReason }): Promise { if (this.configurationService.getValue('files.trimFinalNewlines', { overrideIdentifier: model.textEditorModel.getLanguageIdentifier().language, resource: model.getResource() })) { this.doTrimFinalNewLines(model.textEditorModel, env.reason === SaveReason.AUTO); } @@ -222,7 +222,7 @@ class FormatOnSaveParticipant implements ISaveParticipantParticipant { // Nothing } - async participate(editorModel: ITextFileEditorModel, env: { reason: SaveReason }): Promise { + async participate(editorModel: IResolvedTextFileEditorModel, env: { reason: SaveReason }): Promise { const model = editorModel.textEditorModel; if (env.reason === SaveReason.AUTO @@ -231,13 +231,12 @@ class FormatOnSaveParticipant implements ISaveParticipantParticipant { } const versionNow = model.getVersionId(); - const { tabSize, insertSpaces } = model.getOptions(); const timeout = this._configurationService.getValue('editor.formatOnSaveTimeout', { overrideIdentifier: model.getLanguageIdentifier().language, resource: editorModel.getResource() }); - return new Promise((resolve, reject) => { - let source = new CancellationTokenSource(); - let request = getDocumentFormattingEdits(this._telemetryService, this._editorWorkerService, model, { tabSize, insertSpaces }, FormatMode.Auto, source.token); + return new Promise((resolve, reject) => { + const source = new CancellationTokenSource(); + const request = getDocumentFormattingEdits(this._telemetryService, this._editorWorkerService, model, model.getFormattingOptions(), FormatMode.Auto, source.token); setTimeout(() => { reject(localize('timeout.formatOnSave', "Aborted format on save after {0}ms", timeout)); @@ -258,11 +257,11 @@ class FormatOnSaveParticipant implements ISaveParticipantParticipant { }); } - private _editsWithEditor(editor: ICodeEditor, edits: ISingleEditOperation[]): void { + private _editsWithEditor(editor: ICodeEditor, edits: TextEdit[]): void { FormattingEdit.execute(editor, edits); } - private _editWithModel(model: ITextModel, edits: ISingleEditOperation[]): void { + private _editWithModel(model: ITextModel, edits: TextEdit[]): void { const [{ range }] = edits; const initialSelection = new Selection(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn); @@ -277,7 +276,7 @@ class FormatOnSaveParticipant implements ISaveParticipantParticipant { }); } - private static _asIdentEdit({ text, range }: ISingleEditOperation): IIdentifiedSingleEditOperation { + private static _asIdentEdit({ text, range }: TextEdit): IIdentifiedSingleEditOperation { return { text, range: Range.lift(range), @@ -294,7 +293,7 @@ class CodeActionOnSaveParticipant implements ISaveParticipant { @IConfigurationService private readonly _configurationService: IConfigurationService ) { } - async participate(editorModel: ITextFileEditorModel, env: { reason: SaveReason }): Promise { + async participate(editorModel: IResolvedTextFileEditorModel, env: { reason: SaveReason }): Promise { if (env.reason === SaveReason.AUTO) { return undefined; } @@ -368,13 +367,13 @@ class CodeActionOnSaveParticipant implements ISaveParticipant { class ExtHostSaveParticipant implements ISaveParticipantParticipant { - private _proxy: ExtHostDocumentSaveParticipantShape; + private readonly _proxy: ExtHostDocumentSaveParticipantShape; constructor(extHostContext: IExtHostContext) { this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostDocumentSaveParticipant); } - async participate(editorModel: ITextFileEditorModel, env: { reason: SaveReason }): Promise { + async participate(editorModel: IResolvedTextFileEditorModel, env: { reason: SaveReason }): Promise { if (!shouldSynchronizeModel(editorModel.textEditorModel)) { // the model never made it to the extension @@ -425,7 +424,7 @@ export class SaveParticipant implements ISaveParticipant { this._saveParticipants.dispose(); } - async participate(model: ITextFileEditorModel, env: { reason: SaveReason }): Promise { + async participate(model: IResolvedTextFileEditorModel, env: { reason: SaveReason }): Promise { return this._progressService.withProgress({ location: ProgressLocation.Window }, progress => { progress.report({ message: localize('saveParticipants', "Running Save Participants...") }); const promiseFactory = this._saveParticipants.getValue().map(p => () => { diff --git a/src/vs/workbench/api/electron-browser/mainThreadStatusBar.ts b/src/vs/workbench/api/electron-browser/mainThreadStatusBar.ts index f002ffb6889..e6f2d2f3635 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadStatusBar.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadStatusBar.ts @@ -34,12 +34,12 @@ export class MainThreadStatusBar implements MainThreadStatusBarShape { this.$dispose(id); // Add new - let entry = this._statusbarService.addEntry({ text, tooltip, command, color, extensionId }, alignment, priority); + const entry = this._statusbarService.addEntry({ text, tooltip, command, color, extensionId }, alignment, priority); this._entries[id] = entry; } $dispose(id: number) { - let disposeable = this._entries[id]; + const disposeable = this._entries[id]; if (disposeable) { disposeable.dispose(); } diff --git a/src/vs/workbench/api/electron-browser/mainThreadStorage.ts b/src/vs/workbench/api/electron-browser/mainThreadStorage.ts index 96f63fbecba..04aae06d817 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadStorage.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadStorage.ts @@ -11,10 +11,10 @@ import { IDisposable } from 'vs/base/common/lifecycle'; @extHostNamedCustomer(MainContext.MainThreadStorage) export class MainThreadStorage implements MainThreadStorageShape { - private _storageService: IStorageService; - private _proxy: ExtHostStorageShape; - private _storageListener: IDisposable; - private _sharedStorageKeysToWatch: Map = new Map(); + private readonly _storageService: IStorageService; + private readonly _proxy: ExtHostStorageShape; + private readonly _storageListener: IDisposable; + private readonly _sharedStorageKeysToWatch: Map = new Map(); constructor( extHostContext: IExtHostContext, @@ -24,7 +24,7 @@ export class MainThreadStorage implements MainThreadStorageShape { this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostStorage); this._storageListener = this._storageService.onDidChangeStorage(e => { - let shared = e.scope === StorageScope.GLOBAL; + const shared = e.scope === StorageScope.GLOBAL; if (shared && this._sharedStorageKeysToWatch.has(e.key)) { try { this._proxy.$acceptValue(shared, e.key, this._getValue(shared, e.key)); @@ -51,7 +51,7 @@ export class MainThreadStorage implements MainThreadStorageShape { } private _getValue(shared: boolean, key: string): T | undefined { - let jsonValue = this._storageService.get(key, shared ? StorageScope.GLOBAL : StorageScope.WORKSPACE); + const jsonValue = this._storageService.get(key, shared ? StorageScope.GLOBAL : StorageScope.WORKSPACE); if (!jsonValue) { return undefined; } diff --git a/src/vs/workbench/api/electron-browser/mainThreadTask.ts b/src/vs/workbench/api/electron-browser/mainThreadTask.ts index 985a5df8556..47bc0be5ea3 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadTask.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadTask.ts @@ -70,7 +70,7 @@ namespace TaskProcessEndedDTO { namespace TaskDefinitionDTO { export function from(value: KeyedTaskIdentifier): TaskDefinitionDTO { - let result = Objects.assign(Object.create(null), value); + const result = Objects.assign(Object.create(null), value); delete result._key; return result; } @@ -139,13 +139,13 @@ namespace ProcessExecutionOptionsDTO { namespace ProcessExecutionDTO { export function is(value: ShellExecutionDTO | ProcessExecutionDTO): value is ProcessExecutionDTO { - let candidate = value as ProcessExecutionDTO; + const candidate = value as ProcessExecutionDTO; return candidate && !!candidate.process; } export function from(value: CommandConfiguration): ProcessExecutionDTO { - let process: string = Types.isString(value.name) ? value.name : value.name.value; - let args: string[] = value.args ? value.args.map(value => Types.isString(value) ? value : value.value) : []; - let result: ProcessExecutionDTO = { + const process: string = Types.isString(value.name) ? value.name : value.name.value; + const args: string[] = value.args ? value.args.map(value => Types.isString(value) ? value : value.value) : []; + const result: ProcessExecutionDTO = { process: process, args: args }; @@ -155,7 +155,7 @@ namespace ProcessExecutionDTO { return result; } export function to(value: ProcessExecutionDTO): CommandConfiguration { - let result: CommandConfiguration = { + const result: CommandConfiguration = { runtime: RuntimeType.Process, name: value.process, args: value.args, @@ -171,7 +171,7 @@ namespace ShellExecutionOptionsDTO { if (value === undefined || value === null) { return undefined; } - let result: ShellExecutionOptionsDTO = { + const result: ShellExecutionOptionsDTO = { cwd: value.cwd || CommandOptions.defaults.cwd, env: value.env }; @@ -186,7 +186,7 @@ namespace ShellExecutionOptionsDTO { if (value === undefined || value === null) { return undefined; } - let result: CommandOptions = { + const result: CommandOptions = { cwd: value.cwd, env: value.env }; @@ -207,11 +207,11 @@ namespace ShellExecutionOptionsDTO { namespace ShellExecutionDTO { export function is(value: ShellExecutionDTO | ProcessExecutionDTO): value is ShellExecutionDTO { - let candidate = value as ShellExecutionDTO; + const candidate = value as ShellExecutionDTO; return candidate && (!!candidate.commandLine || !!candidate.command); } export function from(value: CommandConfiguration): ShellExecutionDTO { - let result: ShellExecutionDTO = {}; + const result: ShellExecutionDTO = {}; if (value.name && Types.isString(value.name) && (value.args === undefined || value.args === null || value.args.length === 0)) { result.commandLine = value.name; } else { @@ -224,7 +224,7 @@ namespace ShellExecutionDTO { return result; } export function to(value: ShellExecutionDTO): CommandConfiguration { - let result: CommandConfiguration = { + const result: CommandConfiguration = { runtime: RuntimeType.Shell, name: value.commandLine ? value.commandLine : value.command, args: value.args, @@ -239,7 +239,7 @@ namespace ShellExecutionDTO { namespace TaskSourceDTO { export function from(value: TaskSource): TaskSourceDTO { - let result: TaskSourceDTO = { + const result: TaskSourceDTO = { label: value.label }; if (value.kind === TaskSourceKind.Extension) { @@ -285,7 +285,7 @@ namespace TaskSourceDTO { namespace TaskHandleDTO { export function is(value: any): value is TaskHandleDTO { - let candidate: TaskHandleDTO = value; + const candidate: TaskHandleDTO = value; return candidate && Types.isString(candidate.id) && !!candidate.workspaceFolder; } } @@ -295,7 +295,7 @@ namespace TaskDTO { if (task === undefined || task === null || (!CustomTask.is(task) && !ContributedTask.is(task))) { return undefined; } - let result: TaskDTO = { + const result: TaskDTO = { _id: task._id, name: task.configurationProperties.name, definition: TaskDefinitionDTO.from(task.getDefinition()), @@ -344,12 +344,12 @@ namespace TaskDTO { return undefined; } command.presentation = TaskPresentationOptionsDTO.to(task.presentationOptions); - let source = TaskSourceDTO.to(task.source, workspace); + const source = TaskSourceDTO.to(task.source, workspace); - let label = nls.localize('task.label', '{0}: {1}', source.label, task.name); - let definition = TaskDefinitionDTO.to(task.definition, executeOnly); - let id = `${task.source.extensionId}.${definition._key}`; - let result: ContributedTask = new ContributedTask( + const label = nls.localize('task.label', '{0}: {1}', source.label, task.name); + const definition = TaskDefinitionDTO.to(task.definition, executeOnly); + const id = `${task.source.extensionId}.${definition._key}`; + const result: ContributedTask = new ContributedTask( id, // uuidMap.getUUID(identifier) source, label, @@ -382,9 +382,9 @@ namespace TaskFilterDTO { @extHostNamedCustomer(MainContext.MainThreadTask) export class MainThreadTask implements MainThreadTaskShape { - private _extHostContext: IExtHostContext; - private _proxy: ExtHostTaskShape; - private _providers: Map; + private readonly _extHostContext: IExtHostContext; + private readonly _proxy: ExtHostTaskShape; + private readonly _providers: Map; constructor( extHostContext: IExtHostContext, @@ -416,12 +416,12 @@ export class MainThreadTask implements MainThreadTaskShape { } public $registerTaskProvider(handle: number): Promise { - let provider: ITaskProvider = { + const provider: ITaskProvider = { provideTasks: (validTypes: IStringDictionary) => { return Promise.resolve(this._proxy.$provideTasks(handle, validTypes)).then((value) => { - let tasks: Task[] = []; + const tasks: Task[] = []; for (let dto of value.tasks) { - let task = TaskDTO.to(dto, this._workspaceContextServer, true); + const task = TaskDTO.to(dto, this._workspaceContextServer, true); if (task) { tasks.push(task); } else { @@ -435,7 +435,7 @@ export class MainThreadTask implements MainThreadTaskShape { }); } }; - let disposable = this._taskService.registerTaskProvider(provider); + const disposable = this._taskService.registerTaskProvider(provider); this._providers.set(handle, { disposable, provider }); return Promise.resolve(undefined); } @@ -448,9 +448,9 @@ export class MainThreadTask implements MainThreadTaskShape { public $fetchTasks(filter?: TaskFilterDTO): Promise { return this._taskService.tasks(TaskFilterDTO.to(filter)).then((tasks) => { - let result: TaskDTO[] = []; + const result: TaskDTO[] = []; for (let task of tasks) { - let item = TaskDTO.from(task); + const item = TaskDTO.from(task); if (item) { result.push(item); } @@ -462,12 +462,12 @@ export class MainThreadTask implements MainThreadTaskShape { public $executeTask(value: TaskHandleDTO | TaskDTO): Promise { return new Promise((resolve, reject) => { if (TaskHandleDTO.is(value)) { - let workspaceFolder = this._workspaceContextServer.getWorkspaceFolder(URI.revive(value.workspaceFolder)); + const workspaceFolder = this._workspaceContextServer.getWorkspaceFolder(URI.revive(value.workspaceFolder)); this._taskService.getTask(workspaceFolder, value.id, true).then((task: Task) => { this._taskService.run(task).then(undefined, reason => { // eat the error, it has already been surfaced to the user and we don't care about it here }); - let result: TaskExecutionDTO = { + const result: TaskExecutionDTO = { id: value.id, task: TaskDTO.from(task) }; @@ -476,11 +476,11 @@ export class MainThreadTask implements MainThreadTaskShape { reject(new Error('Task not found')); }); } else { - let task = TaskDTO.to(value, this._workspaceContextServer, true); + const task = TaskDTO.to(value, this._workspaceContextServer, true); this._taskService.run(task).then(undefined, reason => { // eat the error, it has already been surfaced to the user and we don't care about it here }); - let result: TaskExecutionDTO = { + const result: TaskExecutionDTO = { id: task._id, task: TaskDTO.from(task) }; @@ -529,7 +529,7 @@ export class MainThreadTask implements MainThreadTaskShape { }, context: this._extHostContext, resolveVariables: (workspaceFolder: IWorkspaceFolder, toResolve: ResolveSet): Promise => { - let vars: string[] = []; + const vars: string[] = []; toResolve.variables.forEach(item => vars.push(item)); return Promise.resolve(this._proxy.$resolveVariables(workspaceFolder.uri, { process: toResolve.process, variables: vars })).then(values => { const partiallyResolvedVars = new Array(); @@ -538,7 +538,7 @@ export class MainThreadTask implements MainThreadTaskShape { }); return new Promise((resolve, reject) => { this._configurationResolverService.resolveWithInteraction(workspaceFolder, partiallyResolvedVars, 'tasks').then(resolvedVars => { - let result: ResolvedVariables = { + const result: ResolvedVariables = { process: undefined, variables: new Map() }; diff --git a/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts b/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts index 3f30d465934..8a1fc7d216d 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts @@ -7,6 +7,7 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { ITerminalService, ITerminalInstance, IShellLaunchConfig, ITerminalProcessExtHostProxy, ITerminalProcessExtHostRequest, ITerminalDimensions, EXT_HOST_CREATION_DELAY } from 'vs/workbench/contrib/terminal/common/terminal'; import { ExtHostContext, ExtHostTerminalServiceShape, MainThreadTerminalServiceShape, MainContext, IExtHostContext, ShellLaunchConfigDto } from 'vs/workbench/api/node/extHost.protocol'; import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; +import { UriComponents, URI } from 'vs/base/common/uri'; @extHostNamedCustomer(MainContext.MainThreadTerminalService) export class MainThreadTerminalService implements MainThreadTerminalServiceShape { @@ -58,12 +59,12 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape // when the extension host process goes down ? } - public $createTerminal(name?: string, shellPath?: string, shellArgs?: string[], cwd?: string, env?: { [key: string]: string }, waitOnExit?: boolean, strictEnv?: boolean): Promise<{ id: number, name: string }> { + public $createTerminal(name?: string, shellPath?: string, shellArgs?: string[], cwd?: string | UriComponents, env?: { [key: string]: string }, waitOnExit?: boolean, strictEnv?: boolean): Promise<{ id: number, name: string }> { const shellLaunchConfig: IShellLaunchConfig = { name, executable: shellPath, args: shellArgs, - cwd, + cwd: typeof cwd === 'string' ? cwd : URI.revive(cwd), waitOnExit, ignoreConfigurationCwd: true, env, diff --git a/src/vs/workbench/api/electron-browser/mainThreadTreeViews.ts b/src/vs/workbench/api/electron-browser/mainThreadTreeViews.ts index 2d6da3314f5..fe4995feb35 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadTreeViews.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadTreeViews.ts @@ -15,8 +15,8 @@ import { IMarkdownString } from 'vs/base/common/htmlContent'; @extHostNamedCustomer(MainContext.MainThreadTreeViews) export class MainThreadTreeViews extends Disposable implements MainThreadTreeViewsShape { - private _proxy: ExtHostTreeViewsShape; - private _dataProviders: Map = new Map(); + private readonly _proxy: ExtHostTreeViewsShape; + private readonly _dataProviders: Map = new Map(); constructor( extHostContext: IExtHostContext, @@ -133,11 +133,11 @@ type TreeItemHandle = string; class TreeViewDataProvider implements ITreeViewDataProvider { - private itemsMap: Map = new Map(); + private readonly itemsMap: Map = new Map(); - constructor(private treeViewId: string, - private _proxy: ExtHostTreeViewsShape, - private notificationService: INotificationService + constructor(private readonly treeViewId: string, + private readonly _proxy: ExtHostTreeViewsShape, + private readonly notificationService: INotificationService ) { } diff --git a/src/vs/workbench/api/electron-browser/mainThreadWebview.ts b/src/vs/workbench/api/electron-browser/mainThreadWebview.ts index e8b248cb19c..1ccd47f9ff3 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadWebview.ts @@ -2,33 +2,35 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { onUnexpectedError } from 'vs/base/common/errors'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import * as map from 'vs/base/common/map'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { localize } from 'vs/nls'; -import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { ExtHostContext, ExtHostWebviewsShape, IExtHostContext, MainContext, MainThreadWebviewsShape, WebviewInsetHandle, WebviewPanelHandle, WebviewPanelShowOptions } from 'vs/workbench/api/node/extHost.protocol'; +import { ExtHostContext, ExtHostWebviewsShape, IExtHostContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelShowOptions, WebviewInsetHandle } from 'vs/workbench/api/node/extHost.protocol'; import { editorGroupToViewColumn, EditorViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/shared/editor'; -import { CodeInsetController } from 'vs/workbench/contrib/codeinset/electron-browser/codeInset.contribution'; import { WebviewEditor } from 'vs/workbench/contrib/webview/electron-browser/webviewEditor'; -import { WebviewEditorInput } from 'vs/workbench/contrib/webview/electron-browser/webviewEditorInput'; -import { ICreateWebViewShowOptions, IWebviewEditorService, WebviewInputOptions } from 'vs/workbench/contrib/webview/electron-browser/webviewEditorService'; -import { WebviewElement } from 'vs/workbench/contrib/webview/electron-browser/webviewElement'; -import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { WebviewEditorInput, RevivedWebviewEditorInput } from 'vs/workbench/contrib/webview/electron-browser/webviewEditorInput'; +import { ICreateWebViewShowOptions, IWebviewEditorService, WebviewInputOptions, WebviewReviver } from 'vs/workbench/contrib/webview/electron-browser/webviewEditorService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { IPartService, Parts } from 'vs/workbench/services/part/common/partService'; +import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; 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 { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { WebviewElement } from 'vs/workbench/contrib/webview/electron-browser/webviewElement'; +import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; +import { CodeInsetController } from 'vs/workbench/contrib/codeinset/electron-browser/codeInset.contribution'; +import { IPartService, Parts } from 'vs/workbench/services/part/common/partService'; @extHostNamedCustomer(MainContext.MainThreadWebviews) -export class MainThreadWebviews implements MainThreadWebviewsShape { +export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviver { + + private static readonly viewType = 'mainThreadWebview'; private static readonly standardSupportedLinkSchemes = ['http', 'https', 'mailto']; @@ -39,7 +41,7 @@ export class MainThreadWebviews implements MainThreadWebviewsShape { private readonly _proxy: ExtHostWebviewsShape; private readonly _webviews = new Map(); private readonly _webviewsElements = new Map(); - private readonly _revivers = new Map(); + private readonly _revivers = new Set(); private _activeWebview: WebviewPanelHandle | undefined = undefined; @@ -60,6 +62,8 @@ export class MainThreadWebviews implements MainThreadWebviewsShape { _editorService.onDidActiveEditorChange(this.onActiveEditorChanged, this, this._toDispose); _editorService.onDidVisibleEditorsChange(this.onVisibleEditorsChanged, this, this._toDispose); + this._toDispose.push(_webviewService.registerReviver(this)); + lifecycleService.onBeforeShutdown(e => { e.veto(this._onBeforeShutdown()); }, this, this._toDispose); @@ -84,7 +88,7 @@ export class MainThreadWebviews implements MainThreadWebviewsShape { mainThreadShowOptions.group = viewColumnToEditorGroup(this._editorGroupService, showOptions.viewColumn); } - const webview = this._webviewService.createWebview(this.getInternalWebviewId(viewType), title, mainThreadShowOptions, reviveWebviewOptions(options), URI.revive(extensionLocation), this.createWebviewEventDelegate(handle)); + const webview = this._webviewService.createWebview(MainThreadWebviews.viewType, title, mainThreadShowOptions, reviveWebviewOptions(options), URI.revive(extensionLocation), this.createWebviewEventDelegate(handle)); webview.state = { viewType: viewType, state: undefined @@ -205,57 +209,50 @@ export class MainThreadWebviews implements MainThreadWebviewsShape { } public $registerSerializer(viewType: string): void { - if (this._revivers.has(viewType)) { - throw new Error(`Reviver for ${viewType} already registered`); - } - this._revivers.set(viewType, this._webviewService.registerReviver(this.getInternalWebviewId(viewType), { - canRevive: (webview) => { - return !webview.isDisposed() && webview.state; - }, - reviveWebview: (webview): Promise => { - const viewType = webview.state.viewType; - return Promise.resolve(this._extensionService.activateByEvent(`onWebviewPanel:${viewType}`).then(() => { - const handle = 'revival-' + MainThreadWebviews.revivalPool++; - this._webviews.set(handle, webview); - webview._events = this.createWebviewEventDelegate(handle); - let state = undefined; - if (webview.state.state) { - try { - state = JSON.parse(webview.state.state); - } catch { - // noop - } - } - - return this._proxy.$deserializeWebviewPanel(handle, webview.state.viewType, webview.getTitle(), state, editorGroupToViewColumn(this._editorGroupService, webview.group), webview.options) - .then(undefined, error => { - onUnexpectedError(error); - - webview.html = MainThreadWebviews.getDeserializationFailedContents(viewType); - }); - })); - } - })); + this._revivers.add(viewType); } public $unregisterSerializer(viewType: string): void { - const reviver = this._revivers.get(viewType); - if (!reviver) { - throw new Error(`No reviver for ${viewType} registered`); - } - - reviver.dispose(); this._revivers.delete(viewType); } - private getInternalWebviewId(viewType: string): string { - return `mainThreadWebview-${viewType}`; + public reviveWebview(webview: WebviewEditorInput): Promise { + const viewType = webview.state.viewType; + return Promise.resolve(this._extensionService.activateByEvent(`onWebviewPanel:${viewType}`).then(() => { + const handle = 'revival-' + MainThreadWebviews.revivalPool++; + this._webviews.set(handle, webview); + webview._events = this.createWebviewEventDelegate(handle); + + let state = undefined; + if (webview.state.state) { + try { + state = JSON.parse(webview.state.state); + } catch { + // noop + } + } + + return this._proxy.$deserializeWebviewPanel(handle, webview.state.viewType, webview.getTitle(), state, editorGroupToViewColumn(this._editorGroupService, webview.group), webview.options) + .then(undefined, error => { + onUnexpectedError(error); + + webview.html = MainThreadWebviews.getDeserializationFailedContents(viewType); + }); + })); + } + + public canRevive(webview: WebviewEditorInput): boolean { + if (webview.isDisposed() || !webview.state || webview.viewType !== MainThreadWebviews.viewType) { + return false; + } + + return this._revivers.has(webview.state.viewType) || !!(webview as RevivedWebviewEditorInput).reviver; } private _onBeforeShutdown(): boolean { - this._webviews.forEach((webview) => { - if (!webview.isDisposed() && webview.state && this._revivers.has(webview.state.viewType)) { - webview.state.state = webview.webviewState; + this._webviews.forEach((view) => { + if (this.canRevive(view)) { + view.state.state = view.webviewState; } }); return false; // Don't veto shutdown diff --git a/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts b/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts index 63d67c6aa05..c2b3d540575 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts @@ -119,7 +119,7 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape { // --- search --- - $startFileSearch(includePattern: string, _includeFolder: UriComponents | undefined, excludePatternOrDisregardExcludes: string | false, maxResults: number, token: CancellationToken): Promise { + $startFileSearch(includePattern: string, _includeFolder: UriComponents | undefined, excludePatternOrDisregardExcludes: string | false | undefined, maxResults: number, token: CancellationToken): Promise { const includeFolder = URI.revive(_includeFolder); const workspace = this._contextService.getWorkspace(); if (!workspace.folders.length) { @@ -134,6 +134,7 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape { disregardSearchExcludeSettings: true, disregardIgnoreFiles: true, includePattern, + excludePattern: typeof excludePatternOrDisregardExcludes === 'string' ? excludePatternOrDisregardExcludes : undefined, _reason: 'startFileSearch' }); @@ -181,6 +182,7 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape { const query = queryBuilder.file(folders, { _reason: 'checkExists', includePattern: includes.join(', '), + expandPatterns: true, exists: true }); diff --git a/src/vs/workbench/api/node/apiCommands.ts b/src/vs/workbench/api/node/apiCommands.ts index 01d1c77337c..c5177b90d08 100644 --- a/src/vs/workbench/api/node/apiCommands.ts +++ b/src/vs/workbench/api/node/apiCommands.ts @@ -3,11 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { tmpdir } from 'os'; -import { join } from 'vs/base/common/path'; import * as vscode from 'vscode'; import { URI } from 'vs/base/common/uri'; -import { isMalformedFileUri } from 'vs/base/common/resources'; import * as typeConverters from 'vs/workbench/api/node/extHostTypeConverters'; import { CommandsRegistry, ICommandService, ICommandHandler } from 'vs/platform/commands/common/commands'; import { ITextEditorOptions } from 'vs/platform/editor/common/editor'; @@ -17,7 +14,6 @@ import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { IWindowsService } from 'vs/platform/windows/common/windows'; import { IDownloadService } from 'vs/platform/download/common/download'; -import { generateUuid } from 'vs/base/common/uuid'; // ----------------------------------------------------------------- // The following commands are registered on both sides separately. @@ -36,32 +32,12 @@ function adjustHandler(handler: (executor: ICommandsExecutor, ...args: any[]) => }; } -export class PreviewHTMLAPICommand { - public static ID = 'vscode.previewHtml'; - public static execute(executor: ICommandsExecutor, uri: URI, position?: vscode.ViewColumn, label?: string, options?: any): Promise { - return executor.executeCommand('_workbench.previewHtml', - uri, - typeof position === 'number' && typeConverters.ViewColumn.from(position), - label, - options - ); - } -} -CommandsRegistry.registerCommand(PreviewHTMLAPICommand.ID, adjustHandler(PreviewHTMLAPICommand.execute)); - export class OpenFolderAPICommand { public static ID = 'vscode.openFolder'; public static execute(executor: ICommandsExecutor, uri?: URI, forceNewWindow?: boolean): Promise { if (!uri) { return executor.executeCommand('_files.pickFolderAndOpen', forceNewWindow); } - let correctedUri = isMalformedFileUri(uri); - if (correctedUri) { - // workaround for #55916 and #55891, will be removed in 1.28 - console.warn(`'vscode.openFolder' command invoked with an invalid URI (file:// scheme missing): '${uri}'. Converted to a 'file://' URI: ${correctedUri}`); - uri = correctedUri; - } - return executor.executeCommand('_files.windowOpen', { urisToOpen: [{ uri }], forceNewWindow }); } } @@ -171,7 +147,5 @@ CommandsRegistry.registerCommand({ CommandsRegistry.registerCommand('_workbench.downloadResource', function (accessor: ServicesAccessor, resource: URI) { const downloadService = accessor.get(IDownloadService); - const location = join(tmpdir(), generateUuid()); - - return downloadService.download(resource, location).then(() => URI.file(location)); -}); \ No newline at end of file + return downloadService.download(resource).then(location => URI.file(location)); +}); diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index 565c8835155..4d2e7c01c30 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -65,6 +65,7 @@ import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/n import * as vscode from 'vscode'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { originalFSPath } from 'vs/base/common/resources'; +import { CLIServer } from 'vs/workbench/api/node/extHostCLIServer'; export interface IExtensionApiFactory { (extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode; @@ -91,7 +92,7 @@ export function createApiFactory( extHostStorage: ExtHostStorage ): IExtensionApiFactory { - let schemeTransformer: ISchemeTransformer | null = null; + const schemeTransformer: ISchemeTransformer | null = null; // Addressable instances rpcProtocol.set(ExtHostContext.ExtHostLogService, extHostLogService); @@ -113,7 +114,7 @@ export function createApiFactory( const extHostFileSystem = rpcProtocol.set(ExtHostContext.ExtHostFileSystem, new ExtHostFileSystem(rpcProtocol, extHostLanguageFeatures)); const extHostFileSystemEvent = rpcProtocol.set(ExtHostContext.ExtHostFileSystemEventService, new ExtHostFileSystemEventService(rpcProtocol, extHostDocumentsAndEditors)); const extHostQuickOpen = rpcProtocol.set(ExtHostContext.ExtHostQuickOpen, new ExtHostQuickOpen(rpcProtocol, extHostWorkspace, extHostCommands)); - const extHostTerminalService = rpcProtocol.set(ExtHostContext.ExtHostTerminalService, new ExtHostTerminalService(rpcProtocol, extHostConfiguration, extHostLogService, extHostCommands)); + const extHostTerminalService = rpcProtocol.set(ExtHostContext.ExtHostTerminalService, new ExtHostTerminalService(rpcProtocol, extHostConfiguration, extHostLogService)); const extHostDebugService = rpcProtocol.set(ExtHostContext.ExtHostDebugService, new ExtHostDebugService(rpcProtocol, extHostWorkspace, extensionService, extHostDocumentsAndEditors, extHostConfiguration, extHostTerminalService, extHostCommands)); const extHostSCM = rpcProtocol.set(ExtHostContext.ExtHostSCM, new ExtHostSCM(rpcProtocol, extHostCommands, extHostLogService)); const extHostComment = rpcProtocol.set(ExtHostContext.ExtHostComments, new ExtHostComments(rpcProtocol, extHostCommands, extHostDocuments)); @@ -124,6 +125,10 @@ export function createApiFactory( const extHostProgress = rpcProtocol.set(ExtHostContext.ExtHostProgress, new ExtHostProgress(rpcProtocol.getProxy(MainContext.MainThreadProgress))); const extHostOutputService = rpcProtocol.set(ExtHostContext.ExtHostOutputService, new ExtHostOutputService(initData.logsLocation, rpcProtocol)); rpcProtocol.set(ExtHostContext.ExtHostStorage, extHostStorage); + if (initData.remoteAuthority) { + const cliServer = new CLIServer(extHostCommands); + process.env['VSCODE_IPC_HOOK_CLI'] = cliServer.ipcHandlePath; + } // Check that no named customers are missing const expected: ProxyIdentifier[] = Object.keys(ExtHostContext).map((key) => (ExtHostContext)[key]); @@ -175,23 +180,6 @@ export function createApiFactory( }; })(); - // Warn when trying to use the vscode.previewHtml command as it does not work properly in all scenarios and - // has security concerns. - const checkCommand = (() => { - let done = false; - const informOnce = () => { - if (!done) { - done = true; - window.showWarningMessage(localize('previewHtml.deprecated', "Extension '{0}' uses the 'vscode.previewHtml' command which is deprecated and will be removed soon. Please file an issue against this extension to update to use VS Code's webview API.", extension.identifier.value)); - } - }; - return (commandId: string) => { - if (commandId === 'vscode.previewHtml') { - informOnce(); - } - return commandId; - }; - })(); // namespace: commands const commands: typeof vscode.commands = { @@ -200,7 +188,7 @@ export function createApiFactory( }, registerTextEditorCommand(id: string, callback: (textEditor: vscode.TextEditor, edit: vscode.TextEditorEdit, ...args: any[]) => void, thisArg?: any): vscode.Disposable { return extHostCommands.registerCommand(true, id, (...args: any[]): any => { - let activeTextEditor = extHostEditors.getActiveTextEditor(); + const activeTextEditor = extHostEditors.getActiveTextEditor(); if (!activeTextEditor) { console.warn('Cannot execute ' + id + ' because there is no active text editor.'); return undefined; @@ -221,7 +209,7 @@ export function createApiFactory( }, registerDiffInformationCommand: proposedApiFunction(extension, (id: string, callback: (diff: vscode.LineChange[], ...args: any[]) => any, thisArg?: any): vscode.Disposable => { return extHostCommands.registerCommand(true, id, async (...args: any[]): Promise => { - let activeTextEditor = extHostEditors.getActiveTextEditor(); + const activeTextEditor = extHostEditors.getActiveTextEditor(); if (!activeTextEditor) { console.warn('Cannot execute ' + id + ' because there is no active text editor.'); return undefined; @@ -232,7 +220,7 @@ export function createApiFactory( }); }), executeCommand(id: string, ...args: any[]): Thenable { - return extHostCommands.executeCommand(checkCommand(id), ...args); + return extHostCommands.executeCommand(id, ...args); }, getCommands(filterInternal: boolean = false): Thenable { return extHostCommands.getCommands(filterInternal); @@ -578,7 +566,7 @@ export function createApiFactory( openTextDocument(uriOrFileNameOrOptions?: vscode.Uri | string | { language?: string; content?: string; }) { let uriPromise: Thenable; - let options = uriOrFileNameOrOptions as { language?: string; content?: string; }; + const options = uriOrFileNameOrOptions as { language?: string; content?: string; }; if (typeof uriOrFileNameOrOptions === 'string') { uriPromise = Promise.resolve(URI.file(uriOrFileNameOrOptions)); } else if (uriOrFileNameOrOptions instanceof URI) { diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 2e74617076a..0d20d99f8e1 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -142,7 +142,7 @@ export interface MainThreadConfigurationShape extends IDisposable { } export interface MainThreadDiagnosticsShape extends IDisposable { - $changeMany(owner: string, entries: [UriComponents, IMarkerData[]][]): void; + $changeMany(owner: string, entries: [UriComponents, IMarkerData[] | undefined][]): void; $clear(owner: string): void; } @@ -368,7 +368,7 @@ export interface MainThreadProgressShape extends IDisposable { } export interface MainThreadTerminalServiceShape extends IDisposable { - $createTerminal(name?: string, shellPath?: string, shellArgs?: string[], cwd?: string | URI, env?: { [key: string]: string }, waitOnExit?: boolean, strictEnv?: boolean): Promise<{ id: number, name: string }>; + $createTerminal(name?: string, shellPath?: string, shellArgs?: string[], cwd?: string | UriComponents, env?: { [key: string]: string }, waitOnExit?: boolean, strictEnv?: boolean): Promise<{ id: number, name: string }>; $createTerminalRenderer(name: string): Promise; $dispose(terminalId: number): void; $hide(terminalId: number): void; @@ -460,7 +460,7 @@ export interface MainThreadQuickOpenShape extends IDisposable { $show(instance: number, options: IPickOptions, token: CancellationToken): Promise; $setItems(instance: number, items: TransferQuickPickItems[]): Promise; $setError(instance: number, error: Error): Promise; - $input(options: vscode.InputBoxOptions, validateInput: boolean, token: CancellationToken): Promise; + $input(options: vscode.InputBoxOptions | undefined, validateInput: boolean, token: CancellationToken): Promise; $createOrUpdate(params: TransferQuickInput): Promise; $dispose(id: number): Promise; } @@ -527,7 +527,7 @@ export interface ExtHostUrlsShape { } export interface MainThreadWorkspaceShape extends IDisposable { - $startFileSearch(includePattern: string | undefined, includeFolder: URI | undefined, excludePatternOrDisregardExcludes: string | false, maxResults: number, token: CancellationToken): Promise; + $startFileSearch(includePattern: string | undefined, includeFolder: UriComponents | undefined, excludePatternOrDisregardExcludes: string | false | undefined, maxResults: number, token: CancellationToken): Promise; $startTextSearch(query: IPatternInfo, options: ITextQueryBuilderOptions, requestId: number, token: CancellationToken): Promise; $checkExists(includes: string[], token: CancellationToken): Promise; $saveAll(includeUntitled?: boolean): Promise; @@ -582,7 +582,7 @@ export interface SCMProviderFeatures { count?: number; commitTemplate?: string; acceptInputCommand?: modes.Command; - statusBarCommands?: modes.Command[]; + statusBarCommands?: CommandDto[]; } export interface SCMGroupFeatures { @@ -860,9 +860,9 @@ export interface WorkspaceSymbolsDto extends IdObject { } export interface ResourceFileEditDto { - oldUri: UriComponents; - newUri: UriComponents; - options: IFileOperationOptions; + oldUri?: UriComponents; + newUri?: UriComponents; + options?: IFileOperationOptions; } export interface ResourceTextEditDto { @@ -965,7 +965,7 @@ export interface ShellLaunchConfigDto { name?: string; executable?: string; args?: string[] | string; - cwd?: string | URI; + cwd?: string | UriComponents; env?: { [key: string]: string | null }; } @@ -978,7 +978,7 @@ export interface ExtHostTerminalServiceShape { $acceptTerminalRendererInput(id: number, data: string): void; $acceptTerminalTitleChange(id: number, name: string): void; $acceptTerminalDimensions(id: number, cols: number, rows: number): void; - $createProcess(id: number, shellLaunchConfig: ShellLaunchConfigDto, activeWorkspaceRootUri: URI, cols: number, rows: number): void; + $createProcess(id: number, shellLaunchConfig: ShellLaunchConfigDto, activeWorkspaceRootUri: UriComponents, cols: number, rows: number): void; $acceptProcessInput(id: number, data: string): void; $acceptProcessResize(id: number, cols: number, rows: number): void; $acceptProcessShutdown(id: number, immediate: boolean): void; diff --git a/src/vs/workbench/api/node/extHostApiCommands.ts b/src/vs/workbench/api/node/extHostApiCommands.ts index 3e6559eb625..ffd3502e0b7 100644 --- a/src/vs/workbench/api/node/extHostApiCommands.ts +++ b/src/vs/workbench/api/node/extHostApiCommands.ts @@ -15,7 +15,7 @@ import * as search from 'vs/workbench/contrib/search/common/search'; import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands'; import { ExtHostCommands } from 'vs/workbench/api/node/extHostCommands'; import { CustomCodeAction } from 'vs/workbench/api/node/extHostLanguageFeatures'; -import { ICommandsExecutor, PreviewHTMLAPICommand, OpenFolderAPICommand, DiffAPICommand, OpenAPICommand, RemoveFromRecentlyOpenedAPICommand, SetEditorLayoutAPICommand } from './apiCommands'; +import { ICommandsExecutor, OpenFolderAPICommand, DiffAPICommand, OpenAPICommand, RemoveFromRecentlyOpenedAPICommand, SetEditorLayoutAPICommand } from './apiCommands'; import { EditorGroupLayout } from 'vs/workbench/services/editor/common/editorGroupsService'; import { isFalsyOrEmpty } from 'vs/base/common/arrays'; @@ -219,20 +219,6 @@ export class ExtHostApiCommands { }; }; - this._register(PreviewHTMLAPICommand.ID, adjustHandler(PreviewHTMLAPICommand.execute), { - description: ` - Render the HTML of the resource in an editor view. - - See [working with the HTML preview](https://code.visualstudio.com/docs/extensionAPI/vscode-api-commands#working-with-the-html-preview) for more information about the HTML preview's integration with the editor and for best practices for extension authors. - `, - args: [ - { name: 'uri', description: 'Uri of the resource to preview.', constraint: (value: any) => value instanceof URI || typeof value === 'string' }, - { name: 'column', description: '(optional) Column in which to preview.', constraint: (value: any) => typeof value === 'undefined' || (typeof value === 'number' && typeof types.ViewColumn[value] === 'string') }, - { name: 'label', description: '(optional) An human readable string that is used as title for the preview.', constraint: (v: any) => typeof v === 'string' || typeof v === 'undefined' }, - { name: 'options', description: '(optional) Options for controlling webview environment.', constraint: (v: any) => typeof v === 'object' || typeof v === 'undefined' } - ] - }); - this._register(OpenFolderAPICommand.ID, adjustHandler(OpenFolderAPICommand.execute), { description: 'Open a folder or workspace in the current window or new window depending on the newWindow argument. Note that opening in the same window will shutdown the current extension host process and start a new one on the given folder/workspace unless the newWindow parameter is set to true.', args: [ @@ -277,7 +263,7 @@ export class ExtHostApiCommands { // --- command impl private _register(id: string, handler: (...args: any[]) => any, description?: ICommandHandlerDescription): void { - let disposable = this._commands.registerCommand(false, id, handler, this, description); + const disposable = this._commands.registerCommand(false, id, handler, this, description); this._disposables.push(disposable); } @@ -422,7 +408,7 @@ export class ExtHostApiCommands { } private _executeSelectionRangeProvider(resource: URI, positions: types.Position[]): Promise { - let pos = positions.map(typeConverters.Position.from); + const pos = positions.map(typeConverters.Position.from); const args = { resource, position: pos[0], @@ -457,7 +443,7 @@ export class ExtHostApiCommands { } class MergedInfo extends types.SymbolInformation implements vscode.DocumentSymbol { static to(symbol: modes.DocumentSymbol): MergedInfo { - let res = new MergedInfo( + const res = new MergedInfo( symbol.name, typeConverters.SymbolKind.to(symbol.kind), symbol.containerName, diff --git a/src/vs/workbench/api/node/extHostCLIServer.ts b/src/vs/workbench/api/node/extHostCLIServer.ts new file mode 100644 index 00000000000..a2311b7f69e --- /dev/null +++ b/src/vs/workbench/api/node/extHostCLIServer.ts @@ -0,0 +1,101 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { generateRandomPipeName } from 'vs/base/parts/ipc/node/ipc.net'; +import * as http from 'http'; +import * as fs from 'fs'; +import { ExtHostCommands } from 'vs/workbench/api/node/extHostCommands'; +import { IURIToOpen, URIType } from 'vs/platform/windows/common/windows'; +import { URI } from 'vs/base/common/uri'; +import { hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces'; + + +export class CLIServer { + + private _server: http.Server; + private _ipcHandlePath: string | undefined; + + constructor(private _commands: ExtHostCommands) { + this._server = http.createServer((req, res) => this.onRequest(req, res)); + this.setup().catch(err => { + console.error(err); + return ''; + }); + } + + public get ipcHandlePath() { + return this._ipcHandlePath; + } + + private async setup(): Promise { + this._ipcHandlePath = generateRandomPipeName(); + + try { + this._server.listen(this.ipcHandlePath); + this._server.on('error', err => console.error(err)); + } catch (err) { + console.error('Could not start open from terminal server.'); + } + + return this._ipcHandlePath; + } + private collectURIToOpen(strs: string[], typeHint: URIType, result: IURIToOpen[]): void { + if (Array.isArray(strs)) { + for (const s of strs) { + try { + result.push({ uri: URI.parse(s), typeHint }); + } catch (e) { + // ignore + } + } + } + } + + private onRequest(req: http.IncomingMessage, res: http.ServerResponse): void { + const chunks: string[] = []; + req.setEncoding('utf8'); + req.on('data', (d: string) => chunks.push(d)); + req.on('end', () => { + const data = JSON.parse(chunks.join('')); + switch (data.type) { + case 'open': + this.open(data, res); + break; + default: + res.writeHead(404); + res.write(`Unkown message type: ${data.type}`, err => { + if (err) { + console.error(err); + } + }); + res.end(); + break; + } + }); + } + + private open(data: any, res: http.ServerResponse) { + let { fileURIs, folderURIs, forceNewWindow, diffMode, addMode, forceReuseWindow } = data; + if (folderURIs && folderURIs.length || fileURIs && fileURIs.length) { + const urisToOpen: IURIToOpen[] = []; + this.collectURIToOpen(folderURIs, 'folder', urisToOpen); + this.collectURIToOpen(fileURIs, 'file', urisToOpen); + if (!forceReuseWindow && urisToOpen.some(o => o.typeHint === 'folder' || (o.typeHint === 'file' && hasWorkspaceFileExtension(o.uri.path)))) { + forceNewWindow = true; + } + this._commands.executeCommand('_files.windowOpen', { urisToOpen, forceNewWindow, diffMode, addMode, forceReuseWindow }); + } + res.writeHead(200); + res.end(); + } + + dispose(): void { + this._server.close(); + + if (this._ipcHandlePath && process.platform !== 'win32' && fs.existsSync(this._ipcHandlePath)) { + fs.unlinkSync(this._ipcHandlePath); + } + } +} diff --git a/src/vs/workbench/api/node/extHostCommands.ts b/src/vs/workbench/api/node/extHostCommands.ts index cc707471087..1a6b4162422 100644 --- a/src/vs/workbench/api/node/extHostCommands.ts +++ b/src/vs/workbench/api/node/extHostCommands.ts @@ -22,7 +22,7 @@ import { URI } from 'vs/base/common/uri'; interface CommandHandler { callback: Function; thisArg: any; - description: ICommandHandlerDescription; + description?: ICommandHandlerDescription; } export interface ArgumentProcessor { @@ -154,7 +154,7 @@ export class ExtHostCommands implements ExtHostCommandsShape { } try { - let result = callback.apply(thisArg, args); + const result = callback.apply(thisArg, args); return Promise.resolve(result); } catch (err) { this._logService.error(err, id); diff --git a/src/vs/workbench/api/node/extHostComments.ts b/src/vs/workbench/api/node/extHostComments.ts index a5dfa6d017c..a1f3156614a 100644 --- a/src/vs/workbench/api/node/extHostComments.ts +++ b/src/vs/workbench/api/node/extHostComments.ts @@ -344,7 +344,7 @@ export class ExtHostCommentThread implements vscode.CommentThread { } getComment(commentId: string): vscode.Comment | undefined { - let comments = this._comments.filter(comment => comment.commentId === commentId); + const comments = this._comments.filter(comment => comment.commentId === commentId); if (comments && comments.length) { return comments[0]; @@ -480,7 +480,7 @@ class ExtHostCommentControl implements vscode.CommentControl { } $onActiveCommentWidgetChange(commentThread: modes.CommentThread2, comment: modes.Comment | undefined, input: string) { - let extHostCommentThread = this._threads.get(commentThread.commentThreadHandle); + const extHostCommentThread = this._threads.get(commentThread.commentThreadHandle); const extHostCommentWidget = new ExtHostCommentWidget( this._proxy, diff --git a/src/vs/workbench/api/node/extHostDebugService.ts b/src/vs/workbench/api/node/extHostDebugService.ts index 32d93fbbf95..880a550dab4 100644 --- a/src/vs/workbench/api/node/extHostDebugService.ts +++ b/src/vs/workbench/api/node/extHostDebugService.ts @@ -255,7 +255,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { console.error('DebugConfigurationProvider.debugAdapterExecutable is deprecated and will be removed soon; please use DebugAdapterDescriptorFactory.createDebugAdapterDescriptor instead.'); } - let handle = this._configProviderHandleCounter++; + const handle = this._configProviderHandleCounter++; this._configProviders.push({ type, handle, provider }); this._debugServiceProxy.$registerDebugConfigurationProvider(type, @@ -286,7 +286,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { throw new Error(`a DebugAdapterDescriptorFactory can only be registered once per a type.`); } - let handle = this._adapterFactoryHandleCounter++; + const handle = this._adapterFactoryHandleCounter++; this._adapterFactories.push({ type, handle, factory }); this._debugServiceProxy.$registerDebugAdapterDescriptorFactory(type, handle); @@ -303,7 +303,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { return new Disposable(() => { }); } - let handle = this._trackerFactoryHandleCounter++; + const handle = this._trackerFactoryHandleCounter++; this._trackerFactories.push({ type, handle, factory }); this._debugServiceProxy.$registerDebugAdapterTrackerFactory(type, handle); @@ -494,9 +494,9 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { public $acceptBreakpointsDelta(delta: IBreakpointsDeltaDto): void { - let a: vscode.Breakpoint[] = []; - let r: vscode.Breakpoint[] = []; - let c: vscode.Breakpoint[] = []; + const a: vscode.Breakpoint[] = []; + const r: vscode.Breakpoint[] = []; + const c: vscode.Breakpoint[] = []; if (delta.added) { for (const bpd of delta.added) { @@ -528,7 +528,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { if (delta.changed) { for (const bpd of delta.changed) { - let bp = this._breakpoints.get(bpd.id); + const bp = this._breakpoints.get(bpd.id); if (bp) { if (bp instanceof FunctionBreakpoint && bpd.type === 'function') { const fbp = bp; @@ -554,7 +554,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { } public async $provideDebugConfigurations(configProviderHandle: number, folderUri: UriComponents | undefined): Promise { - let provider = this.getConfigProviderByHandle(configProviderHandle); + const provider = this.getConfigProviderByHandle(configProviderHandle); if (!provider) { return Promise.reject(new Error('no handler found')); } @@ -566,7 +566,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { } public async $resolveDebugConfiguration(configProviderHandle: number, folderUri: UriComponents | undefined, debugConfiguration: vscode.DebugConfiguration): Promise { - let provider = this.getConfigProviderByHandle(configProviderHandle); + const provider = this.getConfigProviderByHandle(configProviderHandle); if (!provider) { return Promise.reject(new Error('no handler found')); } @@ -579,7 +579,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { // TODO@AW legacy public async $legacyDebugAdapterExecutable(configProviderHandle: number, folderUri: UriComponents | undefined): Promise { - let provider = this.getConfigProviderByHandle(configProviderHandle); + const provider = this.getConfigProviderByHandle(configProviderHandle); if (!provider) { return Promise.reject(new Error('no handler found')); } @@ -591,7 +591,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { } public async $provideDebugAdapter(adapterProviderHandle: number, sessionDto: IDebugSessionDto): Promise { - let adapterProvider = this.getAdapterProviderByHandle(adapterProviderHandle); + const adapterProvider = this.getAdapterProviderByHandle(adapterProviderHandle); if (!adapterProvider) { return Promise.reject(new Error('no handler found')); } @@ -909,7 +909,7 @@ export class ExtHostVariableResolverService extends AbstractVariableResolverServ } return undefined; } - }); + }, process.env); } } diff --git a/src/vs/workbench/api/node/extHostDiagnostics.ts b/src/vs/workbench/api/node/extHostDiagnostics.ts index 791de4d587d..8a54b7e405c 100644 --- a/src/vs/workbench/api/node/extHostDiagnostics.ts +++ b/src/vs/workbench/api/node/extHostDiagnostics.ts @@ -8,9 +8,9 @@ import { IMarkerData, MarkerSeverity } from 'vs/platform/markers/common/markers' import { URI } from 'vs/base/common/uri'; import * as vscode from 'vscode'; import { MainContext, MainThreadDiagnosticsShape, ExtHostDiagnosticsShape, IMainContext } from './extHost.protocol'; -import { DiagnosticSeverity, Diagnostic } from './extHostTypes'; +import { DiagnosticSeverity } from './extHostTypes'; import * as converter from './extHostTypeConverters'; -import { mergeSort, equals } from 'vs/base/common/arrays'; +import { mergeSort } from 'vs/base/common/arrays'; import { Event, Emitter } from 'vs/base/common/event'; import { keys } from 'vs/base/common/map'; @@ -37,7 +37,7 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection { if (!this._isDisposed) { this._onDidChangeDiagnostics.fire(keys(this._data)); this._proxy.$clear(this._owner); - this._data = undefined; + this._data = undefined!; this._isDisposed = true; } } @@ -61,13 +61,9 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection { this._checkDisposed(); let toSync: vscode.Uri[] = []; - let hasChanged = true; if (first instanceof URI) { - // check if this has actually changed - hasChanged = hasChanged && !equals(diagnostics, this.get(first), Diagnostic.isEqual); - if (!diagnostics) { // remove this entry this.delete(first); @@ -89,7 +85,7 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection { for (const tuple of first) { const [uri, diagnostics] = tuple; if (!lastUri || uri.toString() !== lastUri.toString()) { - if (lastUri && this._data.get(lastUri.toString()).length === 0) { + if (lastUri && this._data.get(lastUri.toString())!.length === 0) { this._data.delete(lastUri.toString()); } lastUri = uri; @@ -99,9 +95,15 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection { if (!diagnostics) { // [Uri, undefined] means clear this - this._data.get(uri.toString()).length = 0; + const currentDiagnostics = this._data.get(uri.toString()); + if (currentDiagnostics) { + currentDiagnostics.length = 0; + } } else { - this._data.get(uri.toString()).push(...diagnostics); + const currentDiagnostics = this._data.get(uri.toString()); + if (currentDiagnostics) { + currentDiagnostics.push(...diagnostics); + } } } } @@ -109,19 +111,11 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection { // send event for extensions this._onDidChangeDiagnostics.fire(toSync); - // if nothing has changed then there is nothing else to do - // we have updated the diagnostics but we don't send a message - // to the renderer. tho we have still send an event for other - // extensions because the diagnostic might carry more information - // than known to us - if (!hasChanged) { - return; - } // compute change and send to main side const entries: [URI, IMarkerData[]][] = []; for (let uri of toSync) { - let marker: IMarkerData[] | undefined; - let diagnostics = this._data.get(uri.toString()); + let marker: IMarkerData[] = []; + const diagnostics = this._data.get(uri.toString()); if (diagnostics) { // no more than N diagnostics per file @@ -149,7 +143,7 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection { endColumn: marker[marker.length - 1].endColumn }); } else { - marker = diagnostics.map(converter.Diagnostic.from); + marker = diagnostics.map(diag => converter.Diagnostic.from(diag)); } } @@ -176,18 +170,18 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection { forEach(callback: (uri: URI, diagnostics: vscode.Diagnostic[], collection: DiagnosticCollection) => any, thisArg?: any): void { this._checkDisposed(); this._data.forEach((value, key) => { - let uri = URI.parse(key); + const uri = URI.parse(key); callback.apply(thisArg, [uri, this.get(uri), this]); }); } get(uri: URI): vscode.Diagnostic[] { this._checkDisposed(); - let result = this._data.get(uri.toString()); + const result = this._data.get(uri.toString()); if (Array.isArray(result)) { return Object.freeze(result.slice(0)); } - return undefined; + return []; } has(uri: URI): boolean { @@ -230,8 +224,8 @@ export class ExtHostDiagnostics implements ExtHostDiagnosticsShape { } static _mapper(last: (vscode.Uri | string)[]): { uris: vscode.Uri[] } { - let uris: vscode.Uri[] = []; - let map = new Set(); + const uris: vscode.Uri[] = []; + const map = new Set(); for (const uri of last) { if (typeof uri === 'string') { if (!map.has(uri)) { @@ -291,8 +285,8 @@ export class ExtHostDiagnostics implements ExtHostDiagnosticsShape { if (resource) { return this._getDiagnostics(resource); } else { - let index = new Map(); - let res: [vscode.Uri, vscode.Diagnostic[]][] = []; + const index = new Map(); + const res: [vscode.Uri, vscode.Diagnostic[]][] = []; this._collections.forEach(collection => { collection.forEach((uri, diagnostics) => { let idx = index.get(uri.toString()); diff --git a/src/vs/workbench/api/node/extHostDocumentContentProviders.ts b/src/vs/workbench/api/node/extHostDocumentContentProviders.ts index 89c7332e1b1..3ffd9a81fda 100644 --- a/src/vs/workbench/api/node/extHostDocumentContentProviders.ts +++ b/src/vs/workbench/api/node/extHostDocumentContentProviders.ts @@ -52,13 +52,13 @@ export class ExtHostDocumentContentProvider implements ExtHostDocumentContentPro this._logService.warn(`Provider for scheme '${scheme}' is firing event for schema '${uri.scheme}' which will be IGNORED`); return; } - if (this._documentsAndEditors.getDocument(uri.toString())) { + if (this._documentsAndEditors.getDocument(uri)) { this.$provideTextDocumentContent(handle, uri).then(value => { if (!value) { return; } - const document = this._documentsAndEditors.getDocument(uri.toString()); + const document = this._documentsAndEditors.getDocument(uri); if (!document) { // disposed in the meantime return; diff --git a/src/vs/workbench/api/node/extHostDocumentData.ts b/src/vs/workbench/api/node/extHostDocumentData.ts index c05dbb6855d..e875c30af2b 100644 --- a/src/vs/workbench/api/node/extHostDocumentData.ts +++ b/src/vs/workbench/api/node/extHostDocumentData.ts @@ -105,7 +105,7 @@ export class ExtHostDocumentData extends MirrorTextModel { } private _getTextInRange(_range: vscode.Range): string { - let range = this._validateRange(_range); + const range = this._validateRange(_range); if (range.isEmpty) { return ''; @@ -115,7 +115,7 @@ export class ExtHostDocumentData extends MirrorTextModel { return this._lines[range.start.line].substring(range.start.character, range.end.character); } - let lineEnding = this._eol, + const lineEnding = this._eol, startLineIndex = range.start.line, endLineIndex = range.end.line, resultLines: string[] = []; @@ -178,9 +178,9 @@ export class ExtHostDocumentData extends MirrorTextModel { offset = Math.max(0, offset); this._ensureLineStarts(); - let out = this._lineStarts!.getIndexOf(offset); + const out = this._lineStarts!.getIndexOf(offset); - let lineLength = this._lines[out.index].length; + const lineLength = this._lines[out.index].length; // Ensure we return a valid position return new Position(out.index, Math.min(out.remainder, lineLength)); @@ -193,8 +193,8 @@ export class ExtHostDocumentData extends MirrorTextModel { throw new Error('Invalid argument'); } - let start = this._validatePosition(range.start); - let end = this._validatePosition(range.end); + const start = this._validatePosition(range.start); + const end = this._validatePosition(range.end); if (start === range.start && end === range.end) { return range; @@ -221,7 +221,7 @@ export class ExtHostDocumentData extends MirrorTextModel { hasChanged = true; } else { - let maxCharacter = this._lines[line].length; + const maxCharacter = this._lines[line].length; if (character < 0) { character = 0; hasChanged = true; @@ -239,7 +239,7 @@ export class ExtHostDocumentData extends MirrorTextModel { } private _getWordRangeAtPosition(_position: vscode.Position, regexp?: RegExp): vscode.Range | undefined { - let position = this._validatePosition(_position); + const position = this._validatePosition(_position); if (!regexp) { // use default when custom-regexp isn't provided @@ -251,7 +251,7 @@ export class ExtHostDocumentData extends MirrorTextModel { regexp = getWordDefinitionFor(this._languageId); } - let wordAtText = getWordAtText( + const wordAtText = getWordAtText( position.character + 1, ensureValidWordDefinition(regexp), this._lines[position.line], diff --git a/src/vs/workbench/api/node/extHostDocumentSaveParticipant.ts b/src/vs/workbench/api/node/extHostDocumentSaveParticipant.ts index 731a9309798..2225d4dbc06 100644 --- a/src/vs/workbench/api/node/extHostDocumentSaveParticipant.ts +++ b/src/vs/workbench/api/node/extHostDocumentSaveParticipant.ts @@ -53,14 +53,14 @@ export class ExtHostDocumentSaveParticipant implements ExtHostDocumentSavePartic const entries = this._callbacks.toArray(); let didTimeout = false; - let didTimeoutHandle = setTimeout(() => didTimeout = true, this._thresholds.timeout); + const didTimeoutHandle = setTimeout(() => didTimeout = true, this._thresholds.timeout); const promise = sequence(entries.map(listener => { return () => { if (didTimeout) { // timeout - no more listeners - return undefined; + return Promise.resolve(); } const document = this._documents.getDocument(resource); @@ -169,7 +169,6 @@ export class ExtHostDocumentSaveParticipant implements ExtHostDocumentSavePartic return this._mainThreadEditors.$tryApplyWorkspaceEdit({ edits: [resourceEdit] }); } - // TODO@joh bubble this to listener? return Promise.reject(new Error('concurrent_edits')); }); } diff --git a/src/vs/workbench/api/node/extHostDocuments.ts b/src/vs/workbench/api/node/extHostDocuments.ts index dfc39581f84..2383a6eb83a 100644 --- a/src/vs/workbench/api/node/extHostDocuments.ts +++ b/src/vs/workbench/api/node/extHostDocuments.ts @@ -60,7 +60,7 @@ export class ExtHostDocuments implements ExtHostDocumentsShape { if (!resource) { return undefined; } - const data = this._documentsAndEditors.getDocument(resource.toString()); + const data = this._documentsAndEditors.getDocument(resource); if (data) { return data; } @@ -77,7 +77,7 @@ export class ExtHostDocuments implements ExtHostDocumentsShape { public ensureDocumentData(uri: URI): Promise { - let cached = this._documentsAndEditors.getDocument(uri.toString()); + const cached = this._documentsAndEditors.getDocument(uri); if (cached) { return Promise.resolve(cached); } @@ -86,7 +86,7 @@ export class ExtHostDocuments implements ExtHostDocumentsShape { if (!promise) { promise = this._proxy.$tryOpenDocument(uri).then(() => { this._documentLoader.delete(uri.toString()); - return this._documentsAndEditors.getDocument(uri.toString()); + return this._documentsAndEditors.getDocument(uri); }, err => { this._documentLoader.delete(uri.toString()); return Promise.reject(err); @@ -103,9 +103,10 @@ export class ExtHostDocuments implements ExtHostDocumentsShape { public $acceptModelModeChanged(uriComponents: UriComponents, oldModeId: string, newModeId: string): void { const uri = URI.revive(uriComponents); - const strURL = uri.toString(); - let data = this._documentsAndEditors.getDocument(strURL); - + const data = this._documentsAndEditors.getDocument(uri); + if (!data) { + throw new Error('unknown document'); + } // Treat a mode change as a remove + add this._onDidRemoveDocument.fire(data.document); @@ -115,16 +116,20 @@ export class ExtHostDocuments implements ExtHostDocumentsShape { public $acceptModelSaved(uriComponents: UriComponents): void { const uri = URI.revive(uriComponents); - const strURL = uri.toString(); - let data = this._documentsAndEditors.getDocument(strURL); + const data = this._documentsAndEditors.getDocument(uri); + if (!data) { + throw new Error('unknown document'); + } this.$acceptDirtyStateChanged(uriComponents, false); this._onDidSaveDocument.fire(data.document); } public $acceptDirtyStateChanged(uriComponents: UriComponents, isDirty: boolean): void { const uri = URI.revive(uriComponents); - const strURL = uri.toString(); - let data = this._documentsAndEditors.getDocument(strURL); + const data = this._documentsAndEditors.getDocument(uri); + if (!data) { + throw new Error('unknown document'); + } data._acceptIsDirty(isDirty); this._onDidChangeDocument.fire({ document: data.document, @@ -134,8 +139,10 @@ export class ExtHostDocuments implements ExtHostDocumentsShape { public $acceptModelChanged(uriComponents: UriComponents, events: IModelChangedEvent, isDirty: boolean): void { const uri = URI.revive(uriComponents); - const strURL = uri.toString(); - let data = this._documentsAndEditors.getDocument(strURL); + const data = this._documentsAndEditors.getDocument(uri); + if (!data) { + throw new Error('unknown document'); + } data._acceptIsDirty(isDirty); data.onEvents(events); this._onDidChangeDocument.fire({ diff --git a/src/vs/workbench/api/node/extHostDocumentsAndEditors.ts b/src/vs/workbench/api/node/extHostDocumentsAndEditors.ts index 4048e3a936b..c0e26886a58 100644 --- a/src/vs/workbench/api/node/extHostDocumentsAndEditors.ts +++ b/src/vs/workbench/api/node/extHostDocumentsAndEditors.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as assert from 'assert'; +import * as assert from 'vs/base/common/assert'; import { Emitter, Event } from 'vs/base/common/event'; import { dispose } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; @@ -17,7 +17,7 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha private _disposables: Disposable[] = []; - private _activeEditorId: string; + private _activeEditorId: string | null; private readonly _editors = new Map(); private readonly _documents = new Map(); @@ -25,12 +25,12 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha private readonly _onDidAddDocuments = new Emitter(); private readonly _onDidRemoveDocuments = new Emitter(); private readonly _onDidChangeVisibleTextEditors = new Emitter(); - private readonly _onDidChangeActiveTextEditor = new Emitter(); + private readonly _onDidChangeActiveTextEditor = new Emitter(); readonly onDidAddDocuments: Event = this._onDidAddDocuments.event; readonly onDidRemoveDocuments: Event = this._onDidRemoveDocuments.event; readonly onDidChangeVisibleTextEditors: Event = this._onDidChangeVisibleTextEditors.event; - readonly onDidChangeActiveTextEditor: Event = this._onDidChangeActiveTextEditor.event; + readonly onDidChangeActiveTextEditor: Event = this._onDidChangeActiveTextEditor.event; constructor( private readonly _mainContext: IMainContext, @@ -93,14 +93,14 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha assert.ok(this._documents.has(resource.toString()), `document '${resource}' does not exist`); assert.ok(!this._editors.has(data.id), `editor '${data.id}' already exists!`); - const documentData = this._documents.get(resource.toString()); + const documentData = this._documents.get(resource.toString())!; const editor = new ExtHostTextEditor( this._mainContext.getProxy(MainContext.MainThreadTextEditors), data.id, documentData, data.selections.map(typeConverters.Selection.to), data.options, - data.visibleRanges.map(typeConverters.Range.to), + data.visibleRanges.map(range => typeConverters.Range.to(range)), typeof data.editorPosition === 'number' ? typeConverters.ViewColumn.to(data.editorPosition) : undefined ); this._editors.set(data.id, editor); @@ -131,8 +131,8 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha } } - getDocument(strUrl: string): ExtHostDocumentData { - return this._documents.get(strUrl); + getDocument(uri: URI): ExtHostDocumentData | undefined { + return this._documents.get(uri.toString()); } allDocuments(): ExtHostDocumentData[] { @@ -141,7 +141,7 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha return result; } - getEditor(id: string): ExtHostTextEditor { + getEditor(id: string): ExtHostTextEditor | undefined { return this._editors.get(id); } diff --git a/src/vs/workbench/api/node/extHostExtensionActivator.ts b/src/vs/workbench/api/node/extHostExtensionActivator.ts index b6f50845288..a248e0e9f69 100644 --- a/src/vs/workbench/api/node/extHostExtensionActivator.ts +++ b/src/vs/workbench/api/node/extHostExtensionActivator.ts @@ -238,14 +238,14 @@ export class ExtensionsActivator { if (this._alreadyActivatedEvents[activationEvent]) { return NO_OP_VOID_PROMISE; } - let activateExtensions = this._registry.getExtensionDescriptionsForActivationEvent(activationEvent); + const activateExtensions = this._registry.getExtensionDescriptionsForActivationEvent(activationEvent); return this._activateExtensions(activateExtensions.map(e => e.identifier), reason).then(() => { this._alreadyActivatedEvents[activationEvent] = true; }); } public activateById(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise { - let desc = this._registry.getExtensionDescription(extensionId); + const desc = this._registry.getExtensionDescription(extensionId); if (!desc) { throw new Error('Extension `' + extensionId + '` is not known'); } @@ -264,7 +264,7 @@ export class ExtensionsActivator { } const currentExtension = this._registry.getExtensionDescription(currentExtensionId)!; - let depIds = (typeof currentExtension.extensionDependencies === 'undefined' ? [] : currentExtension.extensionDependencies); + const depIds = (typeof currentExtension.extensionDependencies === 'undefined' ? [] : currentExtension.extensionDependencies); let currentExtensionGetsGreenLight = true; for (let j = 0, lenJ = depIds.length; j < lenJ; j++) { @@ -330,7 +330,7 @@ export class ExtensionsActivator { return Promise.resolve(undefined); } - let greenMap: { [id: string]: ExtensionIdentifier; } = Object.create(null), + const greenMap: { [id: string]: ExtensionIdentifier; } = Object.create(null), red: ExtensionIdentifier[] = []; for (let i = 0, len = extensionIds.length; i < len; i++) { @@ -345,7 +345,7 @@ export class ExtensionsActivator { } } - let green = Object.keys(greenMap).map(id => greenMap[id]); + const green = Object.keys(greenMap).map(id => greenMap[id]); // console.log('greenExtensions: ', green.map(p => p.id)); // console.log('redExtensions: ', red.map(p => p.id)); diff --git a/src/vs/workbench/api/node/extHostExtensionService.ts b/src/vs/workbench/api/node/extHostExtensionService.ts index cada07e1ff7..8114ceb234a 100644 --- a/src/vs/workbench/api/node/extHostExtensionService.ts +++ b/src/vs/workbench/api/node/extHostExtensionService.ts @@ -5,6 +5,7 @@ import * as nls from 'vs/nls'; import * as path from 'vs/base/common/path'; +import { originalFSPath } from 'vs/base/common/resources'; import { Barrier } from 'vs/base/common/async'; import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; import { TernarySearchTree } from 'vs/base/common/map'; @@ -115,6 +116,9 @@ class ExtensionStoragePath { return Promise.resolve(undefined); } + if (!this._environment.appSettingsHome) { + return undefined; + } const storageName = this._workspace.id; const storagePath = path.join(this._environment.appSettingsHome.fsPath, 'workspaceStorage', storageName); @@ -221,7 +225,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { actualActivateExtension: async (extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise => { if (hostExtensions.has(ExtensionIdentifier.toKey(extensionId))) { - let activationEvent = (reason instanceof ExtensionActivatedByEvent ? reason.activationEvent : null); + const activationEvent = (reason instanceof ExtensionActivatedByEvent ? reason.activationEvent : null); await this._mainThreadExtensionsProxy.$activateExtension(extensionId, activationEvent); return new HostExtension(); } @@ -341,7 +345,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { return result; } - let extension = this._activator.getActivatedExtension(extensionId); + const extension = this._activator.getActivatedExtension(extensionId); if (!extension) { return result; } @@ -378,7 +382,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { this._mainThreadExtensionsProxy.$onWillActivateExtension(extensionDescription.identifier); return this._doActivateExtension(extensionDescription, reason).then((activatedExtension) => { const activationTimes = activatedExtension.activationTimes; - let activationEvent = (reason instanceof ExtensionActivatedByEvent ? reason.activationEvent : null); + const activationEvent = (reason instanceof ExtensionActivatedByEvent ? reason.activationEvent : null); this._mainThreadExtensionsProxy.$onDidActivateExtension(extensionDescription.identifier, activationTimes.startup, activationTimes.codeLoadingTime, activationTimes.activateCallTime, activationTimes.activateResolvedTime, activationEvent); this._logExtensionActivationTimes(extensionDescription, reason, 'success', activationTimes); return activatedExtension; @@ -390,7 +394,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { } private _logExtensionActivationTimes(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason, outcome: string, activationTimes?: ExtensionActivationTimes) { - let event = getTelemetryActivationEvent(extensionDescription, reason); + const event = getTelemetryActivationEvent(extensionDescription, reason); /* __GDPR__ "extensionActivationTimes" : { "${include}": [ @@ -408,7 +412,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { } private _doActivateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise { - let event = getTelemetryActivationEvent(extensionDescription, reason); + const event = getTelemetryActivationEvent(extensionDescription, reason); /* __GDPR__ "activatePlugin" : { "${include}": [ @@ -435,8 +439,8 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { private _loadExtensionContext(extensionDescription: IExtensionDescription): Promise { - let globalState = new ExtensionMemento(extensionDescription.identifier.value, true, this._storage); - let workspaceState = new ExtensionMemento(extensionDescription.identifier.value, false, this._storage); + const globalState = new ExtensionMemento(extensionDescription.identifier.value, true, this._storage); + const workspaceState = new ExtensionMemento(extensionDescription.identifier.value, false, this._storage); this._extHostLogService.trace(`ExtensionService#loadExtensionContext ${extensionDescription.identifier.value}`); return Promise.all([ @@ -614,7 +618,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { return Promise.resolve(undefined); } - const extensionTestsPath = extensionTestsLocationURI.fsPath; + const extensionTestsPath = originalFSPath(extensionTestsLocationURI); // Require the test runner via node require from the provided path let testRunner: ITestRunner | undefined; @@ -759,7 +763,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { } public async $test_down(size: number): Promise { - let b = Buffer.alloc(size, Math.random() % 256); + const b = Buffer.alloc(size, Math.random() % 256); return b; } @@ -795,7 +799,7 @@ function getTelemetryActivationEvent(extensionDescription: IExtensionDescription "reason": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } } */ - let event = { + const event = { id: extensionDescription.identifier.value, name: extensionDescription.name, extensionVersion: extensionDescription.version, diff --git a/src/vs/workbench/api/node/extHostFileSystem.ts b/src/vs/workbench/api/node/extHostFileSystem.ts index 2d6c3792d51..8a83afc3ca4 100644 --- a/src/vs/workbench/api/node/extHostFileSystem.ts +++ b/src/vs/workbench/api/node/extHostFileSystem.ts @@ -20,7 +20,7 @@ import { CharCode } from 'vs/base/common/charCode'; class FsLinkProvider { private _schemes: string[] = []; - private _stateMachine: StateMachine; + private _stateMachine?: StateMachine; add(scheme: string): void { this._stateMachine = undefined; @@ -28,7 +28,7 @@ class FsLinkProvider { } delete(scheme: string): void { - let idx = this._schemes.indexOf(scheme); + const idx = this._schemes.indexOf(scheme); if (idx >= 0) { this._schemes.splice(idx, 1); this._stateMachine = undefined; @@ -94,7 +94,7 @@ class FsLinkProvider { }, this._stateMachine); for (const link of links) { - let docLink = typeConverter.DocumentLink.to(link); + const docLink = typeConverter.DocumentLink.to(link); if (docLink.target) { result.push(docLink); } @@ -172,7 +172,7 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape { this._proxy.$registerFileSystemProvider(handle, scheme, capabilites); const subscription = provider.onDidChangeFile(event => { - let mapped: IFileChangeDto[] = []; + const mapped: IFileChangeDto[] = []; for (const e of event) { let { uri: resource, type } = e; if (resource.scheme !== scheme) { @@ -190,6 +190,8 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape { case FileChangeType.Deleted: newType = files.FileChangeType.DELETED; break; + default: + throw new Error('Unknown FileChangeType'); } mapped.push({ resource, type: newType }); } @@ -219,65 +221,51 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape { return { type, ctime, mtime, size }; } - private _checkProviderExists(handle: number): void { - if (!this._fsProvider.has(handle)) { - const err = new Error(); - err.name = 'ENOPRO'; - err.message = `no provider`; - throw err; - } - } - $stat(handle: number, resource: UriComponents): Promise { - this._checkProviderExists(handle); - return Promise.resolve(this._fsProvider.get(handle).stat(URI.revive(resource))).then(ExtHostFileSystem._asIStat); + return Promise.resolve(this.getProvider(handle).stat(URI.revive(resource))).then(ExtHostFileSystem._asIStat); } $readdir(handle: number, resource: UriComponents): Promise<[string, files.FileType][]> { - this._checkProviderExists(handle); - return Promise.resolve(this._fsProvider.get(handle).readDirectory(URI.revive(resource))); + return Promise.resolve(this.getProvider(handle).readDirectory(URI.revive(resource))); } $readFile(handle: number, resource: UriComponents): Promise { - this._checkProviderExists(handle); - return Promise.resolve(this._fsProvider.get(handle).readFile(URI.revive(resource))).then(data => { + return Promise.resolve(this.getProvider(handle).readFile(URI.revive(resource))).then(data => { return Buffer.isBuffer(data) ? data : Buffer.from(data.buffer, data.byteOffset, data.byteLength); }); } $writeFile(handle: number, resource: UriComponents, content: Buffer, opts: files.FileWriteOptions): Promise { - this._checkProviderExists(handle); - return Promise.resolve(this._fsProvider.get(handle).writeFile(URI.revive(resource), content, opts)); + return Promise.resolve(this.getProvider(handle).writeFile(URI.revive(resource), content, opts)); } $delete(handle: number, resource: UriComponents, opts: files.FileDeleteOptions): Promise { - this._checkProviderExists(handle); - return Promise.resolve(this._fsProvider.get(handle).delete(URI.revive(resource), opts)); + return Promise.resolve(this.getProvider(handle).delete(URI.revive(resource), opts)); } $rename(handle: number, oldUri: UriComponents, newUri: UriComponents, opts: files.FileOverwriteOptions): Promise { - this._checkProviderExists(handle); - return Promise.resolve(this._fsProvider.get(handle).rename(URI.revive(oldUri), URI.revive(newUri), opts)); + return Promise.resolve(this.getProvider(handle).rename(URI.revive(oldUri), URI.revive(newUri), opts)); } $copy(handle: number, oldUri: UriComponents, newUri: UriComponents, opts: files.FileOverwriteOptions): Promise { - this._checkProviderExists(handle); - return Promise.resolve(this._fsProvider.get(handle).copy(URI.revive(oldUri), URI.revive(newUri), opts)); + const provider = this.getProvider(handle); + if (!provider.copy) { + throw new Error('FileSystemProvider does not implement "copy"'); + } + return Promise.resolve(provider.copy(URI.revive(oldUri), URI.revive(newUri), opts)); } $mkdir(handle: number, resource: UriComponents): Promise { - this._checkProviderExists(handle); - return Promise.resolve(this._fsProvider.get(handle).createDirectory(URI.revive(resource))); + return Promise.resolve(this.getProvider(handle).createDirectory(URI.revive(resource))); } $watch(handle: number, session: number, resource: UriComponents, opts: files.IWatchOptions): void { - this._checkProviderExists(handle); - let subscription = this._fsProvider.get(handle).watch(URI.revive(resource), opts); + const subscription = this.getProvider(handle).watch(URI.revive(resource), opts); this._watches.set(session, subscription); } - $unwatch(session: number): void { - let subscription = this._watches.get(session); + $unwatch(_handle: number, session: number): void { + const subscription = this._watches.get(session); if (subscription) { subscription.dispose(); this._watches.delete(session); @@ -285,26 +273,48 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape { } $open(handle: number, resource: UriComponents, opts: files.FileOpenOptions): Promise { - this._checkProviderExists(handle); - return Promise.resolve(this._fsProvider.get(handle).open(URI.revive(resource), opts)); + const provider = this.getProvider(handle); + if (!provider.open) { + throw new Error('FileSystemProvider does not implement "open"'); + } + return Promise.resolve(provider.open(URI.revive(resource), opts)); } $close(handle: number, fd: number): Promise { - this._checkProviderExists(handle); - return Promise.resolve(this._fsProvider.get(handle).close(fd)); + const provider = this.getProvider(handle); + if (!provider.close) { + throw new Error('FileSystemProvider does not implement "close"'); + } + return Promise.resolve(provider.close(fd)); } $read(handle: number, fd: number, pos: number, length: number): Promise { - this._checkProviderExists(handle); + const provider = this.getProvider(handle); + if (!provider.read) { + throw new Error('FileSystemProvider does not implement "read"'); + } const data = Buffer.allocUnsafe(length); - return Promise.resolve(this._fsProvider.get(handle).read(fd, pos, data, 0, length)).then(read => { + return Promise.resolve(provider.read(fd, pos, data, 0, length)).then(read => { return data.slice(0, read); // don't send zeros }); } $write(handle: number, fd: number, pos: number, data: Buffer): Promise { - this._checkProviderExists(handle); - return Promise.resolve(this._fsProvider.get(handle).write(fd, pos, data, 0, data.length)); + const provider = this.getProvider(handle); + if (!provider.write) { + throw new Error('FileSystemProvider does not implement "write"'); + } + return Promise.resolve(provider.write(fd, pos, data, 0, data.length)); } + private getProvider(handle: number): vscode.FileSystemProvider { + const provider = this._fsProvider.get(handle); + if (!provider) { + const err = new Error(); + err.name = 'ENOPRO'; + err.message = `no provider`; + throw err; + } + return provider; + } } diff --git a/src/vs/workbench/api/node/extHostFileSystemEventService.ts b/src/vs/workbench/api/node/extHostFileSystemEventService.ts index f7f682b6281..6d709b3a107 100644 --- a/src/vs/workbench/api/node/extHostFileSystemEventService.ts +++ b/src/vs/workbench/api/node/extHostFileSystemEventService.ts @@ -49,10 +49,10 @@ class FileSystemWatcher implements vscode.FileSystemWatcher { const parsedPattern = parse(globPattern); - let subscription = dispatcher(events => { + const subscription = dispatcher(events => { if (!ignoreCreateEvents) { for (let created of events.created) { - let uri = URI.revive(created); + const uri = URI.revive(created); if (parsedPattern(uri.fsPath)) { this._onDidCreate.fire(uri); } @@ -60,7 +60,7 @@ class FileSystemWatcher implements vscode.FileSystemWatcher { } if (!ignoreChangeEvents) { for (let changed of events.changed) { - let uri = URI.revive(changed); + const uri = URI.revive(changed); if (parsedPattern(uri.fsPath)) { this._onDidChange.fire(uri); } @@ -68,7 +68,7 @@ class FileSystemWatcher implements vscode.FileSystemWatcher { } if (!ignoreDeleteEvents) { for (let deleted of events.deleted) { - let uri = URI.revive(deleted); + const uri = URI.revive(deleted); if (parsedPattern(uri.fsPath)) { this._onDidDelete.fire(uri); } @@ -169,7 +169,7 @@ export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServ } // flatten all WorkspaceEdits collected via waitUntil-call // and apply them in one go. - let allEdits = new Array>(); + const allEdits = new Array>(); for (let edit of edits) { if (edit) { // sparse array let { edits } = typeConverter.WorkspaceEdit.from(edit, this._extHostDocumentsAndEditors); diff --git a/src/vs/workbench/api/node/extHostLanguageFeatures.ts b/src/vs/workbench/api/node/extHostLanguageFeatures.ts index b4b5d85a067..d1cb405a281 100644 --- a/src/vs/workbench/api/node/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/node/extHostLanguageFeatures.ts @@ -65,10 +65,10 @@ class DocumentSymbolAdapter { } return res; }); - let res: modes.DocumentSymbol[] = []; - let parentStack: modes.DocumentSymbol[] = []; + const res: modes.DocumentSymbol[] = []; + const parentStack: modes.DocumentSymbol[] = []; for (const info of infos) { - let element = { + const element = { name: info.name || '!!MISSING: name!!', kind: typeConvert.SymbolKind.from(info.kind), containerName: info.containerName, @@ -83,7 +83,7 @@ class DocumentSymbolAdapter { res.push(element); break; } - let parent = parentStack[parentStack.length - 1]; + const parent = parentStack[parentStack.length - 1]; if (EditorRange.containsRange(parent.range, element.range) && !EditorRange.equalsRange(parent.range, element.range)) { parent.children.push(element); parentStack.push(element); @@ -111,7 +111,7 @@ class CodeLensAdapter { const doc = this._documents.getDocument(resource); return asPromise(() => this._provider.provideCodeLenses(doc, token)).then(lenses => { - let result: CodeLensDto[] = []; + const result: CodeLensDto[] = []; if (isNonEmptyArray(lenses)) { for (const lens of lenses) { const id = this._heapService.keep(lens); @@ -206,7 +206,7 @@ class DefinitionAdapter { provideDefinition(resource: URI, position: IPosition, token: CancellationToken): Promise { const doc = this._documents.getDocument(resource); - let pos = typeConvert.Position.to(position); + const pos = typeConvert.Position.to(position); return asPromise(() => this._provider.provideDefinition(doc, pos, token)).then(convertToLocationLinks); } } @@ -220,7 +220,7 @@ class DeclarationAdapter { provideDeclaration(resource: URI, position: IPosition, token: CancellationToken): Promise { const doc = this._documents.getDocument(resource); - let pos = typeConvert.Position.to(position); + const pos = typeConvert.Position.to(position); return asPromise(() => this._provider.provideDeclaration(doc, pos, token)).then(convertToLocationLinks); } } @@ -234,7 +234,7 @@ class ImplementationAdapter { provideImplementation(resource: URI, position: IPosition, token: CancellationToken): Promise { const doc = this._documents.getDocument(resource); - let pos = typeConvert.Position.to(position); + const pos = typeConvert.Position.to(position); return asPromise(() => this._provider.provideImplementation(doc, pos, token)).then(convertToLocationLinks); } } @@ -263,7 +263,7 @@ class HoverAdapter { public provideHover(resource: URI, position: IPosition, token: CancellationToken): Promise { const doc = this._documents.getDocument(resource); - let pos = typeConvert.Position.to(position); + const pos = typeConvert.Position.to(position); return asPromise(() => this._provider.provideHover(doc, pos, token)).then(value => { if (!value || isFalsyOrEmpty(value.contents)) { @@ -291,7 +291,7 @@ class DocumentHighlightAdapter { provideDocumentHighlights(resource: URI, position: IPosition, token: CancellationToken): Promise { const doc = this._documents.getDocument(resource); - let pos = typeConvert.Position.to(position); + const pos = typeConvert.Position.to(position); return asPromise(() => this._provider.provideDocumentHighlights(doc, pos, token)).then(value => { if (Array.isArray(value)) { @@ -311,7 +311,7 @@ class ReferenceAdapter { provideReferences(resource: URI, position: IPosition, context: modes.ReferenceContext, token: CancellationToken): Promise { const doc = this._documents.getDocument(resource); - let pos = typeConvert.Position.to(position); + const pos = typeConvert.Position.to(position); return asPromise(() => this._provider.provideReferences(doc, pos, context, token)).then(value => { if (Array.isArray(value)) { @@ -546,7 +546,7 @@ class RenameAdapter { provideRenameEdits(resource: URI, position: IPosition, newName: string, token: CancellationToken): Promise { const doc = this._documents.getDocument(resource); - let pos = typeConvert.Position.to(position); + const pos = typeConvert.Position.to(position); return asPromise(() => this._provider.provideRenameEdits(doc, pos, newName, token)).then(value => { if (!value) { @@ -554,7 +554,7 @@ class RenameAdapter { } return typeConvert.WorkspaceEdit.from(value); }, err => { - let rejectReason = RenameAdapter._asMessage(err); + const rejectReason = RenameAdapter._asMessage(err); if (rejectReason) { return { rejectReason, edits: undefined! }; } else { @@ -570,7 +570,7 @@ class RenameAdapter { } const doc = this._documents.getDocument(resource); - let pos = typeConvert.Position.to(position); + const pos = typeConvert.Position.to(position); return asPromise(() => this._provider.prepareRename!(doc, pos, token)).then(rangeOrLocation => { @@ -594,7 +594,7 @@ class RenameAdapter { } return { range: typeConvert.Range.from(range), text }; }, err => { - let rejectReason = RenameAdapter._asMessage(err); + const rejectReason = RenameAdapter._asMessage(err); if (rejectReason) { return { rejectReason, range: undefined!, text: undefined! }; } else { @@ -832,8 +832,8 @@ class LinkProviderAdapter { } const result: LinkDto[] = []; for (const link of links) { - let data = typeConvert.DocumentLink.from(link); - let id = this._heapService.keep(link); + const data = typeConvert.DocumentLink.from(link); + const id = this._heapService.keep(link); result.push(ObjectIdentifier.mixin(data, id)); } return result; @@ -936,7 +936,7 @@ class SelectionRangeAdapter { return []; } - let allResults: modes.SelectionRange[][] = []; + const allResults: modes.SelectionRange[][] = []; for (let i = 0; i < positions.length; i++) { const oneResult: modes.SelectionRange[] = []; allResults.push(oneResult); @@ -1065,7 +1065,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { t1 = Date.now(); this._logService.trace(`[${data.extension.identifier.value}] INVOKE provider '${(ctor as any).name}'`); } - let p = callback(data.adapter, data.extension); + const p = callback(data.adapter, data.extension); const extension = data.extension; if (extension) { Promise.resolve(p).then( @@ -1081,7 +1081,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { return Promise.reject(new Error('no adapter found')); } - private _addNewAdapter(adapter: Adapter, extension: IExtensionDescription): number { + private _addNewAdapter(adapter: Adapter, extension: IExtensionDescription | undefined): number { const handle = this._nextHandle(); this._adapter.set(handle, new AdapterData(adapter, extension)); return handle; @@ -1358,7 +1358,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { // --- links - registerDocumentLinkProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentLinkProvider): vscode.Disposable { + registerDocumentLinkProvider(extension: IExtensionDescription | undefined, selector: vscode.DocumentSelector, provider: vscode.DocumentLinkProvider): vscode.Disposable { const handle = this._addNewAdapter(new LinkProviderAdapter(this._documents, this._heapService, provider), extension); this._proxy.$registerDocumentLinkProvider(handle, this._transformDocumentSelector(selector)); return this._createDisposable(handle); diff --git a/src/vs/workbench/api/node/extHostMessageService.ts b/src/vs/workbench/api/node/extHostMessageService.ts index ea85d1d6d99..635f076610d 100644 --- a/src/vs/workbench/api/node/extHostMessageService.ts +++ b/src/vs/workbench/api/node/extHostMessageService.ts @@ -24,7 +24,7 @@ export class ExtHostMessageService { showMessage(extension: IExtensionDescription, severity: Severity, message: string, optionsOrFirstItem: vscode.MessageOptions | vscode.MessageItem, rest: vscode.MessageItem[]): Promise; showMessage(extension: IExtensionDescription, severity: Severity, message: string, optionsOrFirstItem: vscode.MessageOptions | string | vscode.MessageItem, rest: (string | vscode.MessageItem)[]): Promise { - let options: MainThreadMessageOptions = { extension }; + const options: MainThreadMessageOptions = { extension }; let items: (string | vscode.MessageItem)[]; if (typeof optionsOrFirstItem === 'string' || isMessageItem(optionsOrFirstItem)) { @@ -37,7 +37,7 @@ export class ExtHostMessageService { const commands: { title: string; isCloseAffordance: boolean; handle: number; }[] = []; for (let handle = 0; handle < items.length; handle++) { - let command = items[handle]; + const command = items[handle]; if (typeof command === 'string') { commands.push({ title: command, handle, isCloseAffordance: false }); } else if (typeof command === 'object') { diff --git a/src/vs/workbench/api/node/extHostQuickOpen.ts b/src/vs/workbench/api/node/extHostQuickOpen.ts index 7b8dd2432ee..9aedb8e14e6 100644 --- a/src/vs/workbench/api/node/extHostQuickOpen.ts +++ b/src/vs/workbench/api/node/extHostQuickOpen.ts @@ -15,6 +15,7 @@ import { URI } from 'vs/base/common/uri'; import { ThemeIcon, QuickInputButtons } from 'vs/workbench/api/node/extHostTypes'; import { isPromiseCanceledError } from 'vs/base/common/errors'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { coalesce } from 'vs/base/common/arrays'; export type Item = string | QuickPickItem; @@ -24,7 +25,7 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape { private _workspace: IExtHostWorkspaceProvider; private _commands: ExtHostCommands; - private _onDidSelectItem: (handle: number) => void; + private _onDidSelectItem?: (handle: number) => void; private _validateInput?: (input: string) => string | undefined | null | Thenable; private _sessions = new Map(); @@ -67,10 +68,10 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape { return itemsPromise.then(items => { - let pickItems: TransferQuickPickItems[] = []; + const pickItems: TransferQuickPickItems[] = []; for (let handle = 0; handle < items.length; handle++) { - let item = items[handle]; + const item = items[handle]; let label: string; let description: string | undefined; let detail: string | undefined; @@ -158,12 +159,15 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape { // ---- workspace folder picker - showWorkspaceFolderPick(options?: WorkspaceFolderPickOptions, token = CancellationToken.None): Promise { + showWorkspaceFolderPick(options?: WorkspaceFolderPickOptions, token = CancellationToken.None): Promise { return this._commands.executeCommand('_workbench.pickWorkspaceFolder', [options]).then(async (selectedFolder: WorkspaceFolder) => { if (!selectedFolder) { return undefined; } const workspaceFolders = await this._workspace.getWorkspaceFolders2(); + if (!workspaceFolders) { + return undefined; + } return workspaceFolders.filter(folder => folder.uri.toString() === selectedFolder.uri.toString())[0]; }); } @@ -382,7 +386,9 @@ class ExtHostQuickInput implements QuickInput { _fireDidTriggerButton(handle: number) { const button = this._handlesToButtons.get(handle); - this._onDidTriggerButtonEmitter.fire(button); + if (button) { + this._onDidTriggerButtonEmitter.fire(button); + } } _fireDidHide() { @@ -437,9 +443,13 @@ class ExtHostQuickInput implements QuickInput { } } -function getIconUris(iconPath: QuickInputButton['iconPath']) { +function getIconUris(iconPath: QuickInputButton['iconPath']): { dark: URI, light?: URI } | undefined { + const dark = getDarkIconUri(iconPath); const light = getLightIconUri(iconPath); - return { dark: getDarkIconUri(iconPath) || light, light }; + if (!light && !dark) { + return undefined; + } + return { dark: (dark || light)!, light }; } function getLightIconUri(iconPath: QuickInputButton['iconPath']) { @@ -563,13 +573,13 @@ class ExtHostQuickPick extends ExtHostQuickInput implem onDidChangeSelection = this._onDidChangeSelectionEmitter.event; _fireDidChangeActive(handles: number[]) { - const items = handles.map(handle => this._handlesToItems.get(handle)); + const items = coalesce(handles.map(handle => this._handlesToItems.get(handle))); this._activeItems = items; this._onDidChangeActiveEmitter.fire(items); } _fireDidChangeSelection(handles: number[]) { - const items = handles.map(handle => this._handlesToItems.get(handle)); + const items = coalesce(handles.map(handle => this._handlesToItems.get(handle))); this._selectedItems = items; this._onDidChangeSelectionEmitter.fire(items); } diff --git a/src/vs/workbench/api/node/extHostSCM.ts b/src/vs/workbench/api/node/extHostSCM.ts index 226b62e4396..f0a48e6c2a5 100644 --- a/src/vs/workbench/api/node/extHostSCM.ts +++ b/src/vs/workbench/api/node/extHostSCM.ts @@ -10,7 +10,7 @@ import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { asPromise } from 'vs/base/common/async'; import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import { ExtHostCommands } from 'vs/workbench/api/node/extHostCommands'; -import { MainContext, MainThreadSCMShape, SCMRawResource, SCMRawResourceSplice, SCMRawResourceSplices, IMainContext, ExtHostSCMShape } from './extHost.protocol'; +import { MainContext, MainThreadSCMShape, SCMRawResource, SCMRawResourceSplice, SCMRawResourceSplices, IMainContext, ExtHostSCMShape, CommandDto } from './extHost.protocol'; import { sortedDiff } from 'vs/base/common/arrays'; import { comparePaths } from 'vs/base/common/comparers'; import * as vscode from 'vscode'; @@ -23,7 +23,7 @@ type ProviderHandle = number; type GroupHandle = number; type ResourceStateHandle = number; -function getIconPath(decorations: vscode.SourceControlResourceThemableDecorations) { +function getIconPath(decorations?: vscode.SourceControlResourceThemableDecorations): string | undefined { if (!decorations) { return undefined; } else if (typeof decorations.iconPath === 'string') { @@ -60,7 +60,7 @@ function compareResourceStatesDecorations(a: vscode.SourceControlResourceDecorat } if (a.tooltip !== b.tooltip) { - return (a.tooltip || '').localeCompare(b.tooltip); + return (a.tooltip || '').localeCompare(b.tooltip || ''); } result = compareResourceThemableDecorations(a, b); @@ -205,7 +205,7 @@ export class ExtHostSCMInputBox implements vscode.SourceControlInputBox { return this._visible; } - set visible(visible: boolean | undefined) { + set visible(visible: boolean) { visible = !!visible; this._visible = visible; this._proxy.$setInputBoxVisibility(this._sourceControlHandle, visible); @@ -287,7 +287,7 @@ class ExtHostSourceControlResourceGroup implements vscode.SourceControlResourceG return Promise.resolve(undefined); } - return asPromise(() => this._commands.executeCommand(command.command, ...command.arguments)); + return asPromise(() => this._commands.executeCommand(command.command, ...(command.arguments || []))); } _takeResourceStateSnapshot(): SCMRawResourceSplice[] { @@ -309,11 +309,11 @@ class ExtHostSourceControlResourceGroup implements vscode.SourceControlResourceG this._resourceStatesCommandsMap.set(handle, r.command); } - if (lightIconPath || darkIconPath) { + if (lightIconPath) { icons.push(lightIconPath); } - if (darkIconPath !== lightIconPath) { + if (darkIconPath && (darkIconPath !== lightIconPath)) { icons.push(darkIconPath); } @@ -442,7 +442,7 @@ class ExtHostSourceControl implements vscode.SourceControl { this._statusBarCommands = statusBarCommands; - const internal = (statusBarCommands || []).map(c => this._commands.converter.toInternal(c)); + const internal = (statusBarCommands || []).map(c => this._commands.converter.toInternal(c)) as CommandDto[]; this._proxy.$updateSourceControl(this.handle, { statusBarCommands: internal }); } @@ -599,14 +599,12 @@ export class ExtHostSCM implements ExtHostSCMShape { } // Deprecated - getLastInputBox(extension: IExtensionDescription): ExtHostSCMInputBox { + getLastInputBox(extension: IExtensionDescription): ExtHostSCMInputBox | undefined { this.logService.trace('ExtHostSCM#getLastInputBox', extension.identifier.value); const sourceControls = this._sourceControlsByExtension.get(ExtensionIdentifier.toKey(extension.identifier)); const sourceControl = sourceControls && sourceControls[sourceControls.length - 1]; - const inputBox = sourceControl && sourceControl.inputBox; - - return inputBox; + return sourceControl && sourceControl.inputBox; } $provideOriginalResource(sourceControlHandle: number, uriComponents: UriComponents, token: CancellationToken): Promise { @@ -615,11 +613,12 @@ export class ExtHostSCM implements ExtHostSCMShape { const sourceControl = this._sourceControls.get(sourceControlHandle); - if (!sourceControl || !sourceControl.quickDiffProvider) { + if (!sourceControl || !sourceControl.quickDiffProvider || !sourceControl.quickDiffProvider.provideOriginalResource) { return Promise.resolve(null); } - return asPromise(() => sourceControl.quickDiffProvider.provideOriginalResource(uri, token)); + return asPromise(() => sourceControl.quickDiffProvider!.provideOriginalResource!(uri, token)) + .then(r => r || null); } $onInputBoxValueChange(sourceControlHandle: number, value: string): Promise { diff --git a/src/vs/workbench/api/node/extHostSearch.fileIndex.ts b/src/vs/workbench/api/node/extHostSearch.fileIndex.ts index 57ccbf500e8..9ca147e4cfe 100644 --- a/src/vs/workbench/api/node/extHostSearch.fileIndex.ts +++ b/src/vs/workbench/api/node/extHostSearch.fileIndex.ts @@ -116,7 +116,7 @@ export class FileIndexSearchEngine { } private searchInFolder(fq: IFolderQuery, onResult: (match: IInternalFileMatch) => void): Promise { - let cancellation = new CancellationTokenSource(); + const cancellation = new CancellationTokenSource(); return new Promise((resolve, reject) => { const options = this.getSearchOptionsForFolder(fq); const tree = this.initDirectoryTree(); @@ -511,10 +511,10 @@ export class FileIndexSearchManager { } // Pattern match on results - let results: IInternalFileMatch[] = []; + const results: IInternalFileMatch[] = []; const normalizedSearchValueLowercase = strings.stripWildcards(searchValue).toLowerCase(); for (let i = 0; i < complete.results.length; i++) { - let entry = complete.results[i]; + const entry = complete.results[i]; // Check if this entry is a match for the search value if (!strings.fuzzyContains(entry.relativePath!, normalizedSearchValueLowercase)) { diff --git a/src/vs/workbench/api/node/extHostStatusBar.ts b/src/vs/workbench/api/node/extHostStatusBar.ts index e5ff768408b..cd6c2066146 100644 --- a/src/vs/workbench/api/node/extHostStatusBar.ts +++ b/src/vs/workbench/api/node/extHostStatusBar.ts @@ -139,7 +139,7 @@ class StatusBarMessage { this._update(); return new Disposable(() => { - let idx = this._messages.indexOf(data); + const idx = this._messages.indexOf(data); if (idx >= 0) { this._messages.splice(idx, 1); this._update(); @@ -173,7 +173,7 @@ export class ExtHostStatusBar { setStatusBarMessage(text: string, timeoutOrThenable?: number | Thenable): Disposable { - let d = this._statusMessage.setMessage(text); + const d = this._statusMessage.setMessage(text); let handle: any; if (typeof timeoutOrThenable === 'number') { diff --git a/src/vs/workbench/api/node/extHostTask.ts b/src/vs/workbench/api/node/extHostTask.ts index eeed4f06b6e..8d2fc278b45 100644 --- a/src/vs/workbench/api/node/extHostTask.ts +++ b/src/vs/workbench/api/node/extHostTask.ts @@ -75,14 +75,14 @@ namespace ProcessExecutionOptionsDTO { namespace ProcessExecutionDTO { export function is(value: ShellExecutionDTO | ProcessExecutionDTO): value is ProcessExecutionDTO { - let candidate = value as ProcessExecutionDTO; + const candidate = value as ProcessExecutionDTO; return candidate && !!candidate.process; } export function from(value: vscode.ProcessExecution): ProcessExecutionDTO { if (value === undefined || value === null) { return undefined; } - let result: ProcessExecutionDTO = { + const result: ProcessExecutionDTO = { process: value.process, args: value.args }; @@ -116,14 +116,14 @@ namespace ShellExecutionOptionsDTO { namespace ShellExecutionDTO { export function is(value: ShellExecutionDTO | ProcessExecutionDTO): value is ShellExecutionDTO { - let candidate = value as ShellExecutionDTO; + const candidate = value as ShellExecutionDTO; return candidate && (!!candidate.commandLine || !!candidate.command); } export function from(value: vscode.ShellExecution): ShellExecutionDTO { if (value === undefined || value === null) { return undefined; } - let result: ShellExecutionDTO = { + const result: ShellExecutionDTO = { }; if (value.commandLine !== undefined) { result.commandLine = value.commandLine; @@ -167,9 +167,9 @@ namespace TaskDTO { if (tasks === undefined || tasks === null) { return []; } - let result: TaskDTO[] = []; + const result: TaskDTO[] = []; for (let task of tasks) { - let converted = from(task, extension); + const converted = from(task, extension); if (converted) { result.push(converted); } @@ -187,7 +187,7 @@ namespace TaskDTO { } else if (value.execution instanceof types.ShellExecution) { execution = ShellExecutionDTO.from(value.execution); } - let definition: TaskDefinitionDTO = TaskDefinitionDTO.from(value.definition); + const definition: TaskDefinitionDTO = TaskDefinitionDTO.from(value.definition); let scope: number | UriComponents; if (value.scope) { if (typeof value.scope === 'number') { @@ -202,8 +202,8 @@ namespace TaskDTO { if (!definition || !scope) { return undefined; } - let group = (value.group as types.TaskGroup) ? (value.group as types.TaskGroup).id : undefined; - let result: TaskDTO = { + const group = (value.group as types.TaskGroup) ? (value.group as types.TaskGroup).id : undefined; + const result: TaskDTO = { _id: (value as types.Task)._id, definition, name: value.name, @@ -232,7 +232,7 @@ namespace TaskDTO { } else if (ShellExecutionDTO.is(value.execution)) { execution = ShellExecutionDTO.to(value.execution); } - let definition: vscode.TaskDefinition = TaskDefinitionDTO.to(value.definition); + const definition: vscode.TaskDefinition = TaskDefinitionDTO.to(value.definition); let scope: vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder | undefined; if (value.source) { if (value.source.scope !== undefined) { @@ -248,7 +248,7 @@ namespace TaskDTO { if (!definition || !scope) { return undefined; } - let result = new types.Task(definition, scope, value.name, value.source.label, execution, value.problemMatchers); + const result = new types.Task(definition, scope, value.name, value.source.label, execution, value.problemMatchers); if (value.isBackground !== undefined) { result.isBackground = value.isBackground; } @@ -345,7 +345,7 @@ export class ExtHostTask implements ExtHostTaskShape { if (!provider) { return new types.Disposable(() => { }); } - let handle = this.nextHandle(); + const handle = this.nextHandle(); this._handlers.set(handle, { provider, extension }); this._proxy.$registerTaskProvider(handle); return new types.Disposable(() => { @@ -360,9 +360,9 @@ export class ExtHostTask implements ExtHostTaskShape { public fetchTasks(filter?: vscode.TaskFilter): Promise { return this._proxy.$fetchTasks(TaskFilterDTO.from(filter)).then(async (values) => { - let result: vscode.Task[] = []; + const result: vscode.Task[] = []; for (let value of values) { - let task = await TaskDTO.to(value, this._workspaceProvider); + const task = await TaskDTO.to(value, this._workspaceProvider); if (task) { result.push(task); } @@ -372,12 +372,12 @@ export class ExtHostTask implements ExtHostTaskShape { } public async executeTask(extension: IExtensionDescription, task: vscode.Task): Promise { - let tTask = (task as types.Task); + const tTask = (task as types.Task); // We have a preserved ID. So the task didn't change. if (tTask._id !== undefined) { return this._proxy.$executeTask(TaskHandleDTO.from(tTask)).then(value => this.getTaskExecution(value, task)); } else { - let dto = TaskDTO.from(task, extension); + const dto = TaskDTO.from(task, extension); if (dto === undefined) { return Promise.reject(new Error('Task is not valid')); } @@ -386,7 +386,7 @@ export class ExtHostTask implements ExtHostTaskShape { } public get taskExecutions(): vscode.TaskExecution[] { - let result: vscode.TaskExecution[] = []; + const result: vscode.TaskExecution[] = []; this._taskExecutions.forEach(value => result.push(value)); return result; } @@ -449,12 +449,12 @@ export class ExtHostTask implements ExtHostTaskShape { } public $provideTasks(handle: number, validTypes: { [key: string]: boolean; }): Thenable { - let handler = this._handlers.get(handle); + const handler = this._handlers.get(handle); if (!handler) { return Promise.reject(new Error('no handler found')); } return asPromise(() => handler.provider.provideTasks(CancellationToken.None)).then(value => { - let sanitized: vscode.Task[] = []; + const sanitized: vscode.Task[] = []; for (let task of value) { if (task.definition && validTypes[task.definition.type] === true) { sanitized.push(task); @@ -472,15 +472,15 @@ export class ExtHostTask implements ExtHostTaskShape { public async $resolveVariables(uriComponents: UriComponents, toResolve: { process?: { name: string; cwd?: string; path?: string }, variables: string[] }): Promise<{ process?: string, variables: { [key: string]: string; } }> { const configProvider = await this._configurationService.getConfigProvider(); - let uri: URI = URI.revive(uriComponents); - let result = { + const uri: URI = URI.revive(uriComponents); + const result = { process: undefined as string, variables: Object.create(null) }; - let workspaceFolder = await this._workspaceProvider.resolveWorkspaceFolder(uri); + const workspaceFolder = await this._workspaceProvider.resolveWorkspaceFolder(uri); const workspaceFolders = await this._workspaceProvider.getWorkspaceFolders2(); - let resolver = new ExtHostVariableResolverService(workspaceFolders, this._editorService, configProvider); - let ws: IWorkspaceFolder = { + const resolver = new ExtHostVariableResolverService(workspaceFolders, this._editorService, configProvider); + const ws: IWorkspaceFolder = { uri: workspaceFolder.uri, name: workspaceFolder.name, index: workspaceFolder.index, diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts index 49d23649a80..45d953bb808 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -4,22 +4,19 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; +import pkg from 'vs/platform/product/node/package'; +import * as os from 'os'; import { URI, UriComponents } from 'vs/base/common/uri'; import * as platform from 'vs/base/common/platform'; -import * as terminalEnvironment from 'vs/workbench/contrib/terminal/node/terminalEnvironment'; +import * as terminalEnvironment from 'vs/workbench/contrib/terminal/common/terminalEnvironment'; import { Event, Emitter } from 'vs/base/common/event'; import { ExtHostTerminalServiceShape, MainContext, MainThreadTerminalServiceShape, IMainContext, ShellLaunchConfigDto } from 'vs/workbench/api/node/extHost.protocol'; import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration'; import { ILogService } from 'vs/platform/log/common/log'; -import { EXT_HOST_CREATION_DELAY } from 'vs/workbench/contrib/terminal/common/terminal'; +import { EXT_HOST_CREATION_DELAY, IShellLaunchConfig } from 'vs/workbench/contrib/terminal/common/terminal'; import { TerminalProcess } from 'vs/workbench/contrib/terminal/node/terminalProcess'; import { timeout } from 'vs/base/common/async'; -import { generateRandomPipeName } from 'vs/base/parts/ipc/node/ipc.net'; -import * as http from 'http'; -import * as fs from 'fs'; -import { ExtHostCommands } from 'vs/workbench/api/node/extHostCommands'; -import { sanitizeProcessEnvironment } from 'vs/base/node/processes'; -import { IURIToOpen, URIType } from 'vs/platform/windows/common/windows'; +import { sanitizeProcessEnvironment } from 'vs/base/common/processes'; const RENDERER_NO_PROCESS_ID = -1; @@ -270,7 +267,6 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { private _terminalProcesses: { [id: number]: TerminalProcess } = {}; private _terminalRenderers: ExtHostTerminalRenderer[] = []; private _getTerminalPromises: { [id: number]: Promise } = {}; - private _cliServer: CLIServer | undefined; public get activeTerminal(): ExtHostTerminal { return this._activeTerminal; } public get terminals(): ExtHostTerminal[] { return this._terminals; } @@ -288,7 +284,6 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { mainContext: IMainContext, private _extHostConfiguration: ExtHostConfiguration, private _logService: ILogService, - private _commands: ExtHostCommands ) { this._proxy = mainContext.getProxy(MainContext.MainThreadTerminalService); } @@ -416,7 +411,15 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { } } - public async $createProcess(id: number, shellLaunchConfig: ShellLaunchConfigDto, activeWorkspaceRootUriComponents: UriComponents, cols: number, rows: number): Promise { + public async $createProcess(id: number, shellLaunchConfigDto: ShellLaunchConfigDto, activeWorkspaceRootUriComponents: UriComponents, cols: number, rows: number): Promise { + const shellLaunchConfig: IShellLaunchConfig = { + name: shellLaunchConfigDto.name, + executable: shellLaunchConfigDto.executable, + args: shellLaunchConfigDto.args, + cwd: typeof shellLaunchConfigDto.cwd === 'string' ? shellLaunchConfigDto.cwd : URI.revive(shellLaunchConfigDto.cwd), + env: shellLaunchConfigDto.env + }; + // TODO: This function duplicates a lot of TerminalProcessManager.createProcess, ideally // they would be merged into a single implementation. const configProvider = await this._extHostConfiguration.getConfigProvider(); @@ -436,7 +439,7 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { // TODO: @daniel const activeWorkspaceRootUri = URI.revive(activeWorkspaceRootUriComponents); - const initialCwd = terminalEnvironment.getCwd(shellLaunchConfig, activeWorkspaceRootUri, terminalConfig.cwd); + const initialCwd = terminalEnvironment.getCwd(shellLaunchConfig, os.homedir(), activeWorkspaceRootUri, terminalConfig.cwd); // TODO: Pull in and resolve config settings // // Resolve env vars from config and shell @@ -453,16 +456,11 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { // Sanitize the environment, removing any undesirable VS Code and Electron environment // variables - sanitizeProcessEnvironment(env); + sanitizeProcessEnvironment(env, 'VSCODE_IPC_HOOK_CLI'); // Continue env initialization, merging in the env from the launch // config and adding keys that are needed to create the process - terminalEnvironment.addTerminalEnvironmentKeys(env, platform.locale, terminalConfig.get('setLocaleVariables')); - - if (!this._cliServer) { - this._cliServer = new CLIServer(this._commands); - } - env['VSCODE_IPC_HOOK_CLI'] = this._cliServer.ipcHandlePath; + terminalEnvironment.addTerminalEnvironmentKeys(env, pkg.version, platform.locale, terminalConfig.get('setLocaleVariables')); // Fork the process and listen for messages this._logService.debug(`Terminal process launching on ext host`, shellLaunchConfig, initialCwd, cols, rows, env); @@ -512,11 +510,6 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { // Send exit event to main side this._proxy.$sendProcessExit(id, exitCode); - if (this._cliServer && !Object.keys(this._terminalProcesses).length) { - this._cliServer.dispose(); - this._cliServer = undefined; - } - } private _getTerminalByIdEventually(id: number, retries: number = 5): Promise { @@ -588,74 +581,3 @@ class ApiRequest { this._callback.apply(proxy, [id].concat(this._args)); } } - - -class CLIServer { - - private _server: http.Server; - private _ipcHandlePath: string | undefined; - - constructor(private _commands: ExtHostCommands) { - this._server = http.createServer((req, res) => this.onRequest(req, res)); - this.setup().catch(err => { - console.error(err); - return ''; - }); - } - - public get ipcHandlePath() { - return this._ipcHandlePath; - } - - private async setup(): Promise { - this._ipcHandlePath = generateRandomPipeName(); - - try { - this._server.listen(this.ipcHandlePath); - this._server.on('error', err => console.error(err)); - } catch (err) { - console.error('Could not start open from terminal server.'); - } - - return this.ipcHandlePath; - } - private collectURIToOpen(strs: string[], typeHint: URIType, result: IURIToOpen[]): void { - if (Array.isArray(strs)) { - for (const s of strs) { - try { - result.push({ uri: URI.parse(s), typeHint }); - } catch (e) { - // ignore - } - } - } - } - - private onRequest(req: http.IncomingMessage, res: http.ServerResponse): void { - const chunks: string[] = []; - req.setEncoding('utf8'); - req.on('data', (d: string) => chunks.push(d)); - req.on('end', () => { - let { fileURIs, folderURIs, forceNewWindow, diffMode, addMode, forceReuseWindow } = JSON.parse(chunks.join('')); - if (folderURIs && folderURIs.length || fileURIs && fileURIs.length) { - if (folderURIs && folderURIs.length && !forceReuseWindow) { - forceNewWindow = true; - } - const urisToOpen: IURIToOpen[] = []; - this.collectURIToOpen(folderURIs, 'folder', urisToOpen); - this.collectURIToOpen(fileURIs, 'file', urisToOpen); - this._commands.executeCommand('_files.windowOpen', { urisToOpen, forceNewWindow, diffMode, addMode, forceReuseWindow }); - } - res.writeHead(200); - res.end(); - }); - } - - dispose(): void { - this._server.close(); - - if (this._ipcHandlePath && process.platform !== 'win32' && fs.existsSync(this._ipcHandlePath)) { - fs.unlinkSync(this._ipcHandlePath); - } - } -} diff --git a/src/vs/workbench/api/node/extHostTextEditor.ts b/src/vs/workbench/api/node/extHostTextEditor.ts index 67ddc6361cd..d3080e367b6 100644 --- a/src/vs/workbench/api/node/extHostTextEditor.ts +++ b/src/vs/workbench/api/node/extHostTextEditor.ts @@ -35,7 +35,7 @@ export class TextEditorDecorationType implements vscode.TextEditorDecorationType export interface ITextEditOperation { range: vscode.Range; - text: string; + text: string | null; forceMoveMarkers: boolean; } @@ -105,8 +105,8 @@ export class TextEditorEdit { this._pushEdit(range, null, true); } - private _pushEdit(range: Range, text: string, forceMoveMarkers: boolean): void { - let validRange = this._document.validateRange(range); + private _pushEdit(range: Range, text: string | null, forceMoveMarkers: boolean): void { + const validRange = this._document.validateRange(range); this._collectedEdits.push({ range: validRange, text: text, @@ -170,11 +170,11 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions { return 'auto'; } if (typeof value === 'number') { - let r = Math.floor(value); + const r = Math.floor(value); return (r > 0 ? r : null); } if (typeof value === 'string') { - let r = parseInt(value, 10); + const r = parseInt(value, 10); if (isNaN(r)) { return null; } @@ -184,7 +184,7 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions { } public set tabSize(value: number | string) { - let tabSize = this._validateTabSize(value); + const tabSize = this._validateTabSize(value); if (tabSize === null) { // ignore invalid call return; @@ -211,11 +211,11 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions { return 'tabSize'; } if (typeof value === 'number') { - let r = Math.floor(value); + const r = Math.floor(value); return (r > 0 ? r : null); } if (typeof value === 'string') { - let r = parseInt(value, 10); + const r = parseInt(value, 10); if (isNaN(r)) { return null; } @@ -225,7 +225,7 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions { } public set indentSize(value: number | string) { - let indentSize = this._validateIndentSize(value); + const indentSize = this._validateIndentSize(value); if (indentSize === null) { // ignore invalid call return; @@ -255,7 +255,7 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions { } public set insertSpaces(value: boolean | string) { - let insertSpaces = this._validateInsertSpaces(value); + const insertSpaces = this._validateInsertSpaces(value); if (typeof insertSpaces === 'boolean') { if (this._insertSpaces === insertSpaces) { // nothing to do @@ -300,11 +300,11 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions { } public assign(newOptions: vscode.TextEditorOptions) { - let bulkConfigurationUpdate: ITextEditorConfigurationUpdate = {}; + const bulkConfigurationUpdate: ITextEditorConfigurationUpdate = {}; let hasUpdate = false; if (typeof newOptions.tabSize !== 'undefined') { - let tabSize = this._validateTabSize(newOptions.tabSize); + const tabSize = this._validateTabSize(newOptions.tabSize); if (tabSize === 'auto') { hasUpdate = true; bulkConfigurationUpdate.tabSize = tabSize; @@ -316,21 +316,21 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions { } } - if (typeof newOptions.indentSize !== 'undefined') { - let indentSize = this._validateIndentSize(newOptions.indentSize); - if (indentSize === 'tabSize') { - hasUpdate = true; - bulkConfigurationUpdate.indentSize = indentSize; - } else if (typeof indentSize === 'number' && this._indentSize !== indentSize) { - // reflect the new indentSize value immediately - this._indentSize = indentSize; - hasUpdate = true; - bulkConfigurationUpdate.indentSize = indentSize; - } - } + // if (typeof newOptions.indentSize !== 'undefined') { + // const indentSize = this._validateIndentSize(newOptions.indentSize); + // if (indentSize === 'tabSize') { + // hasUpdate = true; + // bulkConfigurationUpdate.indentSize = indentSize; + // } else if (typeof indentSize === 'number' && this._indentSize !== indentSize) { + // // reflect the new indentSize value immediately + // this._indentSize = indentSize; + // hasUpdate = true; + // bulkConfigurationUpdate.indentSize = indentSize; + // } + // } if (typeof newOptions.insertSpaces !== 'undefined') { - let insertSpaces = this._validateInsertSpaces(newOptions.insertSpaces); + const insertSpaces = this._validateInsertSpaces(newOptions.insertSpaces); if (insertSpaces === 'auto') { hasUpdate = true; bulkConfigurationUpdate.insertSpaces = insertSpaces; @@ -373,7 +373,7 @@ export class ExtHostTextEditor implements vscode.TextEditor { private _selections: Selection[]; private _options: ExtHostTextEditorOptions; private _visibleRanges: Range[]; - private _viewColumn: vscode.ViewColumn; + private _viewColumn: vscode.ViewColumn | undefined; private _disposed: boolean = false; private _hasDecorationsForKey: { [key: string]: boolean; }; @@ -382,7 +382,7 @@ export class ExtHostTextEditor implements vscode.TextEditor { constructor( proxy: MainThreadTextEditorsShape, id: string, document: ExtHostDocumentData, selections: Selection[], options: IResolvedTextEditorConfiguration, - visibleRanges: Range[], viewColumn: vscode.ViewColumn + visibleRanges: Range[], viewColumn: vscode.ViewColumn | undefined ) { this._proxy = proxy; this._id = id; @@ -451,7 +451,7 @@ export class ExtHostTextEditor implements vscode.TextEditor { // ---- view column - get viewColumn(): vscode.ViewColumn { + get viewColumn(): vscode.ViewColumn | undefined { return this._viewColumn; } @@ -510,7 +510,7 @@ export class ExtHostTextEditor implements vscode.TextEditor { TypeConverters.fromRangeOrRangeWithMessage(ranges) ); } else { - let _ranges: number[] = new Array(4 * ranges.length); + const _ranges: number[] = new Array(4 * ranges.length); for (let i = 0, len = ranges.length; i < len; i++) { const range = ranges[i]; _ranges[4 * i] = range.start.line + 1; @@ -538,8 +538,8 @@ export class ExtHostTextEditor implements vscode.TextEditor { ); } - private _trySetSelection(): Promise { - let selection = this._selections.map(TypeConverters.Selection.from); + private _trySetSelection(): Promise { + const selection = this._selections.map(TypeConverters.Selection.from); return this._runOnProxy(() => this._proxy.$trySetSelections(this._id, selection)); } @@ -554,13 +554,13 @@ export class ExtHostTextEditor implements vscode.TextEditor { if (this._disposed) { return Promise.reject(new Error('TextEditor#edit not possible on closed editors')); } - let edit = new TextEditorEdit(this._documentData.document, options); + const edit = new TextEditorEdit(this._documentData.document, options); callback(edit); return this._applyEdit(edit); } private _applyEdit(editBuilder: TextEditorEdit): Promise { - let editData = editBuilder.finalize(); + const editData = editBuilder.finalize(); // return when there is nothing to do if (editData.edits.length === 0 && !editData.setEndOfLine) { @@ -568,7 +568,7 @@ export class ExtHostTextEditor implements vscode.TextEditor { } // check that the edits are not overlapping (i.e. illegal) - let editRanges = editData.edits.map(edit => edit.range); + const editRanges = editData.edits.map(edit => edit.range); // sort ascending (by end and then by start) editRanges.sort((a, b) => { @@ -598,7 +598,7 @@ export class ExtHostTextEditor implements vscode.TextEditor { } // prepare data for serialization - let edits: ISingleEditOperation[] = editData.edits.map((edit) => { + const edits = editData.edits.map((edit): ISingleEditOperation => { return { range: TypeConverters.Range.from(edit.range), text: edit.text, @@ -620,7 +620,7 @@ export class ExtHostTextEditor implements vscode.TextEditor { let ranges: IRange[]; if (!where || (Array.isArray(where) && where.length === 0)) { - ranges = this._selections.map(TypeConverters.Range.from); + ranges = this._selections.map(range => TypeConverters.Range.from(range)); } else if (where instanceof Position) { const { lineNumber, column } = TypeConverters.Position.from(where); @@ -645,7 +645,7 @@ export class ExtHostTextEditor implements vscode.TextEditor { // ---- util - private _runOnProxy(callback: () => Promise): Promise { + private _runOnProxy(callback: () => Promise): Promise { if (this._disposed) { console.warn('TextEditor is closed/disposed'); return Promise.resolve(undefined); diff --git a/src/vs/workbench/api/node/extHostTextEditors.ts b/src/vs/workbench/api/node/extHostTextEditors.ts index 3b316662afb..a42ec9c245f 100644 --- a/src/vs/workbench/api/node/extHostTextEditors.ts +++ b/src/vs/workbench/api/node/extHostTextEditors.ts @@ -75,7 +75,7 @@ export class ExtHostEditors implements ExtHostEditorsShape { } return this._proxy.$tryShowTextDocument(document.uri, options).then(id => { - let editor = this._extHostDocumentsAndEditors.getEditor(id); + const editor = this._extHostDocumentsAndEditors.getEditor(id); if (editor) { return editor; } else { @@ -97,6 +97,9 @@ export class ExtHostEditors implements ExtHostEditorsShape { $acceptEditorPropertiesChanged(id: string, data: IEditorPropertiesChangeData): void { const textEditor = this._extHostDocumentsAndEditors.getEditor(id); + if (!textEditor) { + throw new Error('unknown text editor'); + } // (1) set all properties if (data.options) { @@ -137,9 +140,12 @@ export class ExtHostEditors implements ExtHostEditorsShape { } $acceptEditorPositionData(data: ITextEditorPositionData): void { - for (let id in data) { - let textEditor = this._extHostDocumentsAndEditors.getEditor(id); - let viewColumn = TypeConverters.ViewColumn.to(data[id]); + for (const id in data) { + const textEditor = this._extHostDocumentsAndEditors.getEditor(id); + if (!textEditor) { + throw new Error('Unknown text editor'); + } + const viewColumn = TypeConverters.ViewColumn.to(data[id]); if (textEditor.viewColumn !== viewColumn) { textEditor._acceptViewColumn(viewColumn); this._onDidChangeTextEditorViewColumn.fire({ textEditor, viewColumn }); diff --git a/src/vs/workbench/api/node/extHostTreeViews.ts b/src/vs/workbench/api/node/extHostTreeViews.ts index f5e8ea7aba9..b135cd62d8d 100644 --- a/src/vs/workbench/api/node/extHostTreeViews.ts +++ b/src/vs/workbench/api/node/extHostTreeViews.ts @@ -366,7 +366,7 @@ class ExtHostTreeView extends Disposable { private getHandlesToRefresh(elements: T[]): TreeItemHandle[] { const elementsToUpdate = new Set(); for (const element of elements) { - let elementNode = this.nodes.get(element); + const elementNode = this.nodes.get(element); if (elementNode && !elementsToUpdate.has(elementNode.item.handle)) { // check if an ancestor of extElement is already in the elements to update list let currentNode = elementNode; @@ -384,7 +384,7 @@ class ExtHostTreeView extends Disposable { // Take only top level elements elementsToUpdate.forEach((handle) => { const element = this.elements.get(handle); - let node = this.nodes.get(element); + const node = this.nodes.get(element); if (node && (!node.parent || !elementsToUpdate.has(node.parent.item.handle))) { handlesToUpdate.push(handle); } @@ -553,7 +553,7 @@ class ExtHostTreeView extends Disposable { private clearChildren(parentElement?: T): void { if (parentElement) { - let node = this.nodes.get(parentElement); + const node = this.nodes.get(parentElement); if (node.children) { for (const child of node.children) { const childEleement = this.elements.get(child.item.handle); @@ -569,7 +569,7 @@ class ExtHostTreeView extends Disposable { } private clear(element: T): void { - let node = this.nodes.get(element); + const node = this.nodes.get(element); if (node.children) { for (const child of node.children) { const childEleement = this.elements.get(child.item.handle); diff --git a/src/vs/workbench/api/node/extHostTypeConverters.ts b/src/vs/workbench/api/node/extHostTypeConverters.ts index a445750d59d..b87947ae0b8 100644 --- a/src/vs/workbench/api/node/extHostTypeConverters.ts +++ b/src/vs/workbench/api/node/extHostTypeConverters.ts @@ -236,7 +236,7 @@ export namespace MarkdownString { const resUris: { [href: string]: UriComponents } = Object.create(null); res.uris = resUris; - let renderer = new marked.Renderer(); + const renderer = new marked.Renderer(); renderer.image = renderer.link = (href: string): string => { try { let uri = URI.parse(href, true); @@ -267,7 +267,7 @@ export namespace MarkdownString { } data = cloneAndChange(data, value => { if (value instanceof URI) { - let key = `__uri_${Math.random().toString(16).slice(2, 8)}`; + const key = `__uri_${Math.random().toString(16).slice(2, 8)}`; bucket[key] = value; return key; } else { @@ -457,7 +457,7 @@ export namespace WorkspaceEdit { const [uri, uriOrEdits] = entry; if (Array.isArray(uriOrEdits)) { // text edits - const doc = documents && uri ? documents.getDocument(uri.toString()) : undefined; + const doc = documents && uri ? documents.getDocument(uri) : undefined; result.edits.push({ resource: uri, modelVersionId: doc && doc.version, edits: uriOrEdits.map(TextEdit.from) }); } else { // resource edits diff --git a/src/vs/workbench/api/node/extHostTypes.ts b/src/vs/workbench/api/node/extHostTypes.ts index 71dc9a8b3a7..535dadfb0c8 100644 --- a/src/vs/workbench/api/node/extHostTypes.ts +++ b/src/vs/workbench/api/node/extHostTypes.ts @@ -66,7 +66,7 @@ export class Position { } let result = positions[0]; for (let i = 1; i < positions.length; i++) { - let p = positions[i]; + const p = positions[i]; if (p.isBefore(result!)) { result = p; } @@ -80,7 +80,7 @@ export class Position { } let result = positions[0]; for (let i = 1; i < positions.length; i++) { - let p = positions[i]; + const p = positions[i]; if (p.isAfter(result!)) { result = p; } @@ -303,8 +303,8 @@ export class Range { } intersection(other: Range): Range | undefined { - let start = Position.Max(other.start, this._start); - let end = Position.Min(other.end, this._end); + const start = Position.Max(other.start, this._start); + const end = Position.Min(other.end, this._end); if (start.isAfter(end)) { // this happens when there is no overlap: // |-----| @@ -320,8 +320,8 @@ export class Range { } else if (other.contains(this)) { return other; } - let start = Position.Min(other.start, this._start); - let end = Position.Max(other.end, this.end); + const start = Position.Min(other.start, this._start); + const end = Position.Max(other.end, this.end); return new Range(start, end); } @@ -480,7 +480,7 @@ export class TextEdit { } static setEndOfLine(eol: EndOfLine): TextEdit { - let ret = new TextEdit(new Range(new Position(0, 0), new Position(0, 0)), ''); + const ret = new TextEdit(new Range(new Position(0, 0), new Position(0, 0)), ''); ret.newEol = eol; return ret; } @@ -616,7 +616,7 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit { } get(uri: URI): TextEdit[] { - let res: TextEdit[] = []; + const res: TextEdit[] = []; for (let candidate of this._edits) { if (candidate._type === 2 && candidate.uri.toString() === uri.toString()) { res.push(candidate.edit); @@ -626,7 +626,7 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit { } entries(): [URI, TextEdit[]][] { - let textEdits = new Map(); + const textEdits = new Map(); for (let candidate of this._edits) { if (candidate._type === 2) { let textEdit = textEdits.get(candidate.uri.toString()); @@ -641,7 +641,7 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit { } _allEntries(): ([URI, TextEdit[]] | [URI?, URI?, IFileOperationOptions?])[] { - let res: ([URI, TextEdit[]] | [URI?, URI?, IFileOperationOptions?])[] = []; + const res: ([URI, TextEdit[]] | [URI?, URI?, IFileOperationOptions?])[] = []; for (let edit of this._edits) { if (edit._type === 1) { res.push([edit.from, edit.to, edit.options]); @@ -1846,7 +1846,7 @@ export class Task implements vscode.Task { } this.clear(); this._execution = value; - let type = this._definition.type; + const type = this._definition.type; if (Task.EmptyType === type || Task.ProcessType === type || Task.ShellType === type) { this.computeDefinitionBasedOnExecution(); } diff --git a/src/vs/workbench/api/node/extHostWebview.ts b/src/vs/workbench/api/node/extHostWebview.ts index df6b86f3e92..d5e5d2264db 100644 --- a/src/vs/workbench/api/node/extHostWebview.ts +++ b/src/vs/workbench/api/node/extHostWebview.ts @@ -85,7 +85,7 @@ export class ExtHostWebviewPanel implements vscode.WebviewPanel { private readonly _options: vscode.WebviewPanelOptions; private readonly _webview: ExtHostWebview; private _isDisposed: boolean = false; - private _viewColumn: vscode.ViewColumn; + private _viewColumn: vscode.ViewColumn | undefined; private _visible: boolean = true; private _active: boolean = true; @@ -101,7 +101,7 @@ export class ExtHostWebviewPanel implements vscode.WebviewPanel { proxy: MainThreadWebviewsShape, viewType: string, title: string, - viewColumn: vscode.ViewColumn, + viewColumn: vscode.ViewColumn | undefined, editorOptions: vscode.WebviewPanelOptions, webview: ExtHostWebview ) { @@ -173,7 +173,7 @@ export class ExtHostWebviewPanel implements vscode.WebviewPanel { get viewColumn(): vscode.ViewColumn | undefined { this.assertNotDisposed(); - if (this._viewColumn < 0) { + if (typeof this._viewColumn === 'number' && this._viewColumn < 0) { // We are using a symbolic view column // Return undefined instead to indicate that the real view column is currently unknown but will be resolved. return undefined; diff --git a/src/vs/workbench/api/node/extHostWorkspace.ts b/src/vs/workbench/api/node/extHostWorkspace.ts index edcbfaee62e..bcd01710615 100644 --- a/src/vs/workbench/api/node/extHostWorkspace.ts +++ b/src/vs/workbench/api/node/extHostWorkspace.ts @@ -405,7 +405,7 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac } } - let excludePatternOrDisregardExcludes: string | false = false; + let excludePatternOrDisregardExcludes: string | false | undefined = undefined; if (exclude === null) { excludePatternOrDisregardExcludes = false; } else if (exclude) { @@ -459,7 +459,7 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac excludePattern: options.exclude ? globPatternToString(options.exclude) : undefined }; - let isCanceled = false; + const isCanceled = false; this._activeSearchCallbacks[requestId] = p => { if (isCanceled) { diff --git a/src/vs/workbench/browser/actions.ts b/src/vs/workbench/browser/actions.ts index c6355e52f00..a31b6947af2 100644 --- a/src/vs/workbench/browser/actions.ts +++ b/src/vs/workbench/browser/actions.ts @@ -7,7 +7,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { Action, IAction } from 'vs/base/common/actions'; import { BaseActionItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { ITree, IActionProvider } from 'vs/base/parts/tree/browser/tree'; -import { IInstantiationService, IConstructorSignature0 } from 'vs/platform/instantiation/common/instantiation'; +import { IInstantiationService, IConstructorSignature0, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; /** * The action bar contributor allows to add actions to an actionbar in a given context. @@ -235,7 +235,10 @@ export interface IActionBarRegistry { */ getActionBarContributors(scope: string): ActionBarContributor[]; - setInstantiationService(service: IInstantiationService): void; + /** + * Starts the registry by providing the required services. + */ + start(accessor: ServicesAccessor): void; } class ActionBarRegistry implements IActionBarRegistry { @@ -243,8 +246,8 @@ class ActionBarRegistry implements IActionBarRegistry { private actionBarContributorInstances: { [scope: string]: ActionBarContributor[] } = Object.create(null); private instantiationService: IInstantiationService; - setInstantiationService(service: IInstantiationService): void { - this.instantiationService = service; + start(accessor: ServicesAccessor): void { + this.instantiationService = accessor.get(IInstantiationService); while (this.actionBarContributorConstructors.length > 0) { const entry = this.actionBarContributorConstructors.shift()!; diff --git a/src/vs/workbench/browser/actions/listCommands.ts b/src/vs/workbench/browser/actions/listCommands.ts index e89ad255348..7afe64bfa83 100644 --- a/src/vs/workbench/browser/actions/listCommands.ts +++ b/src/vs/workbench/browser/actions/listCommands.ts @@ -88,10 +88,12 @@ function expandMultiSelection(focused: List | PagedList | ITree | Obje const focus = list.getFocus() ? list.getFocus()[0] : undefined; const selection = list.getSelection(); - if (selection && selection.indexOf(focus) >= 0) { + if (selection && typeof focus === 'number' && selection.indexOf(focus) >= 0) { list.setSelection(selection.filter(s => s !== previousFocus)); } else { - list.setSelection(selection.concat(focus)); + if (typeof focus === 'number') { + list.setSelection(selection.concat(focus)); + } } } @@ -636,7 +638,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ const selectedNode = tree.getNode(start); const parentNode = selectedNode.parent; - if (!parentNode.parent) { // root + if (!parentNode || !parentNode.parent) { // root scope = undefined; } else { scope = parentNode.element; diff --git a/src/vs/workbench/browser/actions/workspaceActions.ts b/src/vs/workbench/browser/actions/workspaceActions.ts index 96ba6f2f22c..c8be1e13669 100644 --- a/src/vs/workbench/browser/actions/workspaceActions.ts +++ b/src/vs/workbench/browser/actions/workspaceActions.ts @@ -240,8 +240,9 @@ export class DuplicateWorkspaceInNewWindowAction extends Action { run(): Promise { const folders = this.workspaceContextService.getWorkspace().folders; + const remoteAuthority = this.windowService.getConfiguration().remoteAuthority; - return this.workspacesService.createUntitledWorkspace(folders).then(newWorkspace => { + return this.workspacesService.createUntitledWorkspace(folders, remoteAuthority).then(newWorkspace => { return this.workspaceEditingService.copyWorkspaceSettings(newWorkspace).then(() => { return this.windowService.openWindow([{ uri: newWorkspace.configPath, typeHint: 'file' }], { forceNewWindow: true }); }); diff --git a/src/vs/workbench/browser/composite.ts b/src/vs/workbench/browser/composite.ts index 894aa1d77b9..4fc60b1cc1d 100644 --- a/src/vs/workbench/browser/composite.ts +++ b/src/vs/workbench/browser/composite.ts @@ -38,7 +38,7 @@ export abstract class Composite extends Component implements IComposite { private _onDidFocus: Emitter; get onDidFocus(): Event { if (!this._onDidFocus) { - this._registerFocusTrackEvents(); + this.registerFocusTrackEvents(); } return this._onDidFocus.event; @@ -47,13 +47,13 @@ export abstract class Composite extends Component implements IComposite { private _onDidBlur: Emitter; get onDidBlur(): Event { if (!this._onDidBlur) { - this._registerFocusTrackEvents(); + this.registerFocusTrackEvents(); } return this._onDidBlur.event; } - private _registerFocusTrackEvents(): void { + private registerFocusTrackEvents(): void { this._onDidFocus = this._register(new Emitter()); this._onDidBlur = this._register(new Emitter()); @@ -217,11 +217,11 @@ export abstract class CompositeDescriptor { constructor( private readonly ctor: IConstructorSignature0, - public readonly id: string, - public readonly name: string, - public readonly cssClass?: string, - public readonly order?: number, - public readonly keybindingId?: string, + readonly id: string, + readonly name: string, + readonly cssClass?: string, + readonly order?: number, + readonly keybindingId?: string, ) { } instantiate(instantiationService: IInstantiationService): T { diff --git a/src/vs/workbench/browser/contextkeys.ts b/src/vs/workbench/browser/contextkeys.ts index 098badbdae0..e5e53f47ef6 100644 --- a/src/vs/workbench/browser/contextkeys.ts +++ b/src/vs/workbench/browser/contextkeys.ts @@ -8,7 +8,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { InputFocusedContext } from 'vs/platform/contextkey/common/contextkeys'; import { IWindowConfiguration, IWindowService } from 'vs/platform/windows/common/windows'; -import { ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, TEXT_DIFF_EDITOR_ID, SplitEditorsVertically } from 'vs/workbench/common/editor'; +import { ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, TEXT_DIFF_EDITOR_ID, SplitEditorsVertically, InEditorZenModeContext } from 'vs/workbench/common/editor'; import { IsMacContext, IsLinuxContext, IsWindowsContext, HasMacNativeTabsContext, IsDevelopmentContext, SupportsWorkspacesContext, SupportsOpenFileFolderContext, WorkbenchStateContext, WorkspaceFolderCountContext } from 'vs/workbench/common/contextkeys'; import { trackFocus, addDisposableListener, EventType } from 'vs/base/browser/dom'; import { preferredSideBySideGroupDirection, GroupDirection, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; @@ -17,18 +17,30 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { WorkbenchState, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { EditorGroupsServiceImpl } from 'vs/workbench/browser/parts/editor/editor'; +import { SidebarVisibleContext, SideBarVisibleContext } from 'vs/workbench/common/viewlet'; +import { IPartService, Parts } from 'vs/workbench/services/part/common/partService'; +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; export class WorkbenchContextKeysHandler extends Disposable { private inputFocusedContext: IContextKey; + private activeEditorContext: IContextKey; private editorsVisibleContext: IContextKey; private textCompareEditorVisibleContext: IContextKey; private textCompareEditorActiveContext: IContextKey; private activeEditorGroupEmpty: IContextKey; private multipleEditorGroupsContext: IContextKey; + private splitEditorsVerticallyContext: IContextKey; + private workbenchStateContext: IContextKey; private workspaceFolderCountContext: IContextKey; - private splitEditorsVerticallyContext: IContextKey; + + + private inZenModeContext: IContextKey; + + private sideBarVisibleContext: IContextKey; + //TODO@Isidor remove in May + private sidebarVisibleContext: IContextKey; constructor( @IContextKeyService private contextKeyService: IContextKeyService, @@ -37,7 +49,9 @@ export class WorkbenchContextKeysHandler extends Disposable { @IEnvironmentService private environmentService: IEnvironmentService, @IWindowService private windowService: IWindowService, @IEditorService private editorService: IEditorService, - @IEditorGroupsService private editorGroupService: EditorGroupsServiceImpl + @IEditorGroupsService private editorGroupService: EditorGroupsServiceImpl, + @IPartService private partService: IPartService, + @IViewletService private viewletService: IViewletService ) { super(); @@ -63,6 +77,11 @@ export class WorkbenchContextKeysHandler extends Disposable { this.updateSplitEditorsVerticallyContext(); } })); + + this._register(this.partService.onZenModeChange(enabled => this.inZenModeContext.set(enabled))); + + this._register(this.viewletService.onDidViewletClose(() => this.updateSideBarContextKeys())); + this._register(this.viewletService.onDidViewletOpen(() => this.updateSideBarContextKeys())); } private initContextKeys(): void { @@ -105,6 +124,13 @@ export class WorkbenchContextKeysHandler extends Disposable { // Editor Layout this.splitEditorsVerticallyContext = SplitEditorsVertically.bindTo(this.contextKeyService); this.updateSplitEditorsVerticallyContext(); + + // Zen Mode + this.inZenModeContext = InEditorZenModeContext.bindTo(this.contextKeyService); + + // Sidebar + this.sideBarVisibleContext = SideBarVisibleContext.bindTo(this.contextKeyService); + this.sidebarVisibleContext = SidebarVisibleContext.bindTo(this.contextKeyService); } private updateEditorContextKeys(): void { @@ -178,4 +204,9 @@ export class WorkbenchContextKeysHandler extends Disposable { case WorkbenchState.WORKSPACE: return 'workspace'; } } + + private updateSideBarContextKeys(): void { + this.sideBarVisibleContext.set(this.partService.isVisible(Parts.SIDEBAR_PART)); + this.sidebarVisibleContext.set(this.partService.isVisible(Parts.SIDEBAR_PART)); + } } \ No newline at end of file diff --git a/src/vs/workbench/browser/dnd.ts b/src/vs/workbench/browser/dnd.ts index 14c2f2f4fe6..30603937bd9 100644 --- a/src/vs/workbench/browser/dnd.ts +++ b/src/vs/workbench/browser/dnd.ts @@ -162,7 +162,7 @@ export class ResourcesDropHandler { ) { } - handleDrop(event: DragEvent, resolveTargetGroup: () => IEditorGroup, afterDrop: (targetGroup: IEditorGroup) => void, targetIndex?: number): void { + handleDrop(event: DragEvent, resolveTargetGroup: () => IEditorGroup | undefined, afterDrop: (targetGroup: IEditorGroup | undefined) => void, targetIndex?: number): void { const untitledOrFileResources = extractResources(event).filter(r => this.fileService.canHandleResource(r.resource) || r.resource.scheme === Schemas.untitled); if (!untitledOrFileResources.length) { return; @@ -235,10 +235,10 @@ export class ResourcesDropHandler { } // Resolve the contents of the dropped dirty resource from source - return this.backupFileService.resolveBackupContent(droppedDirtyEditor.backupResource).then(content => { + return this.backupFileService.resolveBackupContent(droppedDirtyEditor.backupResource!).then(content => { // Set the contents of to the resource to the target - return this.backupFileService.backupResource(droppedDirtyEditor.resource, content.create(this.getDefaultEOL()).createSnapshot(true)); + return this.backupFileService.backupResource(droppedDirtyEditor.resource, content!.create(this.getDefaultEOL()).createSnapshot(true)); }).then(() => false, () => false /* ignore any error */); } @@ -447,6 +447,8 @@ export class DragAndDropObserver extends Disposable { })); this._register(addDisposableListener(this.element, EventType.DRAG_OVER, (e: DragEvent) => { + e.preventDefault(); // needed so that the drop event fires (https://stackoverflow.com/questions/21339924/drop-event-not-firing-in-chrome) + if (this.callbacks.onDragOver) { this.callbacks.onDragOver(e); } diff --git a/src/vs/workbench/browser/labels.ts b/src/vs/workbench/browser/labels.ts index 97f0a3e9d6a..f24e0dee22e 100644 --- a/src/vs/workbench/browser/labels.ts +++ b/src/vs/workbench/browser/labels.ts @@ -386,7 +386,7 @@ class ResourceLabelWidget extends IconLabel { } if (this.label) { - const configuredLangId = this.label.resource ? getConfiguredLangId(this.modelService, this.label.resource) : null; + const configuredLangId = this.label.resource ? getConfiguredLangId(this.modelService, this.modeService, this.label.resource) : null; if (this.lastKnownConfiguredLangId !== configuredLangId) { clearIconCache = true; this.lastKnownConfiguredLangId = configuredLangId || undefined; diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/legacyLayout.ts similarity index 92% rename from src/vs/workbench/browser/layout.ts rename to src/vs/workbench/browser/legacyLayout.ts index 09e3e1b2b10..e47480d7e9f 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/legacyLayout.ts @@ -43,9 +43,9 @@ const HIDE_PANEL_HEIGHT_THRESHOLD = 50; const HIDE_PANEL_WIDTH_THRESHOLD = 100; /** - * The workbench layout is responsible to lay out all parts that make the Workbench. + * @deprecated to be replaced by new Grid layout */ -export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutProvider, IHorizontalSashLayoutProvider { +export class WorkbenchLegacyLayout extends Disposable implements IVerticalSashLayoutProvider, IHorizontalSashLayoutProvider { private static readonly sashXOneWidthSettingsKey = 'workbench.sidebar.width'; private static readonly sashXTwoWidthSettingsKey = 'workbench.panel.width'; @@ -103,13 +103,13 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr } private restorePreviousState(): void { - this._sidebarWidth = Math.max(this.partLayoutInfo.sidebar.minWidth, this.storageService.getInteger(WorkbenchLayout.sashXOneWidthSettingsKey, StorageScope.GLOBAL, DEFAULT_SIDEBAR_PART_WIDTH)); + this._sidebarWidth = Math.max(this.partLayoutInfo.sidebar.minWidth, this.storageService.getNumber(WorkbenchLegacyLayout.sashXOneWidthSettingsKey, StorageScope.GLOBAL, DEFAULT_SIDEBAR_PART_WIDTH)); - this._panelWidth = Math.max(this.partLayoutInfo.panel.minWidth, this.storageService.getInteger(WorkbenchLayout.sashXTwoWidthSettingsKey, StorageScope.GLOBAL, DEFAULT_PANEL_PART_SIZE)); - this._panelHeight = Math.max(this.partLayoutInfo.panel.minHeight, this.storageService.getInteger(WorkbenchLayout.sashYHeightSettingsKey, StorageScope.GLOBAL, DEFAULT_PANEL_PART_SIZE)); + this._panelWidth = Math.max(this.partLayoutInfo.panel.minWidth, this.storageService.getNumber(WorkbenchLegacyLayout.sashXTwoWidthSettingsKey, StorageScope.GLOBAL, DEFAULT_PANEL_PART_SIZE)); + this._panelHeight = Math.max(this.partLayoutInfo.panel.minHeight, this.storageService.getNumber(WorkbenchLegacyLayout.sashYHeightSettingsKey, StorageScope.GLOBAL, DEFAULT_PANEL_PART_SIZE)); this.panelMaximized = false; - this.panelSizeBeforeMaximized = this.storageService.getInteger(WorkbenchLayout.panelSizeBeforeMaximizedKey, StorageScope.GLOBAL, 0); + this.panelSizeBeforeMaximized = this.storageService.getNumber(WorkbenchLegacyLayout.panelSizeBeforeMaximizedKey, StorageScope.GLOBAL, 0); } private registerListeners(): void { @@ -369,29 +369,29 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr })); this._register(this.sashXOne.onDidEnd(() => { - this.storageService.store(WorkbenchLayout.sashXOneWidthSettingsKey, this.sidebarWidth, StorageScope.GLOBAL); + this.storageService.store(WorkbenchLegacyLayout.sashXOneWidthSettingsKey, this.sidebarWidth, StorageScope.GLOBAL); })); this._register(this.sashY.onDidEnd(() => { - this.storageService.store(WorkbenchLayout.sashYHeightSettingsKey, this.panelHeight, StorageScope.GLOBAL); + this.storageService.store(WorkbenchLegacyLayout.sashYHeightSettingsKey, this.panelHeight, StorageScope.GLOBAL); })); this._register(this.sashXTwo.onDidEnd(() => { - this.storageService.store(WorkbenchLayout.sashXTwoWidthSettingsKey, this.panelWidth, StorageScope.GLOBAL); + this.storageService.store(WorkbenchLegacyLayout.sashXTwoWidthSettingsKey, this.panelWidth, StorageScope.GLOBAL); })); this._register(this.sashY.onDidReset(() => { this.panelHeight = this.sidebarHeight * DEFAULT_PANEL_SIZE_COEFFICIENT; - this.storageService.store(WorkbenchLayout.sashYHeightSettingsKey, this.panelHeight, StorageScope.GLOBAL); + this.storageService.store(WorkbenchLegacyLayout.sashYHeightSettingsKey, this.panelHeight, StorageScope.GLOBAL); this.layout(); })); this._register(this.sashXOne.onDidReset(() => { - let activeViewlet = this.viewletService.getActiveViewlet(); - let optimalWidth = activeViewlet && activeViewlet.getOptimalWidth(); - this.sidebarWidth = Math.max(optimalWidth, DEFAULT_SIDEBAR_PART_WIDTH); - this.storageService.store(WorkbenchLayout.sashXOneWidthSettingsKey, this.sidebarWidth, StorageScope.GLOBAL); + const activeViewlet = this.viewletService.getActiveViewlet(); + const optimalWidth = activeViewlet ? activeViewlet.getOptimalWidth() : null; + this.sidebarWidth = typeof optimalWidth === 'number' ? Math.max(optimalWidth, DEFAULT_SIDEBAR_PART_WIDTH) : DEFAULT_SIDEBAR_PART_WIDTH; + this.storageService.store(WorkbenchLegacyLayout.sashXOneWidthSettingsKey, this.sidebarWidth, StorageScope.GLOBAL); this.partService.setSideBarHidden(false); this.layout(); @@ -399,7 +399,7 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr this._register(this.sashXTwo.onDidReset(() => { this.panelWidth = (this.workbenchSize.width - this.sidebarWidth - this.activitybarWidth) * DEFAULT_PANEL_SIZE_COEFFICIENT; - this.storageService.store(WorkbenchLayout.sashXTwoWidthSettingsKey, this.panelWidth, StorageScope.GLOBAL); + this.storageService.store(WorkbenchLegacyLayout.sashXTwoWidthSettingsKey, this.panelWidth, StorageScope.GLOBAL); this.layout(); })); @@ -423,7 +423,7 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr } this.statusbarHeight = isStatusbarHidden ? 0 : this.partLayoutInfo.statusbar.height; - this.titlebarHeight = isTitlebarHidden ? 0 : this.partLayoutInfo.titlebar.height / (!menubarVisibility || menubarVisibility === 'hidden' ? getZoomFactor() : 1); // adjust for zoom prevention + this.titlebarHeight = isTitlebarHidden ? 0 : this.partLayoutInfo.titlebar.height / (isMacintosh || !menubarVisibility || menubarVisibility === 'hidden' ? getZoomFactor() : 1); // adjust for zoom prevention this.sidebarHeight = this.workbenchSize.height - this.statusbarHeight - this.titlebarHeight; let sidebarSize = new Dimension(this.sidebarWidth, this.sidebarHeight); @@ -474,7 +474,7 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr } } - this.storageService.store(WorkbenchLayout.panelSizeBeforeMaximizedKey, this.panelSizeBeforeMaximized, StorageScope.GLOBAL); + this.storageService.store(WorkbenchLegacyLayout.panelSizeBeforeMaximizedKey, this.panelSizeBeforeMaximized, StorageScope.GLOBAL); const panelDimension = new Dimension(panelWidth, panelHeight); @@ -530,16 +530,16 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr if (!isSidebarHidden) { this.sidebarWidth = sidebarSize.width; - this.storageService.store(WorkbenchLayout.sashXOneWidthSettingsKey, this.sidebarWidth, StorageScope.GLOBAL); + this.storageService.store(WorkbenchLegacyLayout.sashXOneWidthSettingsKey, this.sidebarWidth, StorageScope.GLOBAL); } if (!isPanelHidden) { if (panelPosition === Position.BOTTOM) { this.panelHeight = panelDimension.height; - this.storageService.store(WorkbenchLayout.sashYHeightSettingsKey, this.panelHeight, StorageScope.GLOBAL); + this.storageService.store(WorkbenchLegacyLayout.sashYHeightSettingsKey, this.panelHeight, StorageScope.GLOBAL); } else { this.panelWidth = panelDimension.width; - this.storageService.store(WorkbenchLayout.sashXTwoWidthSettingsKey, this.panelWidth, StorageScope.GLOBAL); + this.storageService.store(WorkbenchLegacyLayout.sashXTwoWidthSettingsKey, this.panelWidth, StorageScope.GLOBAL); } } @@ -596,10 +596,10 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr size(activitybarContainer, null, activityBarSize.height); if (sidebarPosition === Position.LEFT) { this.parts.activitybar.getContainer().style.right = ''; - position(activitybarContainer, this.titlebarHeight, null, 0, 0); + position(activitybarContainer, this.titlebarHeight, undefined, 0, 0); } else { this.parts.activitybar.getContainer().style.left = ''; - position(activitybarContainer, this.titlebarHeight, 0, 0, null); + position(activitybarContainer, this.titlebarHeight, 0, 0, undefined); } if (isActivityBarHidden) { hide(activitybarContainer); diff --git a/src/vs/workbench/browser/media/style.css b/src/vs/workbench/browser/media/style.css index 0bd743e7af5..488552dc37c 100644 --- a/src/vs/workbench/browser/media/style.css +++ b/src/vs/workbench/browser/media/style.css @@ -91,16 +91,16 @@ body { cursor: pointer; } -.monaco-workbench .monaco-font-aliasing-antialiased { +.monaco-workbench.monaco-font-aliasing-antialiased { -webkit-font-smoothing: antialiased; } -.monaco-workbench .monaco-font-aliasing-none { +.monaco-workbench.monaco-font-aliasing-none { -webkit-font-smoothing: none; } @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { - .monaco-workbench .monaco-font-aliasing-auto { + .monaco-workbench.monaco-font-aliasing-auto { -webkit-font-smoothing: antialiased; } } diff --git a/src/vs/workbench/browser/panel.ts b/src/vs/workbench/browser/panel.ts index eee8dbfc218..d33465167e2 100644 --- a/src/vs/workbench/browser/panel.ts +++ b/src/vs/workbench/browser/panel.ts @@ -61,6 +61,13 @@ export class PanelRegistry extends CompositeRegistry { getDefaultPanelId(): string { return this.defaultPanelId; } + + /** + * Find out if a panel exists with the provided ID. + */ + hasPanel(id: string): boolean { + return this.getPanels().some(panel => panel.id === id); + } } /** diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts index b8b8e289b18..caf40047a5a 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts @@ -38,11 +38,11 @@ const SCM_VIEWLET_ID = 'workbench.view.scm'; interface ICachedViewlet { id: string; - iconUrl: UriComponents; + iconUrl?: UriComponents; pinned: boolean; - order: number; + order?: number; visible: boolean; - views?: { when: string }[]; + views?: { when?: string }[]; } export class ActivitybarPart extends Part implements ISerializableView { @@ -110,6 +110,7 @@ export class ActivitybarPart extends Part implements ISerializableView { private registerListeners(): void { + // Viewlet registration this._register(this.viewletService.onDidViewletRegister(viewlet => this.onDidRegisterViewlets([viewlet]))); this._register(this.viewletService.onDidViewletDeregister(({ id }) => this.removeComposite(id, true))); @@ -119,6 +120,7 @@ export class ActivitybarPart extends Part implements ISerializableView { // Deactivate viewlet action on close this._register(this.viewletService.onDidViewletClose(viewlet => this.compositeBar.deactivateComposite(viewlet.getId()))); + // Extension registration let disposables: IDisposable[] = []; this._register(this.extensionService.onDidRegisterExtensions(() => { disposables = dispose(disposables); @@ -126,11 +128,13 @@ export class ActivitybarPart extends Part implements ISerializableView { this.compositeBar.onDidChange(() => this.saveCachedViewlets(), this, disposables); this.storageService.onDidChangeStorage(e => this.onDidStorageChange(e), this, disposables); })); + this._register(toDisposable(() => dispose(disposables))); } private onDidRegisterExtensions(): void { this.removeNotExistingComposites(); + for (const viewlet of this.viewletService.getViewlets()) { this.enableCompositeActions(viewlet); const viewContainer = this.getViewContainer(viewlet.id); @@ -142,6 +146,7 @@ export class ActivitybarPart extends Part implements ISerializableView { } } } + this.saveCachedViewlets(); } @@ -154,16 +159,21 @@ export class ActivitybarPart extends Part implements ISerializableView { } private onDidViewletOpen(viewlet: IViewlet): void { + // Update the composite bar by adding - this.compositeBar.addComposite(this.viewletService.getViewlet(viewlet.getId())); + const foundViewlet = this.viewletService.getViewlet(viewlet.getId()); + if (foundViewlet) { + this.compositeBar.addComposite(foundViewlet); + } this.compositeBar.activateComposite(viewlet.getId()); const viewletDescriptor = this.viewletService.getViewlet(viewlet.getId()); - const viewContainer = this.getViewContainer(viewletDescriptor.id); - if (viewContainer) { - const viewDescriptors = this.viewsService.getViewDescriptors(viewContainer); - if (viewDescriptors && viewDescriptors.activeViewDescriptors.length === 0) { - // Update the composite bar by hiding - this.removeComposite(viewletDescriptor.id, true); + if (viewletDescriptor) { + const viewContainer = this.getViewContainer(viewletDescriptor.id); + if (viewContainer) { + const viewDescriptors = this.viewsService.getViewDescriptors(viewContainer); + if (viewDescriptors && viewDescriptors.activeViewDescriptors.length === 0) { + this.removeComposite(viewletDescriptor.id, true); // Update the composite bar by hiding + } } } } @@ -236,7 +246,7 @@ export class ActivitybarPart extends Part implements ISerializableView { badgeBackground: theme.getColor(ACTIVITY_BAR_BADGE_BACKGROUND), badgeForeground: theme.getColor(ACTIVITY_BAR_BADGE_FOREGROUND), dragAndDropBackground: theme.getColor(ACTIVITY_BAR_DRAG_AND_DROP_BACKGROUND), - activeBackgroundColor: null, inactiveBackgroundColor: null, activeBorderBottomColor: null, + activeBackgroundColor: undefined, inactiveBackgroundColor: undefined, activeBorderBottomColor: undefined, }; } @@ -306,7 +316,7 @@ export class ActivitybarPart extends Part implements ISerializableView { private shouldBeHidden(viewletId: string, cachedViewlet: ICachedViewlet): boolean { return cachedViewlet && cachedViewlet.views && cachedViewlet.views.length - ? cachedViewlet.views.every(({ when }) => when && !this.contextKeyService.contextMatchesRules(ContextKeyExpr.deserialize(when))) + ? cachedViewlet.views.every(({ when }) => !!when && !this.contextKeyService.contextMatchesRules(ContextKeyExpr.deserialize(when))) : viewletId === TEST_VIEW_CONTAINER_ID /* Hide Test viewlet for the first time or it had no views registered before */; } @@ -325,6 +335,7 @@ export class ActivitybarPart extends Part implements ISerializableView { } else { this.compositeBar.removeComposite(compositeId); } + const compositeActions = this.compositeActions[compositeId]; if (compositeActions) { compositeActions.activityAction.dispose(); @@ -338,6 +349,7 @@ export class ActivitybarPart extends Part implements ISerializableView { if (activityAction instanceof PlaceHolderViewletActivityAction) { activityAction.setActivity(viewlet); } + if (pinnedAction instanceof PlaceHolderToggleCompositePinnedAction) { pinnedAction.setActivity(viewlet); } @@ -345,6 +357,7 @@ export class ActivitybarPart extends Part implements ISerializableView { getPinnedViewletIds(): string[] { const pinnedCompositeIds = this.compositeBar.getPinnedComposites().map(v => v.id); + return this.viewletService.getViewlets() .filter(v => this.compositeBar.isPinned(v.id)) .sort((v1, v2) => pinnedCompositeIds.indexOf(v1.id) - pinnedCompositeIds.indexOf(v2.id)) @@ -363,14 +376,13 @@ export class ActivitybarPart extends Part implements ISerializableView { } // Pass to super - const sizes = super.layout(dim1 instanceof Dimension ? dim1 : new Dimension(dim1, dim2)); + const sizes = super.layout(dim1 instanceof Dimension ? dim1 : new Dimension(dim1, dim2!)); this.dimension = sizes[1]; let availableHeight = this.dimension.height; if (this.globalActionBar) { - // adjust height for global actions showing - availableHeight -= (this.globalActionBar.items.length * ActivitybarPart.ACTION_HEIGHT); + availableHeight -= (this.globalActionBar.items.length * ActivitybarPart.ACTION_HEIGHT); // adjust height for global actions showing } this.compositeBar.layout(new Dimension(dim1 instanceof Dimension ? dim1.width : dim1, availableHeight)); @@ -414,13 +426,14 @@ export class ActivitybarPart extends Part implements ISerializableView { private saveCachedViewlets(): void { const state: ICachedViewlet[] = []; - const compositeItems = this.compositeBar.getCompositeBarItems(); const allViewlets = this.viewletService.getViewlets(); + + const compositeItems = this.compositeBar.getCompositeBarItems(); for (const compositeItem of compositeItems) { const viewContainer = this.getViewContainer(compositeItem.id); const viewlet = allViewlets.filter(({ id }) => id === compositeItem.id)[0]; if (viewlet) { - const views: { when: string }[] = []; + const views: { when: string | undefined }[] = []; if (viewContainer) { const viewDescriptors = this.viewsService.getViewDescriptors(viewContainer); if (viewDescriptors) { @@ -432,6 +445,7 @@ export class ActivitybarPart extends Part implements ISerializableView { state.push({ id: compositeItem.id, iconUrl: viewlet.iconUrl, views, pinned: compositeItem && compositeItem.pinned, order: compositeItem ? compositeItem.order : undefined, visible: compositeItem && compositeItem.visible }); } } + this.cachedViewletsValue = JSON.stringify(state); } @@ -442,6 +456,7 @@ export class ActivitybarPart extends Part implements ISerializableView { serialized.visible = isUndefinedOrNull(serialized.visible) ? true : serialized.visible; return serialized; }); + for (const old of this.loadOldCachedViewlets()) { const cachedViewlet = cachedViewlets.filter(cached => cached.id === old.id)[0]; if (cachedViewlet) { @@ -449,6 +464,7 @@ export class ActivitybarPart extends Part implements ISerializableView { cachedViewlet.views = old.views; } } + return cachedViewlets; } @@ -456,14 +472,16 @@ export class ActivitybarPart extends Part implements ISerializableView { const previousState = this.storageService.get('workbench.activity.placeholderViewlets', StorageScope.GLOBAL, '[]'); const result = (JSON.parse(previousState)); this.storageService.remove('workbench.activity.placeholderViewlets', StorageScope.GLOBAL); + return result; } - private _cachedViewletsValue: string; + private _cachedViewletsValue: string | null; private get cachedViewletsValue(): string { if (!this._cachedViewletsValue) { this._cachedViewletsValue = this.getStoredCachedViewletsValue(); } + return this._cachedViewletsValue; } @@ -485,8 +503,9 @@ export class ActivitybarPart extends Part implements ISerializableView { private getViewContainer(viewletId: string): ViewContainer | undefined { // TODO: @Joao Remove this after moving SCM Viewlet to ViewContainerViewlet - https://github.com/Microsoft/vscode/issues/49054 if (viewletId === SCM_VIEWLET_ID) { - return null; + return undefined; } + const viewContainerRegistry = Registry.as(ViewContainerExtensions.ViewContainersRegistry); return viewContainerRegistry.get(viewletId); } diff --git a/src/vs/workbench/browser/parts/compositeBar.ts b/src/vs/workbench/browser/parts/compositeBar.ts index 4869bb20391..a1367bbb2e6 100644 --- a/src/vs/workbench/browser/parts/compositeBar.ts +++ b/src/vs/workbench/browser/parts/compositeBar.ts @@ -23,9 +23,9 @@ import { Emitter, Event } from 'vs/base/common/event'; export interface ICompositeBarItem { id: string; - name: string; + name?: string; pinned: boolean; - order: number; + order?: number; visible: boolean; } @@ -49,8 +49,8 @@ export class CompositeBar extends Widget implements ICompositeBar { private dimension: Dimension; private compositeSwitcherBar: ActionBar; - private compositeOverflowAction: CompositeOverflowActivityAction; - private compositeOverflowActionItem: CompositeOverflowActivityActionItem; + private compositeOverflowAction: CompositeOverflowActivityAction | null; + private compositeOverflowActionItem: CompositeOverflowActivityActionItem | null; private model: CompositeBarModel; private visibleComposites: string[]; @@ -174,7 +174,7 @@ export class CompositeBar extends Widget implements ICompositeBar { if (this.model.activate(id)) { // Update if current composite is neither visible nor pinned // or previous active composite is not pinned - if (this.visibleComposites.indexOf(id) === - 1 || !this.model.activeItem.pinned || (previousActiveItem && !previousActiveItem.pinned)) { + if (this.visibleComposites.indexOf(id) === - 1 || (!!this.model.activeItem && !this.model.activeItem.pinned) || (previousActiveItem && !previousActiveItem.pinned)) { this.updateCompositeSwitcher(); } } @@ -304,7 +304,7 @@ export class CompositeBar extends Widget implements ICompositeBar { let size = 0; const limit = this.options.orientation === ActionsOrientation.VERTICAL ? this.dimension.height : this.dimension.width; for (let i = 0; i < compositesToShow.length && size <= limit; i++) { - size += this.compositeSizeInBar.get(compositesToShow[i]); + size += this.compositeSizeInBar.get(compositesToShow[i])!; if (size > limit) { maxVisible = i; } @@ -312,19 +312,19 @@ export class CompositeBar extends Widget implements ICompositeBar { overflows = compositesToShow.length > maxVisible; if (overflows) { - size -= this.compositeSizeInBar.get(compositesToShow[maxVisible]); + size -= this.compositeSizeInBar.get(compositesToShow[maxVisible])!; compositesToShow = compositesToShow.slice(0, maxVisible); size += this.options.overflowActionSize; } // Check if we need to make extra room for the overflow action if (size > limit) { - size -= this.compositeSizeInBar.get(compositesToShow.pop()); + size -= this.compositeSizeInBar.get(compositesToShow.pop()!)!; } // We always try show the active composite - if (this.model.activeItem && compositesToShow.every(compositeId => compositeId !== this.model.activeItem.id)) { - const removedComposite = compositesToShow.pop(); - size = size - this.compositeSizeInBar.get(removedComposite) + this.compositeSizeInBar.get(this.model.activeItem.id); + if (this.model.activeItem && compositesToShow.every(compositeId => !!this.model.activeItem && compositeId !== this.model.activeItem.id)) { + const removedComposite = compositesToShow.pop()!; + size = size - this.compositeSizeInBar.get(removedComposite)! + this.compositeSizeInBar.get(this.model.activeItem.id)!; compositesToShow.push(this.model.activeItem.id); } @@ -342,7 +342,9 @@ export class CompositeBar extends Widget implements ICompositeBar { this.compositeOverflowAction.dispose(); this.compositeOverflowAction = null; - this.compositeOverflowActionItem.dispose(); + if (this.compositeOverflowActionItem) { + this.compositeOverflowActionItem.dispose(); + } this.compositeOverflowActionItem = null; } @@ -378,7 +380,11 @@ export class CompositeBar extends Widget implements ICompositeBar { // Add overflow action as needed if ((visibleCompositesChange && overflows) || this.compositeSwitcherBar.length() === 0) { - this.compositeOverflowAction = this.instantiationService.createInstance(CompositeOverflowActivityAction, () => this.compositeOverflowActionItem.showMenu()); + this.compositeOverflowAction = this.instantiationService.createInstance(CompositeOverflowActivityAction, () => { + if (this.compositeOverflowActionItem) { + this.compositeOverflowActionItem.showMenu(); + } + }); this.compositeOverflowActionItem = this.instantiationService.createInstance( CompositeOverflowActivityActionItem, this.compositeOverflowAction, @@ -398,7 +404,7 @@ export class CompositeBar extends Widget implements ICompositeBar { this._onDidChange.fire(); } - private getOverflowingComposites(): { id: string, name: string }[] { + private getOverflowingComposites(): { id: string, name?: string }[] { let overflowingIds = this.model.visibleItems.filter(item => item.pinned).map(item => item.id); // Show the active composite even if it is not pinned @@ -453,7 +459,7 @@ class CompositeBarModel { private _items: ICompositeBarModelItem[]; private readonly options: ICompositeBarOptions; - activeItem: ICompositeBarModelItem; + activeItem?: ICompositeBarModelItem; constructor( items: ICompositeBarItem[], @@ -507,7 +513,7 @@ class CompositeBarModel { return this.items.filter(item => item.visible && item.pinned); } - private createCompositeBarItem(id: string, name: string, order: number | undefined, pinned: boolean, visible: boolean): ICompositeBarModelItem { + private createCompositeBarItem(id: string, name: string | undefined, order: number | undefined, pinned: boolean, visible: boolean): ICompositeBarModelItem { const options = this.options; return { id, name, pinned, order, visible, @@ -541,7 +547,7 @@ class CompositeBarModel { this.items.push(item); } else { let index = 0; - while (index < this.items.length && this.items[index].order < order) { + while (index < this.items.length && typeof this.items[index].order === 'number' && this.items[index].order! < order) { index++; } this.items.splice(index, 0, item); diff --git a/src/vs/workbench/browser/parts/compositeBarActions.ts b/src/vs/workbench/browser/parts/compositeBarActions.ts index 54c9a40efa7..f6763258e5e 100644 --- a/src/vs/workbench/browser/parts/compositeBarActions.ts +++ b/src/vs/workbench/browser/parts/compositeBarActions.ts @@ -108,14 +108,14 @@ export class ActivityAction extends Action { } export interface ICompositeBarColors { - activeBackgroundColor: Color; - inactiveBackgroundColor: Color; - activeBorderBottomColor: Color; - activeForegroundColor: Color; - inactiveForegroundColor: Color; - badgeBackground: Color; - badgeForeground: Color; - dragAndDropBackground: Color; + activeBackgroundColor?: Color; + inactiveBackgroundColor?: Color; + activeBorderBottomColor?: Color; + activeForegroundColor?: Color; + inactiveForegroundColor?: Color; + badgeBackground?: Color; + badgeForeground?: Color; + dragAndDropBackground?: Color; } export interface IActivityActionItemOptions extends IBaseActionItemOptions { @@ -252,9 +252,9 @@ export class ActivityActionItem extends BaseActionItem { const noOfThousands = badge.number / 1000; const floor = Math.floor(noOfThousands); if (noOfThousands > floor) { - number = nls.localize('largeNumberBadge1', '{0}k+', floor); + number = `${floor}K+`; } else { - number = nls.localize('largeNumberBadge2', '{0}k', noOfThousands); + number = `${noOfThousands}K`; } } this.badgeContent.textContent = number; diff --git a/src/vs/workbench/browser/parts/compositePart.ts b/src/vs/workbench/browser/parts/compositePart.ts index 30f54594cf3..a9a2883e398 100644 --- a/src/vs/workbench/browser/parts/compositePart.ts +++ b/src/vs/workbench/browser/parts/compositePart.ts @@ -98,6 +98,7 @@ export abstract class CompositePart extends Part { } protected openComposite(id: string, focus?: boolean): Composite | undefined { + // Check if composite already visible and just focus in that case if (this.activeComposite && this.activeComposite.getId() === id) { if (focus) { @@ -449,6 +450,7 @@ export abstract class CompositePart extends Part { getProgressIndicator(id: string): IProgressService | null { const compositeItem = this.instantiatedCompositeItems.get(id); + return compositeItem ? compositeItem.progressService : null; } @@ -467,6 +469,7 @@ export abstract class CompositePart extends Part { layout(dimension: Dimension): Dimension[]; layout(width: number, height: number): void; layout(dim1: Dimension | number, dim2?: number): Dimension[] | void { + // Pass to super const sizes = super.layout(dim1 instanceof Dimension ? dim1 : new Dimension(dim1, dim2!)); @@ -483,8 +486,7 @@ export abstract class CompositePart extends Part { protected removeComposite(compositeId: string): boolean { if (this.activeComposite && this.activeComposite.getId() === compositeId) { - // do not remove active compoiste - return false; + return false; // do not remove active composite } delete this.mapCompositeToCompositeContainer[compositeId]; @@ -495,6 +497,7 @@ export abstract class CompositePart extends Part { dispose(compositeItem.disposable); this.instantiatedCompositeItems.delete(compositeId); } + return true; } diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts index 8eaf0a8810b..84acd81c48f 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts @@ -221,7 +221,7 @@ export class BreadcrumbsControl { input = input.master; } - if (!input || !input.getResource() || (input.getResource().scheme !== Schemas.untitled && !this._fileService.canHandleResource(input.getResource()))) { + if (!input || !input.getResource() || (input.getResource()!.scheme !== Schemas.untitled && !this._fileService.canHandleResource(input.getResource()!))) { // cleanup and return when there is no input or when // we cannot handle this input this._ckBreadcrumbsPossible.set(false); @@ -238,7 +238,7 @@ export class BreadcrumbsControl { this._ckBreadcrumbsPossible.set(true); let editor = this._getActiveCodeEditor(); - let model = new EditorBreadcrumbsModel(input.getResource(), editor, this._workspaceService, this._configurationService); + let model = new EditorBreadcrumbsModel(input.getResource()!, editor, this._workspaceService, this._configurationService); dom.toggleClass(this.domNode, 'relative-path', model.isRelative()); let updateBreadcrumbs = () => { @@ -435,7 +435,7 @@ export class BreadcrumbsControl { this._ckBreadcrumbsActive.set(value); } - private _revealInEditor(event: IBreadcrumbsItemEvent, element: any, group: SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE, pinned: boolean = false): void { + private _revealInEditor(event: IBreadcrumbsItemEvent, element: any, group: SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE | undefined, pinned: boolean = false): void { if (element instanceof FileElement) { if (element.kind === FileKind.FILE) { // open file in any editor @@ -450,14 +450,16 @@ export class BreadcrumbsControl { } else if (element instanceof OutlineElement) { // open symbol in code editor - let model = OutlineModel.get(element); - this._codeEditorService.openCodeEditor({ - resource: model.textModel.uri, - options: { - selection: Range.collapseToStart(element.symbol.selectionRange), - revealInCenterIfOutsideViewport: true - } - }, this._getActiveCodeEditor(), group === SIDE_GROUP); + const model = OutlineModel.get(element); + if (model) { + this._codeEditorService.openCodeEditor({ + resource: model.textModel.uri, + options: { + selection: Range.collapseToStart(element.symbol.selectionRange), + revealInCenterIfOutsideViewport: true + } + }, this._getActiveCodeEditor() || null, group === SIDE_GROUP); + } } } @@ -640,7 +642,9 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ } widget.setFocused(undefined); widget.setSelection(undefined); - groups.activeGroup.activeControl.focus(); + if (groups.activeGroup.activeControl) { + groups.activeGroup.activeControl.focus(); + } } }); KeybindingsRegistry.registerCommandAndKeybindingRule({ @@ -651,15 +655,20 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ handler(accessor) { const editors = accessor.get(IEditorService); const lists = accessor.get(IListService); - const element = lists.lastFocusedList.getFocus(); + const element = lists.lastFocusedList ? lists.lastFocusedList.getFocus() : undefined; if (element instanceof OutlineElement) { + const outlineElement = OutlineModel.get(element); + if (!outlineElement) { + return undefined; + } + // open symbol in editor return editors.openEditor({ - resource: OutlineModel.get(element).textModel.uri, + resource: outlineElement.textModel.uri, options: { selection: Range.collapseToStart(element.symbol.selectionRange) } }, SIDE_GROUP); - } else if (URI.isUri(element.resource)) { + } else if (element && URI.isUri(element.resource)) { // open file in editor return editors.openEditor({ resource: element.resource, diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts index decc4f57d70..ff004ddd94a 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts @@ -25,9 +25,10 @@ import { ResourceLabels, IResourceLabel, DEFAULT_LABELS_CONTAINER } from 'vs/wor 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'; -import { IAsyncDataSource, ITreeRenderer, ITreeNode, ITreeFilter, TreeVisibility, ITreeSorter } from 'vs/base/browser/ui/tree/tree'; +import { IAsyncDataSource, ITreeRenderer, ITreeNode, ITreeFilter, TreeVisibility, ITreeSorter, IDataSource } from 'vs/base/browser/ui/tree/tree'; import { OutlineVirtualDelegate, OutlineGroupRenderer, OutlineElementRenderer, OutlineItemComparator, OutlineIdentityProvider, OutlineNavigationLabelProvider, OutlineDataSource, OutlineSortOrder, OutlineItem } from 'vs/editor/contrib/documentSymbols/outlineTree'; import { IIdentityProvider, IListVirtualDelegate, IKeyboardNavigationLabelProvider } from 'vs/base/browser/ui/list/list'; +import { IDataTreeOptions } from 'vs/base/browser/ui/tree/dataTree'; export function createBreadcrumbsPicker(instantiationService: IInstantiationService, parent: HTMLElement, element: BreadcrumbElement): BreadcrumbsPicker { const ctor: IConstructorSignature1 = element instanceof FileElement @@ -87,11 +88,11 @@ export abstract class BreadcrumbsPicker { this._arrow = document.createElement('div'); this._arrow.className = 'arrow'; - this._arrow.style.borderColor = `transparent transparent ${color.toString()}`; + this._arrow.style.borderColor = `transparent transparent ${color ? color.toString() : ''}`; this._domNode.appendChild(this._arrow); this._treeContainer = document.createElement('div'); - this._treeContainer.style.background = color.toString(); + this._treeContainer.style.background = color ? color.toString() : ''; this._treeContainer.style.paddingTop = '2px'; this._treeContainer.style.boxShadow = `0px 5px 8px ${this._themeService.getTheme().getColor(widgetShadow)}`; this._domNode.appendChild(this._treeContainer); @@ -150,13 +151,13 @@ export abstract class BreadcrumbsPicker { this._arrow.style.marginLeft = `${this._layoutInfo.arrowOffset}px`; this._treeContainer.style.height = `${treeHeight}px`; this._treeContainer.style.width = `${this._layoutInfo.width}px`; - this._tree.layout(); + this._tree.layout(treeHeight, this._layoutInfo.width); } protected abstract _setInput(element: BreadcrumbElement): Promise; protected abstract _createTree(container: HTMLElement): Tree; - protected abstract _getTargetFromEvent(element: any, payload: UIEvent): any | undefined; + protected abstract _getTargetFromEvent(element: any, payload: UIEvent | undefined): any | undefined; } //#region - Files @@ -219,10 +220,10 @@ class FileDataSource implements IAsyncDataSource { - for (let child of stat.children) { + for (const child of stat.children || []) { this._parents.set(stat, child); } - return stat.children; + return stat.children || []; }); } } @@ -329,7 +330,7 @@ class FileFilter implements ITreeFilter { return true; } - const expression = this._cachedExpressions.get(folder.uri.toString()); + const expression = this._cachedExpressions.get(folder.uri.toString())!; return !expression(element.resource.path, basename(element.resource)); } } @@ -450,7 +451,14 @@ export class BreadcrumbsOutlinePicker extends BreadcrumbsPicker { } protected _createTree(container: HTMLElement) { - return this._instantiationService.createInstance( + return this._instantiationService.createInstance< + HTMLElement, + IListVirtualDelegate, + ITreeRenderer[], + IDataSource, + IDataTreeOptions, + WorkbenchDataTree + >( WorkbenchDataTree, container, new OutlineVirtualDelegate(), @@ -474,7 +482,7 @@ export class BreadcrumbsOutlinePicker extends BreadcrumbsPicker { protected _setInput(input: BreadcrumbElement): Promise { const element = input as TreeElement; - const model = OutlineModel.get(element); + const model = OutlineModel.get(element)!; const tree = this._tree as WorkbenchDataTree; tree.setInput(model); diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index 5c1e6ef0f87..2daccc8005a 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -103,7 +103,7 @@ Registry.as(EditorExtensions.Editors).registerEditor( interface ISerializedUntitledEditorInput { resource: string; resourceJSON: object; - modeId: string; + modeId: string | null; encoding: string; } diff --git a/src/vs/workbench/browser/parts/editor/editor.ts b/src/vs/workbench/browser/parts/editor/editor.ts index 5da1159887f..0fac3e2d9e6 100644 --- a/src/vs/workbench/browser/parts/editor/editor.ts +++ b/src/vs/workbench/browser/parts/editor/editor.ts @@ -77,7 +77,7 @@ export interface IEditorOpeningEvent extends IEditorIdentifier { * to return a promise that resolves to NULL to prevent the opening * alltogether. */ - prevent(callback: () => Promise): void; + prevent(callback: () => undefined | Promise): void; } export interface IEditorGroupsAccessor { diff --git a/src/vs/workbench/browser/parts/editor/editorActions.ts b/src/vs/workbench/browser/parts/editor/editorActions.ts index 05ed83a7bca..0ef54f44bf5 100644 --- a/src/vs/workbench/browser/parts/editor/editorActions.ts +++ b/src/vs/workbench/browser/parts/editor/editorActions.ts @@ -882,7 +882,7 @@ export class ResetGroupSizesAction extends Action { export class MaximizeGroupAction extends Action { static readonly ID = 'workbench.action.maximizeEditor'; - static readonly LABEL = nls.localize('maximizeEditor', "Maximize Editor Group and Hide Sidebar"); + static readonly LABEL = nls.localize('maximizeEditor', "Maximize Editor Group and Hide Side Bar"); constructor( id: string, @@ -952,7 +952,7 @@ export class OpenNextEditor extends BaseNavigateEditorAction { // Navigate in active group if possible const activeGroup = this.editorGroupService.activeGroup; const activeGroupEditors = activeGroup.getEditors(EditorsOrder.SEQUENTIAL); - const activeEditorIndex = activeGroupEditors.indexOf(activeGroup.activeEditor); + const activeEditorIndex = activeGroup.activeEditor ? activeGroupEditors.indexOf(activeGroup.activeEditor) : -1; if (activeEditorIndex + 1 < activeGroupEditors.length) { return { editor: activeGroupEditors[activeEditorIndex + 1], groupId: activeGroup.id }; } @@ -987,7 +987,7 @@ export class OpenPreviousEditor extends BaseNavigateEditorAction { // Navigate in active group if possible const activeGroup = this.editorGroupService.activeGroup; const activeGroupEditors = activeGroup.getEditors(EditorsOrder.SEQUENTIAL); - const activeEditorIndex = activeGroupEditors.indexOf(activeGroup.activeEditor); + const activeEditorIndex = activeGroup.activeEditor ? activeGroupEditors.indexOf(activeGroup.activeEditor) : -1; if (activeEditorIndex > 0) { return { editor: activeGroupEditors[activeEditorIndex - 1], groupId: activeGroup.id }; } @@ -1020,7 +1020,7 @@ export class OpenNextEditorInGroup extends BaseNavigateEditorAction { protected navigate(): IEditorIdentifier { const group = this.editorGroupService.activeGroup; const editors = group.getEditors(EditorsOrder.SEQUENTIAL); - const index = editors.indexOf(group.activeEditor); + const index = group.activeEditor ? editors.indexOf(group.activeEditor) : -1; return { editor: index + 1 < editors.length ? editors[index + 1] : editors[0], groupId: group.id }; } @@ -1043,7 +1043,7 @@ export class OpenPreviousEditorInGroup extends BaseNavigateEditorAction { protected navigate(): IEditorIdentifier { const group = this.editorGroupService.activeGroup; const editors = group.getEditors(EditorsOrder.SEQUENTIAL); - const index = editors.indexOf(group.activeEditor); + const index = group.activeEditor ? editors.indexOf(group.activeEditor) : -1; return { editor: index > 0 ? editors[index - 1] : editors[editors.length - 1], groupId: group.id }; } diff --git a/src/vs/workbench/browser/parts/editor/editorCommands.ts b/src/vs/workbench/browser/parts/editor/editorCommands.ts index 25e420329d5..e1e12fc7790 100644 --- a/src/vs/workbench/browser/parts/editor/editorCommands.ts +++ b/src/vs/workbench/browser/parts/editor/editorCommands.ts @@ -16,7 +16,7 @@ import { URI } from 'vs/base/common/uri'; import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; import { IListService } from 'vs/platform/list/browser/listService'; import { List } from 'vs/base/browser/ui/list/listWidget'; -import { distinct } from 'vs/base/common/arrays'; +import { distinct, coalesce } from 'vs/base/common/arrays'; import { IEditorGroupsService, IEditorGroup, GroupDirection, GroupLocation, GroupsOrder, preferredSideBySideGroupDirection, EditorGroupLayout } from 'vs/workbench/services/editor/common/editorGroupsService'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; @@ -52,9 +52,9 @@ export const NAVIGATE_IN_ACTIVE_GROUP_PREFIX = 'edt active '; export const OPEN_EDITOR_AT_INDEX_COMMAND_ID = 'workbench.action.openEditorAtIndex'; export interface ActiveEditorMoveArguments { - to?: 'first' | 'last' | 'left' | 'right' | 'up' | 'down' | 'center' | 'position' | 'previous' | 'next'; - by?: 'tab' | 'group'; - value?: number; + to: 'first' | 'last' | 'left' | 'right' | 'up' | 'down' | 'center' | 'position' | 'previous' | 'next'; + by: 'tab' | 'group'; + value: number; } const isActiveEditorMoveArg = function (arg: ActiveEditorMoveArguments): boolean { @@ -357,7 +357,7 @@ function registerOpenEditorAtIndexCommands(): void { case 9: return KeyCode.KEY_9; } - return undefined; + throw new Error('invalid index'); } } @@ -409,7 +409,7 @@ function registerFocusEditorGroupAtIndexCommands(): void { case 7: return 'workbench.action.focusEighthEditorGroup'; } - return undefined; + throw new Error('Invalid index'); } function toKeyCode(index: number): KeyCode { @@ -423,7 +423,7 @@ function registerFocusEditorGroupAtIndexCommands(): void { case 7: return KeyCode.KEY_8; } - return undefined; + throw new Error('Invalid index'); } } @@ -528,9 +528,9 @@ function registerCloseEditorCommands() { return Promise.all(groupIds.map(groupId => { const group = editorGroupService.getGroup(groupId); - const editors = contexts + const editors = coalesce(contexts .filter(context => context.groupId === groupId) - .map(context => typeof context.editorIndex === 'number' ? group.getEditor(context.editorIndex) : group.activeEditor); + .map(context => typeof context.editorIndex === 'number' ? group.getEditor(context.editorIndex) : group.activeEditor)); return group.closeEditors(editors); })); @@ -675,7 +675,7 @@ function getCommandsContext(resourceOrContext: URI | IEditorCommandsContext, con return undefined; } -function resolveCommandsContext(editorGroupService: IEditorGroupsService, context?: IEditorCommandsContext): { group: IEditorGroup, editor: IEditorInput, control: IEditor } { +function resolveCommandsContext(editorGroupService: IEditorGroupsService, context?: IEditorCommandsContext): { group: IEditorGroup, editor?: IEditorInput, control?: IEditor } { // Resolve from context let group = context && typeof context.groupId === 'number' ? editorGroupService.getGroup(context.groupId) : undefined; @@ -689,7 +689,7 @@ function resolveCommandsContext(editorGroupService: IEditorGroupsService, contex control = group.activeControl; } - return { group, editor, control }; + return { group, editor: editor || undefined, control: control || undefined }; } export function getMultiSelectedEditorContexts(editorContext: IEditorCommandsContext | undefined, listService: IListService, editorGroupService: IEditorGroupsService): IEditorCommandsContext[] { diff --git a/src/vs/workbench/browser/parts/editor/editorDropTarget.ts b/src/vs/workbench/browser/parts/editor/editorDropTarget.ts index 52272c0d2f4..464a494ba92 100644 --- a/src/vs/workbench/browser/parts/editor/editorDropTarget.ts +++ b/src/vs/workbench/browser/parts/editor/editorDropTarget.ts @@ -28,7 +28,7 @@ class DropOverlay extends Themable { private container: HTMLElement; private overlay: HTMLElement; - private currentDropOperation: IDropOperation; + private currentDropOperation?: IDropOperation; private _disposed: boolean; private cleanupOverlayScheduler: RunOnceScheduler; @@ -103,12 +103,12 @@ class DropOverlay extends Themable { // Update the dropEffect to "copy" if there is no local data to be dragged because // in that case we can only copy the data into and not move it from its source - if (!isDraggingEditor && !isDraggingGroup) { + if (!isDraggingEditor && !isDraggingGroup && e.dataTransfer) { e.dataTransfer.dropEffect = 'copy'; } // Find out if operation is valid - const isCopy = isDraggingGroup ? this.isCopyOperation(e) : isDraggingEditor ? this.isCopyOperation(e, this.editorTransfer.getData(DraggedEditorIdentifier.prototype)[0].identifier) : true; + const isCopy = isDraggingGroup ? this.isCopyOperation(e) : isDraggingEditor ? this.isCopyOperation(e, this.editorTransfer.getData(DraggedEditorIdentifier.prototype)![0].identifier) : true; if (!isCopy) { const sourceGroupView = this.findSourceGroupView(); if (sourceGroupView === this.groupView) { @@ -158,16 +158,16 @@ class DropOverlay extends Themable { })); } - private findSourceGroupView(): IEditorGroupView { + private findSourceGroupView(): IEditorGroupView | undefined { // Check for group transfer if (this.groupTransfer.hasData(DraggedEditorGroupIdentifier.prototype)) { - return this.accessor.getGroup(this.groupTransfer.getData(DraggedEditorGroupIdentifier.prototype)[0].identifier); + return this.accessor.getGroup(this.groupTransfer.getData(DraggedEditorGroupIdentifier.prototype)![0].identifier); } // Check for editor transfer else if (this.editorTransfer.hasData(DraggedEditorIdentifier.prototype)) { - return this.accessor.getGroup(this.editorTransfer.getData(DraggedEditorIdentifier.prototype)[0].identifier.groupId); + return this.accessor.getGroup(this.editorTransfer.getData(DraggedEditorIdentifier.prototype)![0].identifier.groupId); } return undefined; @@ -189,7 +189,7 @@ class DropOverlay extends Themable { // Check for group transfer if (this.groupTransfer.hasData(DraggedEditorGroupIdentifier.prototype)) { - const draggedEditorGroup = this.groupTransfer.getData(DraggedEditorGroupIdentifier.prototype)[0].identifier; + const draggedEditorGroup = this.groupTransfer.getData(DraggedEditorGroupIdentifier.prototype)![0].identifier; // Return if the drop is a no-op const sourceGroup = this.accessor.getGroup(draggedEditorGroup); @@ -222,7 +222,7 @@ class DropOverlay extends Themable { // Check for editor transfer else if (this.editorTransfer.hasData(DraggedEditorIdentifier.prototype)) { - const draggedEditor = this.editorTransfer.getData(DraggedEditorIdentifier.prototype)[0].identifier; + const draggedEditor = this.editorTransfer.getData(DraggedEditorIdentifier.prototype)![0].identifier; const targetGroup = ensureTargetGroup(); // Return if the drop is a no-op @@ -250,7 +250,7 @@ class DropOverlay extends Themable { // Check for URI transfer else { const dropHandler = this.instantiationService.createInstance(ResourcesDropHandler, { allowWorkspaceOpen: true /* open workspace instead of file if dropped */ }); - dropHandler.handleDrop(event, () => ensureTargetGroup(), targetGroup => targetGroup.focus()); + dropHandler.handleDrop(event, () => ensureTargetGroup(), targetGroup => targetGroup!.focus()); } } @@ -298,7 +298,7 @@ class DropOverlay extends Themable { // child.style.top = edgeHeightThreshold + 'px'; // No split if mouse is above certain threshold in the center of the view - let splitDirection: GroupDirection; + let splitDirection: GroupDirection | undefined; if ( mousePosX > edgeWidthThreshold && mousePosX < editorControlWidth - edgeWidthThreshold && mousePosY > edgeHeightThreshold && mousePosY < editorControlHeight - edgeHeightThreshold @@ -429,7 +429,7 @@ class DropOverlay extends Themable { export class EditorDropTarget extends Themable { - private _overlay: DropOverlay; + private _overlay?: DropOverlay; private counter = 0; @@ -447,7 +447,7 @@ export class EditorDropTarget extends Themable { this.registerListeners(); } - private get overlay(): DropOverlay { + private get overlay(): DropOverlay | undefined { if (this._overlay && !this._overlay.disposed) { return this._overlay; } @@ -468,7 +468,7 @@ export class EditorDropTarget extends Themable { if ( !this.editorTransfer.hasData(DraggedEditorIdentifier.prototype) && !this.groupTransfer.hasData(DraggedEditorGroupIdentifier.prototype) && - !event.dataTransfer.types.length // see https://github.com/Microsoft/vscode/issues/25789 + event.dataTransfer && !event.dataTransfer.types.length // see https://github.com/Microsoft/vscode/issues/25789 ) { event.dataTransfer.dropEffect = 'none'; return; // unsupported transfer @@ -510,7 +510,7 @@ export class EditorDropTarget extends Themable { this.disposeOverlay(); } - private findTargetGroupView(child: HTMLElement): IEditorGroupView { + private findTargetGroupView(child: HTMLElement): IEditorGroupView | undefined { const groups = this.accessor.groups; for (const groupView of groups) { if (isAncestor(child, groupView.element)) { diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index 0a3d443d567..421352abd2a 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -411,6 +411,10 @@ export class EditorGroupView extends Themable implements IEditorGroupView { } const activeEditor = this._group.activeEditor; + if (!activeEditor) { + return Promise.resolve(); + } + options.pinned = this._group.isPinned(activeEditor); // preserve pinned state options.preserveFocus = true; // handle focus after editor is opened @@ -561,7 +565,9 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Pin preview editor once user disables preview if (event.oldPartOptions.enablePreview && !event.newPartOptions.enablePreview) { - this.pinEditor(this._group.previewEditor); + if (this._group.previewEditor) { + this.pinEditor(this._group.previewEditor); + } } } @@ -705,7 +711,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { this._onDidFocus.fire(); } - pinEditor(editor: EditorInput = this.activeEditor): void { + pinEditor(editor: EditorInput | undefined = this.activeEditor || undefined): void { if (editor && !this._group.isPinned(editor)) { // Update model @@ -743,7 +749,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { return this.doOpenEditor(editor, options); } - private doOpenEditor(editor: EditorInput, options?: EditorOptions): Promise { + private doOpenEditor(editor: EditorInput, options?: EditorOptions): Promise { // Determine options const openEditorOptions: IEditorOpenOptions = { @@ -752,7 +758,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { active: this._group.count === 0 || !options || !options.inactive }; - if (!openEditorOptions.active && !openEditorOptions.pinned && this._group.isPreview(this._group.activeEditor)) { + if (!openEditorOptions.active && !openEditorOptions.pinned && this._group.activeEditor && this._group.isPreview(this._group.activeEditor)) { // Special case: we are to open an editor inactive and not pinned, but the current active // editor is also not pinned, which means it will get replaced with this one. As such, // the editor can only be active. @@ -781,13 +787,13 @@ export class EditorGroupView extends Themable implements IEditorGroupView { this._group.openEditor(editor, openEditorOptions); // Show editor - return this.doShowEditor(editor, openEditorOptions.active, options); + return this.doShowEditor(editor, !!openEditorOptions.active, options); } - private doShowEditor(editor: EditorInput, active: boolean, options?: EditorOptions): Promise { + private doShowEditor(editor: EditorInput, active: boolean, options?: EditorOptions): Promise { // Show in editor control if the active editor changed - let openEditorPromise: Promise; + let openEditorPromise: Promise; if (active) { openEditorPromise = this.editorControl.openEditor(editor, options).then(result => { @@ -830,7 +836,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { actions }); - Event.once(handle.onDidClose)(() => dispose(actions.primary)); + Event.once(handle.onDidClose)(() => actions.primary && dispose(actions.primary)); } // Event @@ -855,7 +861,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Do not modify original array editors = editors.slice(0); - let result: IEditor; + let result: IEditor | null; // Use the first editor as active editor const { editor, options } = editors.shift()!; @@ -958,7 +964,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { //#region closeEditor() - closeEditor(editor: EditorInput = this.activeEditor, options?: ICloseEditorOptions): Promise { + closeEditor(editor: EditorInput | undefined = this.activeEditor || undefined, options?: ICloseEditorOptions): Promise { if (!editor) { return Promise.resolve(); } @@ -1015,7 +1021,9 @@ export class EditorGroupView extends Themable implements IEditorGroupView { } // Update model - this._group.closeEditor(editorToClose); + if (editorToClose) { + this._group.closeEditor(editorToClose); + } // Open next active if there are more to show const nextActiveEditor = this._group.activeEditor; @@ -1038,7 +1046,9 @@ export class EditorGroupView extends Themable implements IEditorGroupView { else { // Forward to editor control - this.editorControl.closeEditor(editorToClose); + if (editorToClose) { + this.editorControl.closeEditor(editorToClose); + } // Restore focus to group container as needed unless group gets closed if (restoreFocus && !closeEmptyGroup) { @@ -1077,7 +1087,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { return Promise.resolve(false); // no veto } - const editor = editors.shift(); + const editor = editors.shift()!; // To prevent multiple confirmation dialogs from showing up one after the other // we check if a pending confirmation is currently showing and if so, join that @@ -1376,8 +1386,8 @@ export class EditorGroupView extends Themable implements IEditorGroupView { get maximumWidth(): number { return this.editorControl.maximumWidth; } get maximumHeight(): number { return this.editorControl.maximumHeight; } - private _onDidChange = this._register(new Relay<{ width: number; height: number; }>()); - readonly onDidChange: Event<{ width: number; height: number; }> = this._onDidChange.event; + private _onDidChange = this._register(new Relay<{ width: number; height: number; } | undefined>()); + readonly onDidChange: Event<{ width: number; height: number; } | undefined> = this._onDidChange.event; layout(width: number, height: number): void { this.dimension = new Dimension(width, height); @@ -1422,7 +1432,7 @@ class EditorOpeningEvent implements IEditorOpeningEvent { constructor( private _group: GroupIdentifier, private _editor: EditorInput, - private _options: EditorOptions + private _options: EditorOptions | undefined ) { } @@ -1434,7 +1444,7 @@ class EditorOpeningEvent implements IEditorOpeningEvent { return this._editor; } - get options(): EditorOptions { + get options(): EditorOptions | undefined { return this._options; } diff --git a/src/vs/workbench/browser/parts/editor/editorStatus.ts b/src/vs/workbench/browser/parts/editor/editorStatus.ts index a8542918815..cee38df5af8 100644 --- a/src/vs/workbench/browser/parts/editor/editorStatus.ts +++ b/src/vs/workbench/browser/parts/editor/editorStatus.ts @@ -26,7 +26,6 @@ import { BaseBinaryResourceEditor } from 'vs/workbench/browser/parts/editor/bina import { BinaryResourceDiffEditor } from 'vs/workbench/browser/parts/editor/binaryDiffEditor'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; -import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { SUPPORTED_ENCODINGS, IFileService, FILES_ASSOCIATIONS_CONFIG } from 'vs/platform/files/common/files'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IModeService, ILanguageSelection } from 'vs/editor/common/services/modeService'; @@ -40,7 +39,7 @@ import { ITextFileService } from 'vs/workbench/services/textfile/common/textfile import { ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; import { IConfigurationChangedEvent, IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration'; -import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; +import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { deepClone } from 'vs/base/common/objects'; import { ICodeEditor, isCodeEditor, isDiffEditor, getCodeEditor } from 'vs/editor/browser/editorBrowser'; import { Schemas } from 'vs/base/common/network'; @@ -293,7 +292,7 @@ export class EditorStatus implements IStatusbarItem { @IUntitledEditorService private readonly untitledEditorService: IUntitledEditorService, @IModeService private readonly modeService: IModeService, @ITextFileService private readonly textFileService: ITextFileService, - @IWorkspaceConfigurationService private readonly configurationService: IWorkspaceConfigurationService, + @IConfigurationService private readonly configurationService: IConfigurationService, @INotificationService private readonly notificationService: INotificationService, @IAccessibilityService private readonly accessibilityService: IAccessibilityService ) { @@ -845,7 +844,7 @@ export class ChangeModeAction extends Action { @IModeService private readonly modeService: IModeService, @IModelService private readonly modelService: IModelService, @IEditorService private readonly editorService: IEditorService, - @IWorkspaceConfigurationService private readonly configurationService: IWorkspaceConfigurationService, + @IConfigurationService private readonly configurationService: IConfigurationService, @IQuickInputService private readonly quickInputService: IQuickInputService, @IPreferencesService private readonly preferencesService: IPreferencesService, @IInstantiationService private readonly instantiationService: IInstantiationService, diff --git a/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts index 4e1e729e2b7..e250e63ab42 100644 --- a/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts @@ -16,7 +16,7 @@ import { CLOSE_EDITOR_COMMAND_ID } from 'vs/workbench/browser/parts/editor/edito import { Color } from 'vs/base/common/color'; interface IRenderedEditorLabel { - editor: IEditorInput; + editor?: IEditorInput; pinned: boolean; } @@ -72,8 +72,16 @@ export class NoTabsTitleControl extends TitleControl { this._register(addDisposableListener(this.titleContainer, TouchEventType.Tap, (e: GestureEvent) => this.onTitleClick(e))); // Context Menu - this._register(addDisposableListener(this.titleContainer, EventType.CONTEXT_MENU, (e: Event) => this.onContextMenu(this.group.activeEditor, e, this.titleContainer))); - this._register(addDisposableListener(this.titleContainer, TouchEventType.Contextmenu, (e: Event) => this.onContextMenu(this.group.activeEditor, e, this.titleContainer))); + this._register(addDisposableListener(this.titleContainer, EventType.CONTEXT_MENU, (e: Event) => { + if (this.group.activeEditor) { + this.onContextMenu(this.group.activeEditor, e, this.titleContainer); + } + })); + this._register(addDisposableListener(this.titleContainer, TouchEventType.Contextmenu, (e: Event) => { + if (this.group.activeEditor) { + this.onContextMenu(this.group.activeEditor, e, this.titleContainer); + } + })); } private onTitleLabelClick(e: MouseEvent): void { @@ -95,7 +103,9 @@ export class NoTabsTitleControl extends TitleControl { if (e instanceof MouseEvent && e.button === 1 /* Middle Button */) { EventHelper.stop(e, true /* for https://github.com/Microsoft/vscode/issues/56715 */); - this.group.closeEditor(this.group.activeEditor); + if (this.group.activeEditor) { + this.group.closeEditor(this.group.activeEditor); + } } } @@ -167,7 +177,7 @@ export class NoTabsTitleControl extends TitleControl { if ( !this.activeLabel.editor && this.group.activeEditor || // active editor changed from null => editor this.activeLabel.editor && !this.group.activeEditor || // active editor changed from editor => null - !this.group.isActive(this.activeLabel.editor) // active editor changed from editorA => editorB + (!this.activeLabel.editor || !this.group.isActive(this.activeLabel.editor)) // active editor changed from editorA => editorB ) { fn(); @@ -197,10 +207,10 @@ export class NoTabsTitleControl extends TitleControl { private redraw(): void { const editor = this.group.activeEditor; - const isEditorPinned = this.group.isPinned(this.group.activeEditor); + const isEditorPinned = this.group.activeEditor ? this.group.isPinned(this.group.activeEditor) : false; const isGroupActive = this.accessor.activeGroup === this.group; - this.activeLabel = { editor, pinned: isEditorPinned }; + this.activeLabel = { editor: editor || undefined, pinned: isEditorPinned }; // Update Breadcrumbs if (this.breadcrumbsControl) { @@ -244,7 +254,7 @@ export class NoTabsTitleControl extends TitleControl { title = ''; // dont repeat what is already shown } - this.editorLabel.setResource({ name, description, resource: resource || undefined }, { title, italic: !isEditorPinned, extraClasses: ['no-tabs', 'title-label'] }); + this.editorLabel.setResource({ name, description, resource: resource || undefined }, { title: typeof title === 'string' ? title : undefined, 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 e2bfb8d2cd7..aa38992e096 100644 --- a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts @@ -212,7 +212,7 @@ export class TabsTitleControl extends TitleControl { if (this.editorTransfer.hasData(DraggedEditorIdentifier.prototype)) { isLocalDragAndDrop = true; - const localDraggedEditor = this.editorTransfer.getData(DraggedEditorIdentifier.prototype)[0].identifier; + const localDraggedEditor = this.editorTransfer.getData(DraggedEditorIdentifier.prototype)![0].identifier; if (this.group.id === localDraggedEditor.groupId && this.group.getIndexOfEditor(localDraggedEditor.editor) === this.group.count - 1) { e.dataTransfer!.dropEffect = 'none'; return; @@ -469,7 +469,10 @@ export class TabsTitleControl extends TitleControl { } // Open tabs editor - this.group.openEditor(this.group.getEditor(index)); + const input = this.group.getEditor(index); + if (input) { + this.group.openEditor(input); + } return undefined; }; @@ -477,7 +480,10 @@ export class TabsTitleControl extends TitleControl { const showContextMenu = (e: Event) => { EventHelper.stop(e); - this.onContextMenu(this.group.getEditor(index), e, tab); + const input = this.group.getEditor(index); + if (input) { + this.onContextMenu(input, e, tab); + } }; // Open on Click / Touch @@ -524,7 +530,10 @@ export class TabsTitleControl extends TitleControl { // Run action on Enter/Space if (event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) { handled = true; - this.group.openEditor(this.group.getEditor(index)); + const input = this.group.getEditor(index); + if (input) { + this.group.openEditor(input); + } } // Navigate in editors @@ -569,12 +578,19 @@ export class TabsTitleControl extends TitleControl { disposables.push(addDisposableListener(tab, EventType.CONTEXT_MENU, (e: Event) => { EventHelper.stop(e, true); - this.onContextMenu(this.group.getEditor(index), e, tab); + const input = this.group.getEditor(index); + if (input) { + this.onContextMenu(input, e, tab); + } }, true /* use capture to fix https://github.com/Microsoft/vscode/issues/19145 */)); // Drag support disposables.push(addDisposableListener(tab, EventType.DRAG_START, (e: DragEvent) => { const editor = this.group.getEditor(index); + if (!editor) { + return; + } + this.editorTransfer.setData([new DraggedEditorIdentifier({ editor, groupId: this.group.id })], DraggedEditorIdentifier.prototype); e.dataTransfer!.effectAllowed = 'copyMove'; @@ -608,7 +624,7 @@ export class TabsTitleControl extends TitleControl { if (this.editorTransfer.hasData(DraggedEditorIdentifier.prototype)) { isLocalDragAndDrop = true; - const localDraggedEditor = this.editorTransfer.getData(DraggedEditorIdentifier.prototype)[0].identifier; + const localDraggedEditor = this.editorTransfer.getData(DraggedEditorIdentifier.prototype)![0].identifier; if (localDraggedEditor.editor === this.group.getEditor(index) && localDraggedEditor.groupId === this.group.id) { e.dataTransfer!.dropEffect = 'none'; return; @@ -649,7 +665,7 @@ export class TabsTitleControl extends TitleControl { private isSupportedDropTransfer(e: DragEvent): boolean { if (this.groupTransfer.hasData(DraggedEditorGroupIdentifier.prototype)) { - const group = this.groupTransfer.getData(DraggedEditorGroupIdentifier.prototype)[0]; + const group = this.groupTransfer.getData(DraggedEditorGroupIdentifier.prototype)![0]; if (group.identifier === this.group.id) { return false; // groups cannot be dropped on title area it originates from } @@ -764,7 +780,7 @@ export class TabsTitleControl extends TitleControl { // Remove description if all descriptions are identical if (descriptions.length === 1) { - for (const label of mapDescriptionToDuplicates.get(descriptions[0])) { + for (const label of mapDescriptionToDuplicates.get(descriptions[0]) || []) { label.description = ''; } @@ -774,7 +790,7 @@ export class TabsTitleControl extends TitleControl { // Shorten descriptions const shortenedDescriptions = shorten(descriptions); descriptions.forEach((description, i) => { - for (const label of mapDescriptionToDuplicates.get(description)) { + for (const label of mapDescriptionToDuplicates.get(description) || []) { label.description = shortenedDescriptions[i]; } }); @@ -860,7 +876,7 @@ export class TabsTitleControl extends TitleControl { tabContainer.title = title; // Label - tabLabelWidget.setResource({ 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 }) || undefined }, { title, extraClasses: ['tab-label'], italic: !this.group.isPinned(editor) }); } private redrawEditorActiveAndDirty(isGroupActive: boolean, editor: IEditorInput, tabContainer: HTMLElement, tabLabelWidget: IResourceLabel): void { @@ -963,7 +979,7 @@ export class TabsTitleControl extends TitleControl { layout(dimension: Dimension): void { this.dimension = dimension; - const activeTab = this.getTab(this.group.activeEditor); + const activeTab = this.group.activeEditor ? this.getTab(this.group.activeEditor) : undefined; if (!activeTab || !this.dimension) { return; } @@ -980,7 +996,7 @@ export class TabsTitleControl extends TitleControl { } private doLayout(dimension: Dimension): void { - const activeTab = this.getTab(this.group.activeEditor); + const activeTab = this.group.activeEditor ? this.getTab(this.group.activeEditor) : undefined; if (!activeTab) { return; } @@ -993,8 +1009,8 @@ export class TabsTitleControl extends TitleControl { const visibleContainerWidth = this.tabsContainer.offsetWidth; const totalContainerWidth = this.tabsContainer.scrollWidth; - let activeTabPosX: number | undefined; - let activeTabWidth: number | undefined; + let activeTabPosX: number; + let activeTabWidth: number; if (!this.blockRevealActiveTab) { activeTabPosX = activeTab.offsetLeft; @@ -1015,20 +1031,20 @@ export class TabsTitleControl extends TitleControl { // Reveal the active one const containerScrollPosX = this.tabsScrollbar.getScrollPosition().scrollLeft; - const activeTabFits = activeTabWidth <= visibleContainerWidth; + const activeTabFits = activeTabWidth! <= visibleContainerWidth; // Tab is overflowing to the right: Scroll minimally until the element is fully visible to the right // Note: only try to do this if we actually have enough width to give to show the tab fully! - if (activeTabFits && containerScrollPosX + visibleContainerWidth < activeTabPosX + activeTabWidth) { + if (activeTabFits && containerScrollPosX + visibleContainerWidth < activeTabPosX! + activeTabWidth!) { this.tabsScrollbar.setScrollPosition({ - scrollLeft: containerScrollPosX + ((activeTabPosX + activeTabWidth) /* right corner of tab */ - (containerScrollPosX + visibleContainerWidth) /* right corner of view port */) + scrollLeft: containerScrollPosX + ((activeTabPosX! + activeTabWidth!) /* right corner of tab */ - (containerScrollPosX + visibleContainerWidth) /* right corner of view port */) }); } // Tab is overlflowng to the left or does not fit: Scroll it into view to the left - else if (containerScrollPosX > activeTabPosX || !activeTabFits) { + else if (containerScrollPosX > activeTabPosX! || !activeTabFits) { this.tabsScrollbar.setScrollPosition({ - scrollLeft: activeTabPosX + scrollLeft: activeTabPosX! }); } } @@ -1071,7 +1087,7 @@ export class TabsTitleControl extends TitleControl { // Local Editor DND if (this.editorTransfer.hasData(DraggedEditorIdentifier.prototype)) { - const draggedEditor = this.editorTransfer.getData(DraggedEditorIdentifier.prototype)[0].identifier; + const draggedEditor = this.editorTransfer.getData(DraggedEditorIdentifier.prototype)![0].identifier; const sourceGroup = this.accessor.getGroup(draggedEditor.groupId); // Move editor to target position and index @@ -1090,7 +1106,7 @@ export class TabsTitleControl extends TitleControl { // Local Editor Group DND else if (this.groupTransfer.hasData(DraggedEditorGroupIdentifier.prototype)) { - const sourceGroup = this.accessor.getGroup(this.groupTransfer.getData(DraggedEditorGroupIdentifier.prototype)[0].identifier); + const sourceGroup = this.accessor.getGroup(this.groupTransfer.getData(DraggedEditorGroupIdentifier.prototype)![0].identifier); const mergeGroupOptions: IMergeGroupOptions = { index: targetIndex }; if (!this.isMoveOperation(e, sourceGroup.id)) { @@ -1119,7 +1135,8 @@ export class TabsTitleControl extends TitleControl { dispose(): void { super.dispose(); - this.layoutScheduled = dispose(this.layoutScheduled); + dispose(this.layoutScheduled); + this.layoutScheduled = undefined; } } diff --git a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts index b91fc19dbe6..88f2deeeb0b 100644 --- a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts @@ -61,7 +61,7 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor { return new EditorMemento(this.getId(), key, Object.create(null), limit, editorGroupService); // do not persist in storage as diff editors are never persisted } - getTitle(): string { + getTitle(): string | null { if (this.input) { return this.input.getName(); } @@ -120,6 +120,7 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor { // Readonly flag diffEditor.updateOptions({ readOnly: resolvedDiffEditorModel.isReadonly() }); + return undefined; }, error => { // In case we tried to open a file and the response indicates that this is not a text file, fallback to binary diff. @@ -262,7 +263,7 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor { return super.loadTextEditorViewState(resource) as IDiffEditorViewState; // overridden for text diff editor support } - private saveTextDiffEditorViewState(input: EditorInput): void { + private saveTextDiffEditorViewState(input: EditorInput | null): void { if (!(input instanceof DiffEditorInput)) { return; // only supported for diff editor inputs } @@ -288,11 +289,11 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor { } } - protected retrieveTextEditorViewState(resource: URI): IDiffEditorViewState { + protected retrieveTextEditorViewState(resource: URI): IDiffEditorViewState | null { return this.retrieveTextDiffEditorViewState(resource); // overridden for text diff editor support } - private retrieveTextDiffEditorViewState(resource: URI): IDiffEditorViewState { + private retrieveTextDiffEditorViewState(resource: URI): IDiffEditorViewState | null { const control = this.getControl(); const model = control.getModel(); if (!model || !model.modified || !model.original) { @@ -311,9 +312,9 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor { return control.saveViewState(); } - private toDiffEditorViewStateResource(modelOrInput: IDiffEditorModel | DiffEditorInput): URI { - let original: URI; - let modified: URI; + private toDiffEditorViewStateResource(modelOrInput: IDiffEditorModel | DiffEditorInput): URI | null { + let original: URI | null; + let modified: URI | null; if (modelOrInput instanceof DiffEditorInput) { original = modelOrInput.originalInput.getResource(); diff --git a/src/vs/workbench/browser/parts/editor/titleControl.ts b/src/vs/workbench/browser/parts/editor/titleControl.ts index 0a2b6fb57c7..fb31f528691 100644 --- a/src/vs/workbench/browser/parts/editor/titleControl.ts +++ b/src/vs/workbench/browser/parts/editor/titleControl.ts @@ -264,12 +264,14 @@ export abstract class TitleControl extends Themable { } // Drag Image - let label = this.group.activeEditor.getName(); - if (this.accessor.partOptions.showTabs && this.group.count > 1) { - label = localize('draggedEditorGroup', "{0} (+{1})", label, this.group.count - 1); - } + if (this.group.activeEditor) { + let label = this.group.activeEditor.getName(); + if (this.accessor.partOptions.showTabs && this.group.count > 1) { + label = localize('draggedEditorGroup', "{0} (+{1})", label, this.group.count - 1); + } - applyDragImage(e, label, 'monaco-editor-group-drag-image'); + applyDragImage(e, label, 'monaco-editor-group-drag-image'); + } })); // Drag end @@ -304,7 +306,7 @@ export abstract class TitleControl extends Themable { onHide: () => { // restore previous context - this.resourceContext.set(currentContext); + this.resourceContext.set(currentContext || null); // restore focus to active group this.accessor.activeGroup.focus(); @@ -355,7 +357,8 @@ export abstract class TitleControl extends Themable { } dispose(): void { - this.breadcrumbsControl = dispose(this.breadcrumbsControl); + dispose(this.breadcrumbsControl); + this.breadcrumbsControl = undefined; this.editorToolBarMenuDisposables = dispose(this.editorToolBarMenuDisposables); super.dispose(); diff --git a/src/vs/workbench/browser/parts/panel/panelActions.ts b/src/vs/workbench/browser/parts/panel/panelActions.ts index adb1d3b18d5..0ec0fe83d72 100644 --- a/src/vs/workbench/browser/parts/panel/panelActions.ts +++ b/src/vs/workbench/browser/parts/panel/panelActions.ts @@ -15,6 +15,7 @@ import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IPartService, Parts, Position } from 'vs/workbench/services/part/common/partService'; import { ActivityAction } from 'vs/workbench/browser/parts/compositeBarActions'; import { IActivity } from 'vs/workbench/common/activity'; +import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; export class ClosePanelAction extends Action { @@ -100,6 +101,7 @@ export class TogglePanelPositionAction extends Action { id: string, label: string, @IPartService private readonly partService: IPartService, + @IEditorGroupsService editorGroupsService: IEditorGroupsService ) { super(id, label, partService.getPanelPosition() === Position.RIGHT ? 'move-panel-to-bottom' : 'move-panel-to-right'); @@ -111,7 +113,7 @@ export class TogglePanelPositionAction extends Action { this.label = positionRight ? TogglePanelPositionAction.MOVE_TO_BOTTOM_LABEL : TogglePanelPositionAction.MOVE_TO_RIGHT_LABEL; }; - this.toDispose.push(partService.onEditorLayout(() => setClassAndLabel())); + this.toDispose.push(editorGroupsService.onDidLayout(() => setClassAndLabel())); setClassAndLabel(); } @@ -143,13 +145,14 @@ export class ToggleMaximizedPanelAction extends Action { constructor( id: string, label: string, - @IPartService private readonly partService: IPartService + @IPartService private readonly partService: IPartService, + @IEditorGroupsService editorGroupsService: IEditorGroupsService ) { super(id, label, partService.isPanelMaximized() ? 'minimize-panel-action' : 'maximize-panel-action'); this.toDispose = []; - this.toDispose.push(partService.onEditorLayout(() => { + this.toDispose.push(editorGroupsService.onDidLayout(() => { const maximized = this.partService.isPanelMaximized(); this.class = maximized ? 'minimize-panel-action' : 'maximize-panel-action'; this.label = maximized ? ToggleMaximizedPanelAction.RESTORE_LABEL : ToggleMaximizedPanelAction.MAXIMIZE_LABEL; @@ -249,7 +252,7 @@ export class NextPanelViewAction extends SwitchPanelViewAction { super(id, name, panelService); } - public run(): Promise { + run(): Promise { return super.run(1); } } diff --git a/src/vs/workbench/browser/parts/panel/panelPart.ts b/src/vs/workbench/browser/parts/panel/panelPart.ts index d6bd16824cc..04dbc490a5c 100644 --- a/src/vs/workbench/browser/parts/panel/panelPart.ts +++ b/src/vs/workbench/browser/parts/panel/panelPart.ts @@ -8,7 +8,7 @@ import { IAction } from 'vs/base/common/actions'; import { Event, Emitter } from 'vs/base/common/event'; import { Registry } from 'vs/platform/registry/common/platform'; import { ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar'; -import { IPanel } from 'vs/workbench/common/panel'; +import { IPanel, ActivePanelContext, PanelFocusContext } from 'vs/workbench/common/panel'; import { CompositePart, ICompositeTitleLabel } from 'vs/workbench/browser/parts/compositePart'; import { Panel, PanelRegistry, Extensions as PanelExtensions, PanelDescriptor } from 'vs/workbench/browser/panel'; import { IPanelService, IPanelIdentifier } from 'vs/workbench/services/panel/common/panelService'; @@ -29,15 +29,12 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { Dimension, trackFocus } from 'vs/base/browser/dom'; import { localize } from 'vs/nls'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { isUndefinedOrNull } from 'vs/base/common/types'; import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { ISerializableView } from 'vs/base/browser/ui/grid/grid'; import { LayoutPriority } from 'vs/base/browser/ui/grid/gridview'; -export const ActivePanelContext = new RawContextKey('activePanel', ''); -export const PanelFocusContext = new RawContextKey('panelFocus', false); - interface ICachedPanel { id: string; pinned: boolean; @@ -66,11 +63,16 @@ export class PanelPart extends CompositePart implements IPanelService, IS private _onDidChange = this._register(new Emitter<{ width: number; height: number; }>()); get onDidChange(): Event<{ width: number, height: number }> { return this._onDidChange.event; } + get onDidPanelOpen(): Event<{ panel: IPanel, focus: boolean }> { return Event.map(this.onDidCompositeOpen.event, compositeOpen => ({ panel: compositeOpen.composite, focus: compositeOpen.focus })); } + get onDidPanelClose(): Event { return this.onDidCompositeClose.event; } + private activePanelContextKey: IContextKey; private panelFocusContextKey: IContextKey; - private blockOpeningPanel: boolean; + private compositeBar: CompositeBar; private compositeActions: { [compositeId: string]: { activityAction: PanelActivityAction, pinnedAction: ToggleCompositePinnedAction } } = Object.create(null); + + private blockOpeningPanel: boolean; private dimension: Dimension; constructor( @@ -142,25 +144,13 @@ export class PanelPart extends CompositePart implements IPanelService, IS this.registerListeners(); } - create(parent: HTMLElement): void { - this.element = parent; - - super.create(parent); - - const focusTracker = trackFocus(parent); - - focusTracker.onDidFocus(() => { - this.panelFocusContextKey.set(true); - }); - focusTracker.onDidBlur(() => { - this.panelFocusContextKey.set(false); - }); - } - private registerListeners(): void { - this._register(this.onDidPanelOpen(({ panel }) => this._onDidPanelOpen(panel))); - this._register(this.onDidPanelClose(this._onDidPanelClose, this)); + // Panel open/close + this._register(this.onDidPanelOpen(({ panel }) => this.onPanelOpen(panel))); + this._register(this.onDidPanelClose(this.onPanelClose, this)); + + // Panel register/deregister this._register(this.registry.onDidRegister(panelDescriptor => this.compositeBar.addComposite(panelDescriptor))); this._register(this.registry.onDidDeregister(panelDescriptor => { this.compositeBar.hideComposite(panelDescriptor.id); @@ -176,17 +166,18 @@ export class PanelPart extends CompositePart implements IPanelService, IS // Deactivate panel action on close this._register(this.onDidPanelClose(panel => this.compositeBar.deactivateComposite(panel.getId()))); + // State this.lifecycleService.when(LifecyclePhase.Eventually).then(() => { this._register(this.compositeBar.onDidChange(() => this.saveCachedPanels())); this._register(this.storageService.onDidChangeStorage(e => this.onDidStorageChange(e))); }); } - private _onDidPanelOpen(panel: IPanel): void { + private onPanelOpen(panel: IPanel): void { this.activePanelContextKey.set(panel.getId()); } - private _onDidPanelClose(panel: IPanel): void { + private onPanelClose(panel: IPanel): void { const id = panel.getId(); if (this.activePanelContextKey.get() === id) { @@ -194,12 +185,14 @@ export class PanelPart extends CompositePart implements IPanelService, IS } } - get onDidPanelOpen(): Event<{ panel: IPanel, focus: boolean }> { - return Event.map(this.onDidCompositeOpen.event, compositeOpen => ({ panel: compositeOpen.composite, focus: compositeOpen.focus })); - } + create(parent: HTMLElement): void { + this.element = parent; - get onDidPanelClose(): Event { - return this.onDidCompositeClose.event; + super.create(parent); + + const focusTracker = this._register(trackFocus(parent)); + this._register(focusTracker.onDidFocus(() => this.panelFocusContextKey.set(true))); + this._register(focusTracker.onDidBlur(() => this.panelFocusContextKey.set(false))); } updateStyles(): void { @@ -303,8 +296,7 @@ export class PanelPart extends CompositePart implements IPanelService, IS const { width, height } = dim1 instanceof Dimension ? dim1 : { width: dim1, height: dim2 }; if (this.partService.getPanelPosition() === Position.RIGHT) { - // Take into account the 1px border when layouting - this.dimension = new Dimension(width - 1, height!); + this.dimension = new Dimension(width - 1, height!); // Take into account the 1px border when layouting } else { this.dimension = new Dimension(width, height!); } @@ -321,9 +313,9 @@ export class PanelPart extends CompositePart implements IPanelService, IS if (this.dimension) { let availableWidth = this.dimension.width - 40; // take padding into account if (this.toolBar) { - // adjust height for global actions showing - availableWidth = Math.max(PanelPart.MIN_COMPOSITE_BAR_WIDTH, availableWidth - this.getToolbarWidth()); + availableWidth = Math.max(PanelPart.MIN_COMPOSITE_BAR_WIDTH, availableWidth - this.getToolbarWidth()); // adjust height for global actions showing } + this.compositeBar.layout(new Dimension(availableWidth, this.dimension.height)); } } @@ -337,6 +329,7 @@ export class PanelPart extends CompositePart implements IPanelService, IS }; this.compositeActions[compositeId] = compositeActions; } + return compositeActions; } @@ -348,8 +341,10 @@ export class PanelPart extends CompositePart implements IPanelService, IS compositeActions.pinnedAction.dispose(); delete this.compositeActions[compositeId]; } + return true; } + return false; } @@ -358,6 +353,7 @@ export class PanelPart extends CompositePart implements IPanelService, IS if (!activePanel) { return 0; } + return this.toolBar.getItemsWidth(); } @@ -396,22 +392,26 @@ export class PanelPart extends CompositePart implements IPanelService, IS private saveCachedPanels(): void { const state: ICachedPanel[] = []; + const compositeItems = this.compositeBar.getCompositeBarItems(); for (const compositeItem of compositeItems) { state.push({ id: compositeItem.id, pinned: compositeItem.pinned, order: compositeItem.order, visible: compositeItem.visible }); } + this.cachedPanelsValue = JSON.stringify(state); } private getCachedPanels(): ICachedPanel[] { - const storedStates = >JSON.parse(this.cachedPanelsValue); const registeredPanels = this.getPanels(); + + const storedStates = >JSON.parse(this.cachedPanelsValue); const cachedPanels = storedStates.map(c => { const serialized: ICachedPanel = typeof c === 'string' /* migration from pinned states to composites states */ ? { id: c, pinned: true, order: undefined, visible: true } : c; const registered = registeredPanels.some(p => p.id === serialized.id); serialized.visible = registered ? isUndefinedOrNull(serialized.visible) ? true : serialized.visible : false; return serialized; }); + return cachedPanels; } @@ -420,6 +420,7 @@ export class PanelPart extends CompositePart implements IPanelService, IS if (!this._cachedPanelsValue) { this._cachedPanelsValue = this.getStoredCachedPanelsValue(); } + return this._cachedPanelsValue; } diff --git a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts index 653dde866ac..f17cef29eb2 100644 --- a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts +++ b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts @@ -13,7 +13,7 @@ import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/wor import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IPartService, Parts, Position as SideBarPosition } from 'vs/workbench/services/part/common/partService'; -import { IViewlet } from 'vs/workbench/common/viewlet'; +import { IViewlet, SidebarFocusContext, ActiveViewletContext } from 'vs/workbench/common/viewlet'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -27,16 +27,12 @@ import { SIDE_BAR_TITLE_FOREGROUND, SIDE_BAR_BACKGROUND, SIDE_BAR_FOREGROUND, SI import { INotificationService } from 'vs/platform/notification/common/notification'; import { EventType, addDisposableListener, trackFocus, Dimension } from 'vs/base/browser/dom'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; -import { RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { ISerializableView } from 'vs/base/browser/ui/grid/grid'; import { LayoutPriority } from 'vs/base/browser/ui/grid/gridview'; -export const SidebarVisibleContext = new RawContextKey('sidebarVisible', false); -export const SidebarFocusContext = new RawContextKey('sideBarFocus', false); -export const ActiveViewletContext = new RawContextKey('activeViewlet', ''); - export class SidebarPart extends CompositePart implements ISerializableView, IViewletService { _serviceBrand: any; @@ -54,11 +50,18 @@ export class SidebarPart extends CompositePart implements ISerializable private _onDidChange = this._register(new Emitter<{ width: number; height: number; }>()); get onDidChange(): Event<{ width: number, height: number }> { return this._onDidChange.event; } + get onDidViewletRegister(): Event { return >this.viewletRegistry.onDidRegister; } + + private _onDidViewletDeregister = this._register(new Emitter()); + get onDidViewletDeregister(): Event { return this._onDidViewletDeregister.event; } + + get onDidViewletOpen(): Event { return Event.map(this.onDidCompositeOpen.event, compositeEvent => compositeEvent.composite); } + get onDidViewletClose(): Event { return this.onDidCompositeClose.event as Event; } + private viewletRegistry: ViewletRegistry; private sideBarFocusContextKey: IContextKey; private activeViewletContextKey: IContextKey; private blockOpeningViewlet: boolean; - private _onDidViewletDeregister = this._register(new Emitter()); constructor( id: string, @@ -92,52 +95,47 @@ export class SidebarPart extends CompositePart implements ISerializable { hasTitle: true, borderWidth: () => (this.getColor(SIDE_BAR_BORDER) || this.getColor(contrastBorder)) ? 1 : 0 } ); - this.sideBarFocusContextKey = SidebarFocusContext.bindTo(contextKeyService); this.viewletRegistry = Registry.as(ViewletExtensions.Viewlets); + this.sideBarFocusContextKey = SidebarFocusContext.bindTo(contextKeyService); this.activeViewletContextKey = ActiveViewletContext.bindTo(contextKeyService); + this.registerListeners(); + } + + private registerListeners(): void { + + // Viewlet open this._register(this.onDidViewletOpen(viewlet => { this.activeViewletContextKey.set(viewlet.getId()); })); + + // Viewlet close this._register(this.onDidViewletClose(viewlet => { if (this.activeViewletContextKey.get() === viewlet.getId()) { this.activeViewletContextKey.reset(); } })); + + // Viewlet deregister this._register(this.registry.onDidDeregister(async (viewletDescriptor: ViewletDescriptor) => { if (this.getActiveViewlet().getId() === viewletDescriptor.id) { await this.openViewlet(this.getDefaultViewletId()); } + this.removeComposite(viewletDescriptor.id); this._onDidViewletDeregister.fire(viewletDescriptor); })); } - get onDidViewletRegister(): Event { return >this.viewletRegistry.onDidRegister; } - get onDidViewletDeregister(): Event { return this._onDidViewletDeregister.event; } - - get onDidViewletOpen(): Event { - return Event.map(this.onDidCompositeOpen.event, compositeEvent => compositeEvent.composite); - } - - get onDidViewletClose(): Event { - return this.onDidCompositeClose.event as Event; - } - create(parent: HTMLElement): void { this.element = parent; super.create(parent); - const focusTracker = trackFocus(parent); - - focusTracker.onDidFocus(() => { - this.sideBarFocusContextKey.set(true); - }); - focusTracker.onDidBlur(() => { - this.sideBarFocusContextKey.set(false); - }); + const focusTracker = this._register(trackFocus(parent)); + this._register(focusTracker.onDidFocus(() => this.sideBarFocusContextKey.set(true))); + this._register(focusTracker.onDidBlur(() => this.sideBarFocusContextKey.set(false))); } createTitleArea(parent: HTMLElement): HTMLElement { @@ -201,15 +199,17 @@ export class SidebarPart extends CompositePart implements ISerializable this.hideActiveComposite(); } - openViewlet(id: string, focus?: boolean): Promise { - if (this.getViewlet(id)) { + openViewlet(id: string | undefined, focus?: boolean): Promise { + if (typeof id === 'string' && this.getViewlet(id)) { return Promise.resolve(this.doOpenViewlet(id, focus)); } + return this.extensionService.whenInstalledExtensionsRegistered() .then(() => { - if (this.getViewlet(id)) { + if (typeof id === 'string' && this.getViewlet(id)) { return this.doOpenViewlet(id, focus); } + return null; }); } diff --git a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts index 254ae288772..0399029f2e6 100644 --- a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts @@ -58,7 +58,7 @@ export class MenubarControl extends Disposable { 'Terminal': IMenu; 'Window'?: IMenu; 'Help': IMenu; - [index: string]: IMenu; + [index: string]: IMenu | undefined; }; private topLevelTitles = { @@ -121,8 +121,11 @@ export class MenubarControl extends Disposable { this.menuUpdater = this._register(new RunOnceScheduler(() => this.doUpdateMenubar(false), 200)); if (isMacintosh || this.currentTitlebarStyleSetting !== 'custom') { - for (let topLevelMenuName of Object.keys(this.topLevelMenus)) { - this._register(this.topLevelMenus[topLevelMenuName].onDidChange(() => this.updateMenubar())); + for (const topLevelMenuName of Object.keys(this.topLevelMenus)) { + const menu = this.topLevelMenus[topLevelMenuName]; + if (menu) { + this._register(menu.onDidChange(() => this.updateMenubar())); + } } this.doUpdateMenubar(true); @@ -441,7 +444,7 @@ export class MenubarControl extends Disposable { return new Action('update.checking', nls.localize('checkingForUpdates', "Checking For Updates..."), undefined, false); case StateType.AvailableForDownload: - return new Action('update.downloadNow', nls.localize({ key: 'download now', comment: ['&& denotes a mnemonic'] }, "D&&ownload Now"), null, true, () => + return new Action('update.downloadNow', nls.localize({ key: 'download now', comment: ['&& denotes a mnemonic'] }, "D&&ownload Now"), undefined, true, () => this.updateService.downloadUpdate()); case StateType.Downloading: @@ -533,9 +536,9 @@ export class MenubarControl extends Disposable { target.pop(); }; - for (let title of Object.keys(this.topLevelMenus)) { + for (const title of Object.keys(this.topLevelMenus)) { const menu = this.topLevelMenus[title]; - if (firstTime) { + if (firstTime && menu) { this._register(menu.onDidChange(() => { const actions = []; updateActions(menu, actions); @@ -544,7 +547,9 @@ export class MenubarControl extends Disposable { } const actions = []; - updateActions(menu, actions); + if (menu) { + updateActions(menu, actions); + } if (!firstTime) { this.menubar.updateMenu({ actions: actions, label: mnemonicMenuLabel(this.topLevelTitles[title]) }); @@ -554,7 +559,7 @@ export class MenubarControl extends Disposable { } } - private getMenubarKeybinding(id: string): IMenubarKeybinding { + private getMenubarKeybinding(id: string): IMenubarKeybinding | undefined { const binding = this.keybindingService.lookupKeybinding(id); if (!binding) { return undefined; @@ -563,19 +568,19 @@ export class MenubarControl extends Disposable { // first try to resolve a native accelerator const electronAccelerator = binding.getElectronAccelerator(); if (electronAccelerator) { - return { label: electronAccelerator, userSettingsLabel: binding.getUserSettingsLabel() }; + return { label: electronAccelerator, userSettingsLabel: binding.getUserSettingsLabel() || undefined }; } // we need this fallback to support keybindings that cannot show in electron menus (e.g. chords) const acceleratorLabel = binding.getLabel(); if (acceleratorLabel) { - return { label: acceleratorLabel, isNative: false, userSettingsLabel: binding.getUserSettingsLabel() }; + return { label: acceleratorLabel, isNative: false, userSettingsLabel: binding.getUserSettingsLabel() || undefined }; } - return null; + return undefined; } - private populateMenuItems(menu: IMenu, menuToPopulate: IMenubarMenu, keybindings: { [id: string]: IMenubarKeybinding }) { + private populateMenuItems(menu: IMenu, menuToPopulate: IMenubarMenu, keybindings: { [id: string]: IMenubarKeybinding | undefined }) { let groups = menu.getActions(); for (let group of groups) { const [, actions] = group; @@ -643,15 +648,17 @@ export class MenubarControl extends Disposable { } menubarData.keybindings = this.getAdditionalKeybindings(); - for (let topLevelMenuName of Object.keys(this.topLevelMenus)) { + for (const topLevelMenuName of Object.keys(this.topLevelMenus)) { const menu = this.topLevelMenus[topLevelMenuName]; - let menubarMenu: IMenubarMenu = { items: [] }; - this.populateMenuItems(menu, menubarMenu, menubarData.keybindings); - if (menubarMenu.items.length === 0) { - // Menus are incomplete - return false; + if (menu) { + const menubarMenu: IMenubarMenu = { items: [] }; + this.populateMenuItems(menu, menubarMenu, menubarData.keybindings); + if (menubarMenu.items.length === 0) { + // Menus are incomplete + return false; + } + menubarData.menus[topLevelMenuName] = menubarMenu; } - menubarData.menus[topLevelMenuName] = menubarMenu; } return true; diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index 3ab509fad99..d4fa3b061d8 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -52,9 +52,12 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi get minimumHeight(): number { return isMacintosh ? 22 / getZoomFactor() : (30 / (this.configurationService.getValue('window.menuBarVisibility') === 'hidden' ? getZoomFactor() : 1)); } get maximumHeight(): number { return isMacintosh ? 22 / getZoomFactor() : (30 / (this.configurationService.getValue('window.menuBarVisibility') === 'hidden' ? getZoomFactor() : 1)); } - private _onDidChange = this._register(new Emitter<{ width: number; height: number; }>()); + private _onDidChange = this._register(new Emitter<{ width: number, height: number }>()); get onDidChange(): Event<{ width: number, height: number }> { return this._onDidChange.event; } + private _onMenubarVisibilityChange = this._register(new Emitter()); + get onMenubarVisibilityChange(): Event { return this._onMenubarVisibilityChange.event; } + _serviceBrand: any; private title: HTMLElement; @@ -141,6 +144,8 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi } this.adjustTitleMarginToCenter(); + + this._onMenubarVisibilityChange.fire(visible); } } @@ -154,10 +159,6 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi } } - onMenubarVisibilityChange(): Event { - return this.menubarPart.onVisibilityChange; - } - private onActiveEditorChange(): void { // Dispose old listeners @@ -179,7 +180,7 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi } private updateRepresentedFilename(): void { - const file = toResource(this.editorService.activeEditor, { supportSideBySide: true, filter: 'file' }); + const file = toResource(this.editorService.activeEditor || null, { supportSideBySide: true, filter: 'file' }); const path = file ? file.fsPath : ''; // Apply to window @@ -282,7 +283,7 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi // Compute folder resource // Single Root Workspace: always the root single workspace in this case // Otherwise: root folder of the currently active file if any - const folder = this.contextService.getWorkbenchState() === WorkbenchState.FOLDER ? workspace.folders[0] : this.contextService.getWorkspaceFolder(toResource(editor, { supportSideBySide: true })); + const folder = this.contextService.getWorkbenchState() === WorkbenchState.FOLDER ? workspace.folders[0] : this.contextService.getWorkspaceFolder(toResource(editor || null, { supportSideBySide: true })!); // Variables const activeEditorShort = editor ? editor.getTitle(Verbosity.SHORT) : ''; @@ -472,7 +473,7 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi const titleBackground = this.getColor(this.isInactive ? TITLE_BAR_INACTIVE_BACKGROUND : TITLE_BAR_ACTIVE_BACKGROUND); this.element.style.backgroundColor = titleBackground; - if (Color.fromHex(titleBackground).isLighter()) { + if (titleBackground && Color.fromHex(titleBackground).isLighter()) { addClass(this.element, 'light'); } else { removeClass(this.element, 'light'); @@ -494,8 +495,7 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi const setting = this.configurationService.getValue('window.doubleClickIconToClose'); if (setting) { this.appIcon.style['-webkit-app-region'] = 'no-drag'; - } - else { + } else { this.appIcon.style['-webkit-app-region'] = 'drag'; } } @@ -582,7 +582,7 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi runAtThisOrScheduleAtNextAnimationFrame(() => this.adjustTitleMarginToCenter()); if (this.menubarPart) { - const menubarDimension = new Dimension(undefined, dimension.height); + const menubarDimension = new Dimension(0, dimension.height); this.menubarPart.layout(menubarDimension); } } @@ -597,7 +597,7 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi return super.layout(dim1); } - const dimensions = new Dimension(dim1, dim2); + const dimensions = new Dimension(dim1, dim2!); this.updateLayout(dimensions); super.layout(dimensions); @@ -617,7 +617,7 @@ class ShowItemInFolderAction extends Action { } run(): Promise { - return this.windowsService.showItemInFolder(this.path); + return this.windowsService.showItemInFolder(URI.file(this.path)); } } diff --git a/src/vs/workbench/browser/parts/views/customView.ts b/src/vs/workbench/browser/parts/views/customView.ts index 6dfc083014d..4450da30eb0 100644 --- a/src/vs/workbench/browser/parts/views/customView.ts +++ b/src/vs/workbench/browser/parts/views/customView.ts @@ -190,7 +190,7 @@ export class CustomTreeView extends Disposable implements ITreeView { private menus: TitleMenus; private markdownRenderer: MarkdownRenderer; - private markdownResult: IMarkdownRenderResult; + private markdownResult: IMarkdownRenderResult | null; private readonly _onDidExpandItem: Emitter = this._register(new Emitter()); readonly onDidExpandItem: Event = this._onDidExpandItem.event; @@ -242,15 +242,15 @@ export class CustomTreeView extends Disposable implements ITreeView { this.create(); } - private _dataProvider: ITreeViewDataProvider; - get dataProvider(): ITreeViewDataProvider { + private _dataProvider: ITreeViewDataProvider | null; + get dataProvider(): ITreeViewDataProvider | null { return this._dataProvider; } - set dataProvider(dataProvider: ITreeViewDataProvider) { + set dataProvider(dataProvider: ITreeViewDataProvider | null) { if (dataProvider) { this._dataProvider = new class implements ITreeViewDataProvider { - getChildren(node?: ITreeItem): Promise { + getChildren(node: ITreeItem): Promise { if (node && node.children) { return Promise.resolve(node.children); } @@ -377,7 +377,7 @@ export class CustomTreeView extends Disposable implements ITreeView { } private createTree() { - const actionItemProvider = (action: IAction) => action instanceof MenuItemAction ? this.instantiationService.createInstance(ContextAwareMenuItemActionItem, action) : undefined; + const actionItemProvider = (action: IAction) => action instanceof MenuItemAction ? this.instantiationService.createInstance(ContextAwareMenuItemActionItem, action) : null; 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, (task: Promise) => this.progressService.withProgress({ location: this.viewContainer.id }, () => task)); @@ -461,7 +461,7 @@ export class CustomTreeView extends Disposable implements ITreeView { this.elementsToRefresh = []; } for (const element of elements) { - element.children = null; // reset children + element.children = undefined; // reset children } if (this.isVisible) { return this.doRefresh(elements); @@ -676,7 +676,7 @@ class TreeRenderer implements IRenderer { renderElement(tree: ITree, node: ITreeItem, templateId: string, templateData: ITreeExplorerTemplateData): void { const resource = node.resourceUri ? URI.revive(node.resourceUri) : null; - const treeItemLabel: ITreeItemLabel = node.label ? node.label : resource ? { label: basename(resource) } : undefined; + const treeItemLabel: ITreeItemLabel | undefined = node.label ? node.label : resource ? { label: basename(resource) } : undefined; const description = isString(node.description) ? node.description : resource && node.description === true ? this.labelService.getUriLabel(dirname(resource), { relative: true }) : undefined; const label = treeItemLabel ? treeItemLabel.label : undefined; const matches = treeItemLabel && treeItemLabel.highlights ? treeItemLabel.highlights.map(([start, end]) => ({ start, end })) : undefined; @@ -872,7 +872,7 @@ class TreeMenus extends Disposable implements IDisposable { return this.getActions(MenuId.ViewItemContext, { key: 'viewItem', value: element.contextValue }).secondary; } - private getActions(menuId: MenuId, context: { key: string, value: string }): { primary: IAction[]; secondary: IAction[]; } { + private getActions(menuId: MenuId, context: { key: string, value?: string }): { primary: IAction[]; secondary: IAction[]; } { const contextKeyService = this.contextKeyService.createScoped(); contextKeyService.createKey('view', this.id); contextKeyService.createKey(context.key, context.value); diff --git a/src/vs/workbench/browser/parts/views/viewsViewlet.ts b/src/vs/workbench/browser/parts/views/viewsViewlet.ts index 4a63b4a810b..804e934a8ce 100644 --- a/src/vs/workbench/browser/parts/views/viewsViewlet.ts +++ b/src/vs/workbench/browser/parts/views/viewsViewlet.ts @@ -67,7 +67,7 @@ export abstract class ViewContainerViewlet extends PanelViewlet implements IView this.viewletState = this.getMemento(StorageScope.WORKSPACE); this.visibleViewsStorageId = `${id}.numberOfVisibleViews`; - this.visibleViewsCountFromCache = this.storageService.getInteger(this.visibleViewsStorageId, StorageScope.WORKSPACE, 1); + this.visibleViewsCountFromCache = this.storageService.getNumber(this.visibleViewsStorageId, StorageScope.WORKSPACE, 1); this._register(toDisposable(() => this.viewDisposables = dispose(this.viewDisposables))); } @@ -178,7 +178,7 @@ export abstract class ViewContainerViewlet extends PanelViewlet implements IView } protected createView(viewDescriptor: IViewDescriptor, options: IViewletViewOptions): ViewletPanel { - return this.instantiationService.createInstance(viewDescriptor.ctor, options) as ViewletPanel; + return (this.instantiationService as any).createInstance(viewDescriptor.ctorDescriptor.ctor, ...(viewDescriptor.ctorDescriptor.arguments || []), options) as ViewletPanel; } protected getView(id: string): ViewletPanel { diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index 1eb763e0023..e1f55c10fb7 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -243,6 +243,22 @@ import { isMacintosh } from 'vs/base/common/platform'; } }); + // Window + registry.registerConfiguration({ + 'id': 'window', + 'order': 8, + 'title': nls.localize('windowConfigurationTitle', "Window"), + 'type': 'object', + 'properties': { + 'window.title': { + 'type': 'string', + 'default': isMacintosh ? '${activeEditorShort}${separator}${rootName}' : '${dirty}${activeEditorShort}${separator}${rootName}${separator}${appName}', + 'markdownDescription': nls.localize({ comment: ['This is the description for a setting. Values surrounded by parenthesis are not to be translated.'], key: 'title' }, + "Controls the window title based on the active editor. Variables are substituted based on the context:\n- `\${activeEditorShort}`: the file name (e.g. myFile.txt).\n- `\${activeEditorMedium}`: the path of the file relative to the workspace folder (e.g. myFolder/myFileFolder/myFile.txt).\n- `\${activeEditorLong}`: the full path of the file (e.g. /Users/Development/myFolder/myFileFolder/myFile.txt).\n- `\${activeFolderShort}`: the name of the folder the file is contained in (e.g. myFileFolder).\n- `\${activeFolderMedium}`: the path of the folder the file is contained in, relative to the workspace folder (e.g. myFolder/myFileFolder).\n- `\${activeFolderLong}`: the full path of the folder the file is contained in (e.g. /Users/Development/myFolder/myFileFolder).\n- `\${folderName}`: name of the workspace folder the file is contained in (e.g. myFolder).\n- `\${folderPath}`: file path of the workspace folder the file is contained in (e.g. /Users/Development/myFolder).\n- `\${rootName}`: name of the workspace (e.g. myFolder or myWorkspace).\n- `\${rootPath}`: file path of the workspace (e.g. /Users/Development/myWorkspace).\n- `\${appName}`: e.g. VS Code.\n- `\${dirty}`: a dirty indicator if the active editor is dirty.\n- `\${separator}`: a conditional separator (\" - \") that only shows when surrounded by variables with values or static text.") + } + } + }); + // Zen Mode registry.registerConfiguration({ 'id': 'zenMode', diff --git a/src/vs/workbench/common/contributions.ts b/src/vs/workbench/common/contributions.ts index 01a8631bf8f..e60c8dbc795 100644 --- a/src/vs/workbench/common/contributions.ts +++ b/src/vs/workbench/common/contributions.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IInstantiationService, IConstructorSignature0 } from 'vs/platform/instantiation/common/instantiation'; +import { IInstantiationService, IConstructorSignature0, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; import { runWhenIdle, IdleDeadline } from 'vs/base/common/async'; @@ -34,10 +34,9 @@ export interface IWorkbenchContributionsRegistry { /** * Starts the registry by providing the required services. */ - start(instantiationService: IInstantiationService, lifecycleService: ILifecycleService): void; + start(accessor: ServicesAccessor): void; } - class WorkbenchContributionsRegistry implements IWorkbenchContributionsRegistry { private instantiationService: IInstantiationService; private lifecycleService: ILifecycleService; @@ -63,12 +62,12 @@ class WorkbenchContributionsRegistry implements IWorkbenchContributionsRegistry } } - start(instantiationService: IInstantiationService, lifecycleService: ILifecycleService): void { - this.instantiationService = instantiationService; - this.lifecycleService = lifecycleService; + start(accessor: ServicesAccessor): void { + this.instantiationService = accessor.get(IInstantiationService); + this.lifecycleService = accessor.get(ILifecycleService); [LifecyclePhase.Starting, LifecyclePhase.Ready, LifecyclePhase.Restored, LifecyclePhase.Eventually].forEach(phase => { - this.instantiateByPhase(instantiationService, lifecycleService, phase); + this.instantiateByPhase(this.instantiationService, this.lifecycleService, phase); }); } diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index 027f4d17d21..fb00992d23e 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -10,7 +10,7 @@ import { URI } from 'vs/base/common/uri'; import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; import { IEditor as ICodeEditor, IEditorViewState, ScrollType, IDiffEditor } from 'vs/editor/common/editorCommon'; import { IEditorModel, IEditorOptions, ITextEditorOptions, IBaseResourceInput } from 'vs/platform/editor/common/editor'; -import { IInstantiationService, IConstructorSignature0 } from 'vs/platform/instantiation/common/instantiation'; +import { IInstantiationService, IConstructorSignature0, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { RawContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { Registry } from 'vs/platform/registry/common/platform'; import { ITextModel } from 'vs/editor/common/model'; @@ -175,7 +175,10 @@ export interface IEditorInputFactoryRegistry { */ getEditorInputFactory(editorInputId: string): IEditorInputFactory; - setInstantiationService(service: IInstantiationService): void; + /** + * Starts the registry by providing the required services. + */ + start(accessor: ServicesAccessor): void; } export interface IEditorInputFactory { @@ -530,7 +533,12 @@ export class SideBySideEditorInput extends EditorInput { static readonly ID: string = 'workbench.editorinputs.sidebysideEditorInput'; - constructor(private name: string, private description: string, private _details: EditorInput, private _master: EditorInput) { + constructor( + private readonly name: string, + private readonly description: string | null, + private readonly _details: EditorInput, + private readonly _master: EditorInput + ) { super(); this.registerListeners(); @@ -599,7 +607,7 @@ export class SideBySideEditorInput extends EditorInput { return this.name; } - getDescription(): string { + getDescription(): string | null { return this.description; } @@ -678,7 +686,7 @@ export class EditorOptions implements IEditorOptions { /** * Helper to create EditorOptions inline. */ - static create(settings: IEditorOptions): EditorOptions | null { + static create(settings: IEditorOptions): EditorOptions { const options = new EditorOptions(); options.preserveFocus = settings.preserveFocus; @@ -752,9 +760,9 @@ export class TextEditorOptions extends EditorOptions { private revealInCenterIfOutsideViewport: boolean; private editorViewState: IEditorViewState | null; - static from(input?: IBaseResourceInput): TextEditorOptions | null { + static from(input?: IBaseResourceInput): TextEditorOptions | undefined { if (!input || !input.options) { - return null; + return undefined; } return TextEditorOptions.create(input.options); @@ -971,7 +979,7 @@ export interface IResourceOptions { filter?: string | string[]; } -export function toResource(editor: IEditorInput, options?: IResourceOptions): URI | null { +export function toResource(editor: IEditorInput | null, options?: IResourceOptions): URI | null { if (!editor) { return null; } @@ -1034,8 +1042,8 @@ class EditorInputFactoryRegistry implements IEditorInputFactoryRegistry { private editorInputFactoryConstructors: { [editorInputId: string]: IConstructorSignature0 } = Object.create(null); private editorInputFactoryInstances: { [editorInputId: string]: IEditorInputFactory } = Object.create(null); - setInstantiationService(service: IInstantiationService): void { - this.instantiationService = service; + start(accessor: ServicesAccessor): void { + this.instantiationService = accessor.get(IInstantiationService); for (let key in this.editorInputFactoryConstructors) { const element = this.editorInputFactoryConstructors[key]; diff --git a/src/vs/workbench/common/editor/diffEditorInput.ts b/src/vs/workbench/common/editor/diffEditorInput.ts index 44a8af3658a..ff410762774 100644 --- a/src/vs/workbench/common/editor/diffEditorInput.ts +++ b/src/vs/workbench/common/editor/diffEditorInput.ts @@ -18,7 +18,7 @@ export class DiffEditorInput extends SideBySideEditorInput { private cachedModel: DiffEditorModel | null; - constructor(name: string, description: string, original: EditorInput, modified: EditorInput, private forceOpenAsBinary?: boolean) { + constructor(name: string, description: string | null, original: EditorInput, modified: EditorInput, private forceOpenAsBinary?: boolean) { super(name, description, original, modified); } diff --git a/src/vs/workbench/common/editor/diffEditorModel.ts b/src/vs/workbench/common/editor/diffEditorModel.ts index 794afd369d5..8405f386269 100644 --- a/src/vs/workbench/common/editor/diffEditorModel.ts +++ b/src/vs/workbench/common/editor/diffEditorModel.ts @@ -11,33 +11,39 @@ import { IEditorModel } from 'vs/platform/editor/common/editor'; * and the modified version. */ export class DiffEditorModel extends EditorModel { - protected _originalModel: IEditorModel; - protected _modifiedModel: IEditorModel; + protected _originalModel: IEditorModel | null; + protected _modifiedModel: IEditorModel | null; - constructor(originalModel: IEditorModel, modifiedModel: IEditorModel) { + constructor(originalModel: IEditorModel | null, modifiedModel: IEditorModel | null) { super(); this._originalModel = originalModel; this._modifiedModel = modifiedModel; } - get originalModel(): EditorModel { + get originalModel(): EditorModel | null { + if (!this._originalModel) { + return null; + } return this._originalModel as EditorModel; } - get modifiedModel(): EditorModel { + get modifiedModel(): EditorModel | null { + if (!this._modifiedModel) { + return null; + } return this._modifiedModel as EditorModel; } load(): Promise { return Promise.all([ - this._originalModel.load(), - this._modifiedModel.load() + this._originalModel ? this._originalModel.load() : Promise.resolve(undefined), + this._modifiedModel ? this._modifiedModel.load() : Promise.resolve(undefined), ]).then(() => this); } isResolved(): boolean { - return this.originalModel.isResolved() && this.modifiedModel.isResolved(); + return !!this.originalModel && this.originalModel.isResolved() && !!this.modifiedModel && this.modifiedModel.isResolved(); } dispose(): void { diff --git a/src/vs/workbench/common/editor/textEditorModel.ts b/src/vs/workbench/common/editor/textEditorModel.ts index b300ee95fb5..4ba0027e985 100644 --- a/src/vs/workbench/common/editor/textEditorModel.ts +++ b/src/vs/workbench/common/editor/textEditorModel.ts @@ -6,7 +6,7 @@ import { ITextModel, ITextBufferFactory } from 'vs/editor/common/model'; import { EditorModel } from 'vs/workbench/common/editor'; import { URI } from 'vs/base/common/uri'; -import { ITextEditorModel } from 'vs/editor/common/services/resolverService'; +import { ITextEditorModel, IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService'; import { IModeService, ILanguageSelection } from 'vs/editor/common/services/modeService'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IDisposable } from 'vs/base/common/lifecycle'; @@ -59,7 +59,7 @@ export abstract class BaseTextEditorModel extends EditorModel implements ITextEd }); } - get textEditorModel(): ITextModel { + get textEditorModel(): ITextModel | null { return this.textEditorModelHandle ? this.modelService.getModel(this.textEditorModelHandle) : null; } @@ -68,7 +68,7 @@ export abstract class BaseTextEditorModel extends EditorModel implements ITextEd /** * Creates the text editor model with the provided value, modeId (can be comma separated for multiple values) and optional resource URL. */ - protected createTextEditorModel(value: ITextBufferFactory, resource?: URI, modeId?: string): EditorModel { + protected createTextEditorModel(value: ITextBufferFactory, resource: URI, modeId?: string): EditorModel { const firstLineText = this.getFirstLineText(value); const languageSelection = this.getOrCreateMode(this.modeService, modeId, firstLineText); @@ -111,7 +111,7 @@ export abstract class BaseTextEditorModel extends EditorModel implements ITextEd * * @param firstLineText optional first line of the text buffer to set the mode on. This can be used to guess a mode from content. */ - protected getOrCreateMode(modeService: IModeService, modeId: string, firstLineText?: string): ILanguageSelection { + protected getOrCreateMode(modeService: IModeService, modeId: string | undefined, firstLineText?: string): ILanguageSelection { return modeService.create(modeId); } @@ -135,7 +135,7 @@ export abstract class BaseTextEditorModel extends EditorModel implements ITextEd return null; } - isResolved(): boolean { + isResolved(): this is IResolvedTextEditorModel { return !!this.textEditorModelHandle; } diff --git a/src/vs/workbench/common/editor/untitledEditorInput.ts b/src/vs/workbench/common/editor/untitledEditorInput.ts index e1800711ea6..637ea614ee6 100644 --- a/src/vs/workbench/common/editor/untitledEditorInput.ts +++ b/src/vs/workbench/common/editor/untitledEditorInput.ts @@ -17,6 +17,7 @@ import { ITextFileService } from 'vs/workbench/services/textfile/common/textfile import { telemetryURIDescriptor } from 'vs/platform/telemetry/common/telemetryUtils'; import { IHashService } from 'vs/workbench/services/hash/common/hashService'; import { ILabelService } from 'vs/platform/label/common/label'; +import { IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService'; /** * An editor input to be used for untitled text buffers. @@ -27,7 +28,7 @@ export class UntitledEditorInput extends EditorInput implements IEncodingSupport private _hasAssociatedFilePath: boolean; private cachedModel: UntitledEditorModel; - private modelResolve?: Promise; + private modelResolve?: Promise; private readonly _onDidModelChangeContent: Emitter = this._register(new Emitter()); get onDidModelChangeContent(): Event { return this._onDidModelChangeContent.event; } @@ -63,7 +64,7 @@ export class UntitledEditorInput extends EditorInput implements IEncodingSupport return this.resource; } - getModeId(): string { + getModeId(): string | null { if (this.cachedModel) { return this.cachedModel.getModeId(); } @@ -121,25 +122,21 @@ export class UntitledEditorInput extends EditorInput implements IEncodingSupport return this.labelService.getUriLabel(this.resource); } - getTitle(verbosity: Verbosity): string { + getTitle(verbosity: Verbosity): string | null { if (!this.hasAssociatedFilePath) { return this.getName(); } - let title: string | undefined; switch (verbosity) { case Verbosity.SHORT: - title = this.shortTitle; - break; + return this.shortTitle; case Verbosity.MEDIUM: - title = this.mediumTitle; - break; + return this.mediumTitle; case Verbosity.LONG: - title = this.longTitle; - break; + return this.longTitle; } - return title; + return null; } isDirty(): boolean { @@ -203,7 +200,7 @@ export class UntitledEditorInput extends EditorInput implements IEncodingSupport } } - resolve(): Promise { + resolve(): Promise { // Join a model resolve if we have had one before if (this.modelResolve) { diff --git a/src/vs/workbench/common/editor/untitledEditorModel.ts b/src/vs/workbench/common/editor/untitledEditorModel.ts index 70cfc8c48e0..15c9bd51637 100644 --- a/src/vs/workbench/common/editor/untitledEditorModel.ts +++ b/src/vs/workbench/common/editor/untitledEditorModel.ts @@ -16,6 +16,7 @@ import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration'; import { ITextBufferFactory } from 'vs/editor/common/model'; import { createTextBufferFactory } from 'vs/editor/common/model/textModel'; +import { IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService'; export class UntitledEditorModel extends BaseTextEditorModel implements IEncodingSupport { @@ -90,12 +91,12 @@ export class UntitledEditorModel extends BaseTextEditorModel implements IEncodin return this.versionId; } - getModeId(): string { + getModeId(): string | null { if (this.textEditorModel) { return this.textEditorModel.getLanguageIdentifier().language; } - return null; + return this.modeId; } getEncoding(): string { @@ -136,15 +137,15 @@ export class UntitledEditorModel extends BaseTextEditorModel implements IEncodin this.contentChangeEventScheduler.schedule(); } - load(): Promise { + load(): Promise { // Check for backups first - return this.backupFileService.loadBackupResource(this.resource).then(backupResource => { + return this.backupFileService.loadBackupResource(this.resource).then((backupResource) => { if (backupResource) { return this.backupFileService.resolveBackupContent(backupResource); } - return null; + return undefined; }).then(backupTextBufferFactory => { const hasBackup = !!backupTextBufferFactory; @@ -171,22 +172,29 @@ export class UntitledEditorModel extends BaseTextEditorModel implements IEncodin // Encoding this.configuredEncoding = this.configurationService.getValue(this.resource, 'files.encoding'); + // We know for a fact there is a text editor model here + const textEditorModel = this.textEditorModel!; + // Listen to content changes - this._register(this.textEditorModel.onDidChangeContent(() => this.onModelContentChanged())); + this._register(textEditorModel.onDidChangeContent(() => this.onModelContentChanged())); // Listen to mode changes - this._register(this.textEditorModel.onDidChangeLanguage(() => this.onConfigurationChange())); // mode change can have impact on config + this._register(textEditorModel.onDidChangeLanguage(() => this.onConfigurationChange())); // mode change can have impact on config - return this; + return this as UntitledEditorModel & IResolvedTextEditorModel; }); } private onModelContentChanged(): void { + if (!this.isResolved()) { + return; + } + this.versionId++; // mark the untitled editor as non-dirty once its content becomes empty and we do // not have an associated path set. we never want dirty indicator in that case. - if (!this._hasAssociatedFilePath && this.textEditorModel.getLineCount() === 1 && this.textEditorModel.getLineContent(1) === '') { + if (!this._hasAssociatedFilePath && this.textEditorModel && this.textEditorModel.getLineCount() === 1 && this.textEditorModel.getLineContent(1) === '') { this.setDirty(false); } diff --git a/src/vs/workbench/common/panel.ts b/src/vs/workbench/common/panel.ts index 57c365b0a62..628846c404a 100644 --- a/src/vs/workbench/common/panel.ts +++ b/src/vs/workbench/common/panel.ts @@ -4,5 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import { IComposite } from 'vs/workbench/common/composite'; +import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; + +export const ActivePanelContext = new RawContextKey('activePanel', ''); +export const PanelFocusContext = new RawContextKey('panelFocus', false); export interface IPanel extends IComposite { } diff --git a/src/vs/workbench/common/resources.ts b/src/vs/workbench/common/resources.ts index b8cf29b7272..6a597a960ff 100644 --- a/src/vs/workbench/common/resources.ts +++ b/src/vs/workbench/common/resources.ts @@ -25,11 +25,11 @@ export class ResourceContextKey extends Disposable implements IContextKey { static HasResource = new RawContextKey('resourceSet', false); static IsFileSystemResource = new RawContextKey('isFileSystemResource', false); - private readonly _resourceKey: IContextKey; - private readonly _schemeKey: IContextKey; - private readonly _filenameKey: IContextKey; + private readonly _resourceKey: IContextKey; + private readonly _schemeKey: IContextKey; + private readonly _filenameKey: IContextKey; private readonly _langIdKey: IContextKey; - private readonly _extensionKey: IContextKey; + private readonly _extensionKey: IContextKey; private readonly _hasResource: IContextKey; private readonly _isFileSystemResource: IContextKey; @@ -59,15 +59,15 @@ export class ResourceContextKey extends Disposable implements IContextKey { })); } - set(value: URI) { + set(value: URI | null) { if (!ResourceContextKey._uriEquals(this._resourceKey.get(), value)) { this._resourceKey.set(value); - this._schemeKey.set(value && value.scheme); - this._filenameKey.set(value && basename(value)); + this._schemeKey.set(value ? value.scheme : null); + this._filenameKey.set(value ? basename(value) : null); this._langIdKey.set(value ? this._modeService.getModeIdByFilepathOrFirstLine(value.fsPath) : null); - this._extensionKey.set(value && extname(value)); + this._extensionKey.set(value ? extname(value) : null); this._hasResource.set(!!value); - this._isFileSystemResource.set(value && this._fileService.canHandleResource(value)); + this._isFileSystemResource.set(value ? this._fileService.canHandleResource(value) : false); } } @@ -82,7 +82,7 @@ export class ResourceContextKey extends Disposable implements IContextKey { } get(): URI | undefined { - return this._resourceKey.get(); + return this._resourceKey.get() || undefined; } private static _uriEquals(a: URI | undefined | null, b: URI | undefined | null): boolean { diff --git a/src/vs/workbench/common/viewlet.ts b/src/vs/workbench/common/viewlet.ts index aa90d23fb94..09836ae5e8a 100644 --- a/src/vs/workbench/common/viewlet.ts +++ b/src/vs/workbench/common/viewlet.ts @@ -4,6 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import { IComposite } from 'vs/workbench/common/composite'; +import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; + +export const SidebarVisibleContext = new RawContextKey('sidebarVisible', false); +export const SideBarVisibleContext = new RawContextKey('sideBarVisible', false); +export const SidebarFocusContext = new RawContextKey('sideBarFocus', false); +export const ActiveViewletContext = new RawContextKey('activeViewlet', ''); export interface IViewlet extends IComposite { diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index 2a16b9bd9f6..236ffeacaa2 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -121,8 +121,7 @@ export interface IViewDescriptor { readonly name: string; - // TODO@Sandeep do we really need this?! - readonly ctor: any; + readonly ctorDescriptor: { ctor: any, arguments?: any[] }; readonly when?: ContextKeyExpr; @@ -378,7 +377,7 @@ export interface ITreeItem { handle: string; - parentHandle: string; + parentHandle: string | null; collapsibleState: TreeItemCollapsibleState; diff --git a/src/vs/workbench/contrib/backup/common/backupModelTracker.ts b/src/vs/workbench/contrib/backup/common/backupModelTracker.ts index 68a433394c4..2b245986c9b 100644 --- a/src/vs/workbench/contrib/backup/common/backupModelTracker.ts +++ b/src/vs/workbench/contrib/backup/common/backupModelTracker.ts @@ -65,14 +65,24 @@ export class BackupModelTracker extends Disposable implements IWorkbenchContribu // Do not backup when auto save after delay is configured if (!this.configuredAutoSaveAfterDelay) { const model = this.textFileService.models.get(event.resource); - this.backupFileService.backupResource(model.getResource(), model.createSnapshot(), model.getVersionId()); + if (model) { + const snapshot = model.createSnapshot(); + if (snapshot) { + this.backupFileService.backupResource(model.getResource(), snapshot, model.getVersionId()); + } + } } } } private onUntitledModelChanged(resource: Uri): void { if (this.untitledEditorService.isDirty(resource)) { - this.untitledEditorService.loadOrCreate({ resource }).then(model => this.backupFileService.backupResource(resource, model.createSnapshot(), model.getVersionId())); + this.untitledEditorService.loadOrCreate({ resource }).then(model => { + const snapshot = model.createSnapshot(); + if (snapshot) { + this.backupFileService.backupResource(resource, snapshot, model.getVersionId()); + } + }); } else { this.discardBackup(resource); } diff --git a/src/vs/workbench/contrib/codeEditor/browser/selectionClipboard.ts b/src/vs/workbench/contrib/codeEditor/browser/selectionClipboard.ts index a1f46f8ff1d..4c3a8ba75ff 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/selectionClipboard.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/selectionClipboard.ts @@ -5,6 +5,7 @@ import { RunOnceScheduler } from 'vs/base/common/async'; import { Disposable } from 'vs/base/common/lifecycle'; +import * as process from 'vs/base/common/process'; import * as platform from 'vs/base/common/platform'; import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; diff --git a/src/vs/workbench/contrib/codeinset/electron-browser/codeInset.contribution.ts b/src/vs/workbench/contrib/codeinset/electron-browser/codeInset.contribution.ts index bce7c3f7d4f..a0369eaff5e 100644 --- a/src/vs/workbench/contrib/codeinset/electron-browser/codeInset.contribution.ts +++ b/src/vs/workbench/contrib/codeinset/electron-browser/codeInset.contribution.ts @@ -309,7 +309,7 @@ export class CodeInsetController implements editorCommon.IEditorContribution { const widgetPromises = widgetRequests.map(request => { if (request.resolved) { - return Promise.resolve(void 0); + return Promise.resolve(undefined); } let a = new Promise(resolve => { this._pendingWebviews.set(request.symbol.id, element => { diff --git a/src/vs/workbench/contrib/comments/electron-browser/commentThreadWidget.ts b/src/vs/workbench/contrib/comments/electron-browser/commentThreadWidget.ts index 5c3bad5a5b0..973dd5a3292 100644 --- a/src/vs/workbench/contrib/comments/electron-browser/commentThreadWidget.ts +++ b/src/vs/workbench/contrib/comments/electron-browser/commentThreadWidget.ts @@ -11,7 +11,7 @@ import { Action } from 'vs/base/common/actions'; import * as arrays from 'vs/base/common/arrays'; import { Color } from 'vs/base/common/color'; import { Emitter, Event } from 'vs/base/common/event'; -import { IDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import * as platform from 'vs/base/common/platform'; import * as strings from 'vs/base/common/strings'; import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; @@ -69,7 +69,7 @@ export class ReviewZoneWidget extends ZoneWidget { private _owner: string; private _pendingComment: string; private _draftMode: modes.DraftMode; - private _localToDispose: IDisposable[]; + private _submitActionsDisposables: IDisposable[]; private _globalToDispose: IDisposable[]; private _markdownRenderer: MarkdownRenderer; private _styleElement: HTMLStyleElement; @@ -117,7 +117,7 @@ export class ReviewZoneWidget extends ZoneWidget { this._draftMode = draftMode; this._isCollapsed = commentThread.collapsibleState !== modes.CommentThreadCollapsibleState.Expanded; this._globalToDispose = []; - this._localToDispose = []; + this._submitActionsDisposables = []; this._formActions = null; this.create(); @@ -296,12 +296,14 @@ export class ReviewZoneWidget extends ZoneWidget { } updateDraftMode(draftMode: modes.DraftMode) { - this._draftMode = draftMode; + if (this._draftMode !== draftMode) { + this._draftMode = draftMode; - if (this._formActions) { - let model = this._commentEditor.getModel(); - dom.clearNode(this._formActions); - this.createCommentWidgetActions(this._formActions, model); + if (this._formActions) { + let model = this._commentEditor.getModel(); + dom.clearNode(this._formActions); + this.createCommentWidgetActions(this._formActions, model); + } } } @@ -316,8 +318,8 @@ export class ReviewZoneWidget extends ZoneWidget { display(lineNumber: number) { this._commentGlyph = new CommentGlyphWidget(this.editor, lineNumber); - this._localToDispose.push(this.editor.onMouseDown(e => this.onEditorMouseDown(e))); - this._localToDispose.push(this.editor.onMouseUp(e => this.onEditorMouseUp(e))); + this._disposables.push(this.editor.onMouseDown(e => this.onEditorMouseDown(e))); + this._disposables.push(this.editor.onMouseUp(e => this.onEditorMouseUp(e))); let headHeight = Math.ceil(this.editor.getConfiguration().lineHeight * 1.2); this._headElement.style.height = `${headHeight}px`; this._headElement.style.lineHeight = this._headElement.style.height; @@ -343,12 +345,12 @@ export class ReviewZoneWidget extends ZoneWidget { }); const resource = URI.parse(`${COMMENT_SCHEME}:commentinput-${modeId}.md?${params}`); const model = this.modelService.createModel(this._pendingComment || '', this.modeService.createByFilepathOrFirstLine(resource.path), resource, false); - this._localToDispose.push(model); + this._disposables.push(model); this._commentEditor.setModel(model); - this._localToDispose.push(this._commentEditor); - this._localToDispose.push(this._commentEditor.getModel().onDidChangeContent(() => this.setCommentEditorDecorations())); + this._disposables.push(this._commentEditor); + this._disposables.push(this._commentEditor.getModel().onDidChangeContent(() => this.setCommentEditorDecorations())); if ((this._commentThread as modes.CommentThread2).commentThreadHandle !== undefined) { - this._localToDispose.push(this._commentEditor.onDidFocusEditorWidget(() => { + this._disposables.push(this._commentEditor.onDidFocusEditorWidget(() => { let commentThread = this._commentThread as modes.CommentThread2; commentThread.input = { uri: this._commentEditor.getModel().uri, @@ -357,7 +359,7 @@ export class ReviewZoneWidget extends ZoneWidget { this.commentService.setActiveCommentThread(this._commentThread); })); - this._localToDispose.push(this._commentEditor.getModel().onDidChangeContent(() => { + this._disposables.push(this._commentEditor.getModel().onDidChangeContent(() => { let modelContent = this._commentEditor.getValue(); let thread = (this._commentThread as modes.CommentThread2); if (thread.input.uri === this._commentEditor.getModel().uri && thread.input.value !== modelContent) { @@ -367,7 +369,7 @@ export class ReviewZoneWidget extends ZoneWidget { } })); - this._localToDispose.push((this._commentThread as modes.CommentThread2).onDidChangeInput(input => { + this._disposables.push((this._commentThread as modes.CommentThread2).onDidChangeInput(input => { let thread = (this._commentThread as modes.CommentThread2); if (thread.input.uri !== this._commentEditor.getModel().uri) { @@ -389,7 +391,7 @@ export class ReviewZoneWidget extends ZoneWidget { } })); - this._localToDispose.push((this._commentThread as modes.CommentThread2).onDidChangeComments(_ => { + this._disposables.push((this._commentThread as modes.CommentThread2).onDidChangeComments(_ => { this.update(this._commentThread); })); } @@ -406,7 +408,7 @@ export class ReviewZoneWidget extends ZoneWidget { } } - this._localToDispose.push(this._commentEditor.onKeyDown((ev: IKeyboardEvent) => { + this._disposables.push(this._commentEditor.onKeyDown((ev: IKeyboardEvent) => { const hasExistingComments = this._commentThread.comments.length > 0; if (this._commentEditor.getModel().getValueLength() === 0 && ev.keyCode === KeyCode.Escape) { @@ -432,7 +434,7 @@ export class ReviewZoneWidget extends ZoneWidget { if ((this._commentThread as modes.CommentThread2).commentThreadHandle !== undefined) { this.createCommentWidgetActions2(this._formActions, model); - this._localToDispose.push((this._commentThread as modes.CommentThread2).onDidChangeAcceptInputCommands(_ => { + this._disposables.push((this._commentThread as modes.CommentThread2).onDidChangeAcceptInputCommands(_ => { dom.clearNode(this._formActions); this.createCommentWidgetActions2(this._formActions, model); })); @@ -471,12 +473,14 @@ export class ReviewZoneWidget extends ZoneWidget { } private createCommentWidgetActions(container: HTMLElement, model: ITextModel) { + dispose(this._submitActionsDisposables); + const button = new Button(container); - this._localToDispose.push(attachButtonStyler(button, this.themeService)); + this._submitActionsDisposables.push(attachButtonStyler(button, this.themeService)); button.label = 'Add comment'; button.enabled = model.getValueLength() > 0; - this._localToDispose.push(this._commentEditor.onDidChangeModelContent(_ => { + this._submitActionsDisposables.push(this._commentEditor.onDidChangeModelContent(_ => { if (this._commentEditor.getValue()) { button.enabled = true; } else { @@ -498,7 +502,7 @@ export class ReviewZoneWidget extends ZoneWidget { const deleteDraftLabel = this.commentService.getDeleteDraftLabel(this._owner); if (deleteDraftLabel) { const deletedraftButton = new Button(container); - this._disposables.push(attachButtonStyler(deletedraftButton, this.themeService)); + this._submitActionsDisposables.push(attachButtonStyler(deletedraftButton, this.themeService)); deletedraftButton.label = deleteDraftLabel; deletedraftButton.enabled = true; @@ -514,7 +518,7 @@ export class ReviewZoneWidget extends ZoneWidget { const submitDraftLabel = this.commentService.getFinishDraftLabel(this._owner); if (submitDraftLabel) { const submitdraftButton = new Button(container); - this._disposables.push(attachButtonStyler(submitdraftButton, this.themeService)); + this._submitActionsDisposables.push(attachButtonStyler(submitdraftButton, this.themeService)); submitdraftButton.label = this.commentService.getFinishDraftLabel(this._owner); submitdraftButton.enabled = true; @@ -540,7 +544,7 @@ export class ReviewZoneWidget extends ZoneWidget { draftButton.label = this.commentService.getStartDraftLabel(this._owner); draftButton.enabled = model.getValueLength() > 0; - this._localToDispose.push(this._commentEditor.onDidChangeModelContent(_ => { + this._submitActionsDisposables.push(this._commentEditor.onDidChangeModelContent(_ => { if (this._commentEditor.getValue()) { draftButton.enabled = true; } else { @@ -571,12 +575,12 @@ export class ReviewZoneWidget extends ZoneWidget { commentThread.acceptInputCommands.reverse().forEach(command => { const button = new Button(container); - this._localToDispose.push(attachButtonStyler(button, this.themeService)); + this._disposables.push(attachButtonStyler(button, this.themeService)); button.label = command.title; let commandId = command.id; let args = command.arguments || []; - this._localToDispose.push(button.onDidClick(async () => { + this._disposables.push(button.onDidClick(async () => { commentThread.input = { uri: this._commentEditor.getModel().uri, value: this._commentEditor.getValue() @@ -707,8 +711,8 @@ export class ReviewZoneWidget extends ZoneWidget { } this._reviewThreadReplyButton.textContent = nls.localize('reply', "Reply..."); // bind click/escape actions for reviewThreadReplyButton and textArea - this._localToDispose.push(dom.addDisposableListener(this._reviewThreadReplyButton, 'click', _ => this.expandReplyArea())); - this._localToDispose.push(dom.addDisposableListener(this._reviewThreadReplyButton, 'focus', _ => this.expandReplyArea())); + this._disposables.push(dom.addDisposableListener(this._reviewThreadReplyButton, 'click', _ => this.expandReplyArea())); + this._disposables.push(dom.addDisposableListener(this._reviewThreadReplyButton, 'focus', _ => this.expandReplyArea())); this._commentEditor.onDidBlurEditorWidget(() => { if (this._commentEditor.getModel().getValueLength() === 0 && dom.hasClass(this._commentForm, 'expand')) { @@ -920,7 +924,7 @@ export class ReviewZoneWidget extends ZoneWidget { } this._globalToDispose.forEach(global => global.dispose()); - this._localToDispose.forEach(local => local.dispose()); + this._submitActionsDisposables.forEach(local => local.dispose()); this._onDidClose.fire(undefined); } } \ No newline at end of file diff --git a/src/vs/workbench/contrib/debug/browser/debugANSIHandling.ts b/src/vs/workbench/contrib/debug/browser/debugANSIHandling.ts index 31b85e0087e..9894e465b20 100644 --- a/src/vs/workbench/contrib/debug/browser/debugANSIHandling.ts +++ b/src/vs/workbench/contrib/debug/browser/debugANSIHandling.ts @@ -56,7 +56,7 @@ export function handleANSIOutput(text: string, linkDetector: LinkDetector): HTML * Certain ranges that are matched here do not contain real graphics rendition sequences. For * the sake of having a simpler expression, they have been included anyway. */ - if (ansiSequence.match(/^(?:[349][0-7]|10[0-7]|[01]|4|[34]9)(?:;(?:[349][0-7]|10[0-7]|[01]|4|[34]9))*;?m$/)) { + if (ansiSequence.match(/^(?:[349][0-7]|10[0-7]|[013]|4|[34]9)(?:;(?:[349][0-7]|10[0-7]|[013]|4|[34]9))*;?m$/)) { const styleCodes: number[] = ansiSequence.slice(0, -1) // Remove final 'm' character. .split(';') // Separate style codes. @@ -68,6 +68,8 @@ export function handleANSIOutput(text: string, linkDetector: LinkDetector): HTML styleNames = []; } else if (code === 1) { styleNames.push('code-bold'); + } else if (code === 3) { + styleNames.push('code-italic'); } else if (code === 4) { styleNames.push('code-underline'); } else if ((code >= 30 && code <= 37) || (code >= 90 && code <= 97)) { diff --git a/src/vs/workbench/contrib/debug/browser/debugCommands.ts b/src/vs/workbench/contrib/debug/browser/debugCommands.ts index b231d8a4853..ffb98ba210f 100644 --- a/src/vs/workbench/contrib/debug/browser/debugCommands.ts +++ b/src/vs/workbench/contrib/debug/browser/debugCommands.ts @@ -22,7 +22,7 @@ import { openBreakpointSource } from 'vs/workbench/contrib/debug/browser/breakpo import { INotificationService } from 'vs/platform/notification/common/notification'; import { InputFocusedContext } from 'vs/platform/contextkey/common/contextkeys'; import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; -import { PanelFocusContext } from 'vs/workbench/browser/parts/panel/panelPart'; +import { PanelFocusContext } from 'vs/workbench/common/panel'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { onUnexpectedError } from 'vs/base/common/errors'; diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts index e4bef65fcc3..7b460194b62 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts @@ -16,7 +16,7 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { openBreakpointSource } from 'vs/workbench/contrib/debug/browser/breakpointsView'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { PanelFocusContext } from 'vs/workbench/browser/parts/panel/panelPart'; +import { PanelFocusContext } from 'vs/workbench/common/panel'; import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; export const TOGGLE_BREAKPOINT_ID = 'editor.debug.action.toggleBreakpoint'; diff --git a/src/vs/workbench/contrib/debug/browser/debugToolbar.ts b/src/vs/workbench/contrib/debug/browser/debugToolbar.ts index cd9233d3714..f28f1268117 100644 --- a/src/vs/workbench/contrib/debug/browser/debugToolbar.ts +++ b/src/vs/workbench/contrib/debug/browser/debugToolbar.ts @@ -228,7 +228,7 @@ export class DebugToolbar extends Themable implements IWorkbenchContribution { this.$el.style.left = `${x}px`; if (y === undefined) { - y = this.storageService.getInteger(DEBUG_TOOLBAR_Y_KEY, StorageScope.GLOBAL, 0); + y = this.storageService.getNumber(DEBUG_TOOLBAR_Y_KEY, StorageScope.GLOBAL, 0); } const titleAreaHeight = 35; if ((y < titleAreaHeight / 2) || (y > titleAreaHeight + titleAreaHeight / 2)) { diff --git a/src/vs/workbench/contrib/debug/browser/media/repl.css b/src/vs/workbench/contrib/debug/browser/media/repl.css index a1c82b4b5e2..c12de58c3ad 100644 --- a/src/vs/workbench/contrib/debug/browser/media/repl.css +++ b/src/vs/workbench/contrib/debug/browser/media/repl.css @@ -124,6 +124,7 @@ /* ANSI Codes */ .monaco-workbench .repl .repl-tree .output.expression .code-bold { font-weight: bold; } +.monaco-workbench .repl .repl-tree .output.expression .code-italic { font-style: italic; } .monaco-workbench .repl .repl-tree .output.expression .code-underline { text-decoration: underline; } /* Regular and bright color codes are currently treated the same. */ diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index 89c6088288c..e303532fc05 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -246,6 +246,8 @@ export interface IThread extends ITreeElement { */ readonly exceptionInfo: Promise; + readonly stateLabel: string; + /** * Gets the callstack if it has already been received from the debug * adapter, otherwise it returns null. diff --git a/src/vs/workbench/contrib/debug/common/debugModel.ts b/src/vs/workbench/contrib/debug/common/debugModel.ts index 36f80abb7f8..91422072397 100644 --- a/src/vs/workbench/contrib/debug/common/debugModel.ts +++ b/src/vs/workbench/contrib/debug/common/debugModel.ts @@ -418,6 +418,15 @@ export class Thread implements IThread { return this.staleCallStack; } + get stateLabel(): string { + if (this.stopped) { + return this.stoppedDetails.description || + this.stoppedDetails.reason ? nls.localize({ key: 'pausedOn', comment: ['indicates reason for program being paused'] }, "Paused on {0}", this.stoppedDetails.reason) : nls.localize('paused', "Paused"); + } + + return nls.localize({ key: 'running', comment: ['indicates state'] }, "Running"); + } + /** * Queries the debug adapter for the callstack and returns a promise * which completes once the call stack has been retrieved. diff --git a/src/vs/workbench/contrib/debug/electron-browser/callStackView.ts b/src/vs/workbench/contrib/debug/electron-browser/callStackView.ts index dc153ca4e0e..ff9c117f7e5 100644 --- a/src/vs/workbench/contrib/debug/electron-browser/callStackView.ts +++ b/src/vs/workbench/contrib/debug/electron-browser/callStackView.ts @@ -133,7 +133,7 @@ export class CallStackView extends ViewletPanel { return e.getLabel(); } if (e instanceof Thread) { - return e.name; + return `${e.name} ${e.stateLabel}`; } if (e instanceof StackFrame || typeof e === 'string') { return e; @@ -410,13 +410,7 @@ class ThreadsRenderer implements ITreeRenderer(PanelExtensions.Panels).registerPanel(new PanelDescri Registry.as(PanelExtensions.Panels).setDefaultPanelId(REPL_ID); // Register default debug views -ViewsRegistry.registerViews([{ id: VARIABLES_VIEW_ID, name: nls.localize('variables', "Variables"), ctor: VariablesView, order: 10, weight: 40, canToggleVisibility: true, focusCommand: { id: 'workbench.debug.action.focusVariablesView' } }], VIEW_CONTAINER); -ViewsRegistry.registerViews([{ id: WATCH_VIEW_ID, name: nls.localize('watch', "Watch"), ctor: WatchExpressionsView, order: 20, weight: 10, canToggleVisibility: true, focusCommand: { id: 'workbench.debug.action.focusWatchView' } }], VIEW_CONTAINER); -ViewsRegistry.registerViews([{ id: CALLSTACK_VIEW_ID, name: nls.localize('callStack', "Call Stack"), ctor: CallStackView, order: 30, weight: 30, canToggleVisibility: true, focusCommand: { id: 'workbench.debug.action.focusCallStackView' } }], VIEW_CONTAINER); -ViewsRegistry.registerViews([{ id: BREAKPOINTS_VIEW_ID, name: nls.localize('breakpoints', "Breakpoints"), ctor: BreakpointsView, order: 40, weight: 20, canToggleVisibility: true, focusCommand: { id: 'workbench.debug.action.focusBreakpointsView' } }], VIEW_CONTAINER); -ViewsRegistry.registerViews([{ id: LOADED_SCRIPTS_VIEW_ID, name: nls.localize('loadedScripts', "Loaded Scripts"), ctor: LoadedScriptsView, order: 35, weight: 5, canToggleVisibility: true, collapsed: true, when: CONTEXT_LOADED_SCRIPTS_SUPPORTED }], VIEW_CONTAINER); +ViewsRegistry.registerViews([{ id: VARIABLES_VIEW_ID, name: nls.localize('variables', "Variables"), ctorDescriptor: { ctor: VariablesView }, order: 10, weight: 40, canToggleVisibility: true, focusCommand: { id: 'workbench.debug.action.focusVariablesView' } }], VIEW_CONTAINER); +ViewsRegistry.registerViews([{ id: WATCH_VIEW_ID, name: nls.localize('watch', "Watch"), ctorDescriptor: { ctor: WatchExpressionsView }, order: 20, weight: 10, canToggleVisibility: true, focusCommand: { id: 'workbench.debug.action.focusWatchView' } }], VIEW_CONTAINER); +ViewsRegistry.registerViews([{ id: CALLSTACK_VIEW_ID, name: nls.localize('callStack', "Call Stack"), ctorDescriptor: { ctor: CallStackView }, order: 30, weight: 30, canToggleVisibility: true, focusCommand: { id: 'workbench.debug.action.focusCallStackView' } }], VIEW_CONTAINER); +ViewsRegistry.registerViews([{ id: BREAKPOINTS_VIEW_ID, name: nls.localize('breakpoints', "Breakpoints"), ctorDescriptor: { ctor: BreakpointsView }, order: 40, weight: 20, canToggleVisibility: true, focusCommand: { id: 'workbench.debug.action.focusBreakpointsView' } }], VIEW_CONTAINER); +ViewsRegistry.registerViews([{ id: LOADED_SCRIPTS_VIEW_ID, name: nls.localize('loadedScripts', "Loaded Scripts"), ctorDescriptor: { ctor: LoadedScriptsView }, order: 35, weight: 5, canToggleVisibility: true, collapsed: true, when: CONTEXT_LOADED_SCRIPTS_SUPPORTED }], VIEW_CONTAINER); // register action to open viewlet const registry = Registry.as(WorkbenchActionRegistryExtensions.WorkbenchActions); diff --git a/src/vs/workbench/contrib/debug/electron-browser/terminalSupport.ts b/src/vs/workbench/contrib/debug/electron-browser/terminalSupport.ts index 43be1917204..e91e056226a 100644 --- a/src/vs/workbench/contrib/debug/electron-browser/terminalSupport.ts +++ b/src/vs/workbench/contrib/debug/electron-browser/terminalSupport.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import { IDisposable } from 'vs/base/common/lifecycle'; import { ITerminalService, ITerminalInstance } from 'vs/workbench/contrib/terminal/common/terminal'; -import { ITerminalService as IExternalTerminalService } from 'vs/workbench/contrib/execution/common/execution'; +import { IExternalTerminalService } from 'vs/workbench/contrib/externalTerminal/common/externalTerminal'; import { ITerminalLauncher, ITerminalSettings } from 'vs/workbench/contrib/debug/common/debug'; import { hasChildProcesses, prepareCommand } from 'vs/workbench/contrib/debug/node/terminals'; @@ -17,14 +17,14 @@ export class TerminalLauncher implements ITerminalLauncher { constructor( @ITerminalService private readonly terminalService: ITerminalService, - @IExternalTerminalService private readonly nativeTerminalService: IExternalTerminalService + @IExternalTerminalService private readonly externalTerminalService: IExternalTerminalService ) { } runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): Promise { if (args.kind === 'external') { - return this.nativeTerminalService.runInTerminal(args.title, args.cwd, args.args, args.env || {}); + return this.externalTerminalService.runInTerminal(args.title, args.cwd, args.args, args.env || {}); } if (!this.terminalDisposedListener) { diff --git a/src/vs/workbench/contrib/debug/node/debugAdapter.ts b/src/vs/workbench/contrib/debug/node/debugAdapter.ts index b5f6da93cb8..1f0a6b7e6dc 100644 --- a/src/vs/workbench/contrib/debug/node/debugAdapter.ts +++ b/src/vs/workbench/contrib/debug/node/debugAdapter.ts @@ -394,7 +394,10 @@ export class ExecutableDebugAdapter extends StreamDebugAdapter { // console.log('%c' + sanitize(data), 'background: #ddd; font-style: italic;'); // }); this.serverProcess.stderr.on('data', (data: string) => { - outputService.getChannel(ExtensionsChannelId).append(sanitize(data)); + const channel = outputService.getChannel(ExtensionsChannelId); + if (channel) { + channel.append(sanitize(data)); + } }); } diff --git a/src/vs/workbench/contrib/debug/node/terminals.ts b/src/vs/workbench/contrib/debug/node/terminals.ts index d91a788c908..f399b6bb8d9 100644 --- a/src/vs/workbench/contrib/debug/node/terminals.ts +++ b/src/vs/workbench/contrib/debug/node/terminals.ts @@ -129,7 +129,7 @@ class MacTerminalService extends TerminalLauncher { // and then launches the program inside that window. const script = terminalApp === MacTerminalService.DEFAULT_TERMINAL_OSX ? 'TerminalHelper' : 'iTermHelper'; - const scriptpath = getPathFromAmdModule(require, `vs/workbench/contrib/execution/electron-browser/${script}.scpt`); + const scriptpath = getPathFromAmdModule(require, `vs/workbench/contrib/externalTerminal/electron-browser/${script}.scpt`); const osaArgs = [ scriptpath, diff --git a/src/vs/workbench/contrib/debug/test/browser/debugANSIHandling.test.ts b/src/vs/workbench/contrib/debug/test/browser/debugANSIHandling.test.ts index e42257fb540..4902d43a463 100644 --- a/src/vs/workbench/contrib/debug/test/browser/debugANSIHandling.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/debugANSIHandling.test.ts @@ -92,6 +92,11 @@ suite('Debug - ANSI Handling', () => { assert(dom.hasClass(child, 'code-bold')); }); + // Italic code + assertSingleSequenceElement('\x1b[3m', (child) => { + assert(dom.hasClass(child, 'code-italic')); + }); + // Underline code assertSingleSequenceElement('\x1b[4m', (child) => { assert(dom.hasClass(child, 'code-underline')); @@ -126,10 +131,11 @@ suite('Debug - ANSI Handling', () => { } // Codes do not interfere - assertSingleSequenceElement('\x1b[1;4;30;31;32;33;34;35;36;37m', (child) => { - assert.equal(10, child.classList.length); + assertSingleSequenceElement('\x1b[1;3;4;30;31;32;33;34;35;36;37m', (child) => { + assert.equal(11, child.classList.length); assert(dom.hasClass(child, 'code-bold')); + assert(dom.hasClass(child, 'code-italic')); assert(dom.hasClass(child, 'code-underline')); for (let i = 30; i <= 37; i++) { assert(dom.hasClass(child, 'code-foreground-' + i)); @@ -178,14 +184,15 @@ suite('Debug - ANSI Handling', () => { test('Expected multiple sequence operation', () => { // Multiple codes affect the same text - assertSingleSequenceElement('\x1b[1m\x1b[4m\x1b[32m', (child) => { + assertSingleSequenceElement('\x1b[1m\x1b[3m\x1b[4m\x1b[32m', (child) => { assert(dom.hasClass(child, 'code-bold')); + assert(dom.hasClass(child, 'code-italic')); assert(dom.hasClass(child, 'code-underline')); assert(dom.hasClass(child, 'code-foreground-32')); }); // Consecutive codes do not affect previous ones - assertMultipleSequenceElements('\x1b[1mbold\x1b[32mgreen\x1b[4munderline\x1b[0mnothing', [ + assertMultipleSequenceElements('\x1b[1mbold\x1b[32mgreen\x1b[4munderline\x1b[3mitalic\x1b[0mnothing', [ (bold) => { assert.equal(1, bold.classList.length); assert(dom.hasClass(bold, 'code-bold')); @@ -201,6 +208,13 @@ suite('Debug - ANSI Handling', () => { assert(dom.hasClass(underline, 'code-foreground-32')); assert(dom.hasClass(underline, 'code-underline')); }, + (italic) => { + assert.equal(4, italic.classList.length); + assert(dom.hasClass(italic, 'code-bold')); + assert(dom.hasClass(italic, 'code-foreground-32')); + assert(dom.hasClass(italic, 'code-underline')); + assert(dom.hasClass(italic, 'code-italic')); + }, (nothing) => { assert.equal(0, nothing.classList.length); }, diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionsUtils.ts b/src/vs/workbench/contrib/extensions/common/extensionsUtils.ts similarity index 96% rename from src/vs/workbench/contrib/extensions/electron-browser/extensionsUtils.ts rename to src/vs/workbench/contrib/extensions/common/extensionsUtils.ts index 0284d7fc7b7..91f521c2d94 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensionsUtils.ts +++ b/src/vs/workbench/contrib/extensions/common/extensionsUtils.ts @@ -15,7 +15,6 @@ import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { Severity, INotificationService } from 'vs/platform/notification/common/notification'; -import product from 'vs/platform/product/node/product'; export interface IExtensionStatus { identifier: IExtensionIdentifier; @@ -137,8 +136,3 @@ export function isKeymapExtension(tipsService: IExtensionTipsService, extension: const cats = extension.local.manifest.categories; return cats && cats.indexOf('Keymaps') !== -1 || tipsService.getKeymapRecommendations().some(({ extensionId }) => areSameExtensions({ id: extensionId }, extension.local.identifier)); } - -export function getKeywordsForExtension(extension: string): string[] { - const keywords = product.extensionKeywords || {}; - return keywords[extension] || []; -} \ No newline at end of file diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionEditor.ts index 54f6af6c0d4..cee2fd121d7 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensionEditor.ts @@ -89,8 +89,8 @@ function removeEmbeddedSVGs(documentContent: string): string { class NavBar { - private _onChange = new Emitter<{ id: string, focus: boolean }>(); - get onChange(): Event<{ id: string, focus: boolean }> { return this._onChange.event; } + private _onChange = new Emitter<{ id: string | null, focus: boolean }>(); + get onChange(): Event<{ id: string | null, focus: boolean }> { return this._onChange.event; } private currentId: string | null = null; private actions: Action[]; @@ -564,17 +564,21 @@ export class ExtensionEditor extends BaseEditor { } private openReadme(): Promise { - return this.openMarkdown(this.extensionReadme.get(), localize('noReadme', "No README available.")); + return this.openMarkdown(this.extensionReadme!.get(), localize('noReadme', "No README available.")); } private openChangelog(): Promise { - return this.openMarkdown(this.extensionChangelog.get(), localize('noChangelog', "No Changelog available.")); + return this.openMarkdown(this.extensionChangelog!.get(), localize('noChangelog', "No Changelog available.")); } private openContributions(): Promise { const content = $('div', { class: 'subcontent', tabindex: '0' }); - return this.loadContents(() => this.extensionManifest.get()) + return this.loadContents(() => this.extensionManifest!.get()) .then(manifest => { + if (!manifest) { + return content; + } + const scrollableContent = new DomScrollableElement(content, {}); const layout = () => scrollableContent.scanDomNode(); @@ -619,7 +623,7 @@ export class ExtensionEditor extends BaseEditor { return Promise.resolve(this.content); } - return this.loadContents(() => this.extensionDependencies.get()) + return this.loadContents(() => this.extensionDependencies!.get()) .then(extensionDependencies => { if (extensionDependencies) { const content = $('div', { class: 'subcontent' }); diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionTipsService.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionTipsService.ts index 5b401acdbcd..df036e95dbc 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensionTipsService.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensionTipsService.ts @@ -43,7 +43,6 @@ import { URI } from 'vs/base/common/uri'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IExperimentService, ExperimentActionType, ExperimentState } from 'vs/workbench/contrib/experiments/node/experimentService'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { getKeywordsForExtension } from 'vs/workbench/contrib/extensions/electron-browser/extensionsUtils'; import { ExtensionType } from 'vs/platform/extensions/common/extensions'; import { extname } from 'vs/base/common/resources'; @@ -759,7 +758,8 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe return; } - const keywords = getKeywordsForExtension(fileExtension); + const lookup = product.extensionKeywords || {}; + const keywords = lookup[fileExtension] || []; this._galleryService.query({ text: `tag:"__ext_${fileExtension}" ${keywords.map(tag => `tag:"${tag}"`)}` }).then(pager => { if (!pager || !pager.firstPage || !pager.firstPage.length) { return; diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts index fab31c3e8d7..689e0509b46 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts @@ -33,7 +33,7 @@ import * as jsonContributionRegistry from 'vs/platform/jsonschemas/common/jsonCo import { ExtensionsConfigurationSchema, ExtensionsConfigurationSchemaId } from 'vs/workbench/contrib/extensions/common/extensionsFileTemplate'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { KeymapExtensions } from 'vs/workbench/contrib/extensions/electron-browser/extensionsUtils'; +import { KeymapExtensions } from 'vs/workbench/contrib/extensions/common/extensionsUtils'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { GalleryExtensionsHandler, ExtensionsHandler } from 'vs/workbench/contrib/extensions/browser/extensionsQuickOpen'; import { EditorDescriptor, IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/browser/editor'; @@ -70,7 +70,7 @@ Registry.as(Extensions.Quickopen).registerQuickOpenHandler( ExtensionsHandler, ExtensionsHandler.ID, 'ext ', - null, + undefined, localize('extensionsCommands', "Manage Extensions"), true ) @@ -81,7 +81,7 @@ Registry.as(Extensions.Quickopen).registerQuickOpenHandler( GalleryExtensionsHandler, GalleryExtensionsHandler.ID, 'ext install ', - null, + undefined, localize('galleryExtensionsCommands', "Install Gallery Extensions"), true ) diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionsActions.ts index 32eb8cc1048..f063733ef90 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensionsActions.ts @@ -2529,11 +2529,11 @@ export class OpenExtensionsFolderAction extends Action { const extensionsHome = URI.file(this.environmentService.extensionsPath); return Promise.resolve(this.fileService.resolveFile(extensionsHome)).then(file => { - let itemToShow: string; + let itemToShow: URI; if (file.children && file.children.length > 0) { - itemToShow = file.children[0].resource.fsPath; + itemToShow = file.children[0].resource; } else { - itemToShow = extensionsHome.fsPath; + itemToShow = extensionsHome; } return this.windowsService.showItemInFolder(itemToShow); diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionsAutoProfiler.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionsAutoProfiler.ts index 5fb573a33a3..154964ca211 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensionsAutoProfiler.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensionsAutoProfiler.ts @@ -49,7 +49,7 @@ export class ExtensionsAutoProfiler extends Disposable implements IWorkbenchCont if (event.isResponsive && this._session.has(target)) { // stop profiling when responsive again - this._session.get(target).cancel(); + this._session.get(target)!.cancel(); } else if (!event.isResponsive && !this._session.has(target)) { // start profiling if not yet profiling diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionsViewlet.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionsViewlet.ts index d3dfeb19447..215268085ae 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensionsViewlet.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensionsViewlet.ts @@ -114,7 +114,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio return { id, name: viewIdNameMappings[id], - ctor: ExtensionsListView, + ctorDescriptor: { ctor: ExtensionsListView }, when: ContextKeyExpr.and(ContextKeyExpr.has('searchExtensions'), ContextKeyExpr.not('searchInstalledExtensions'), ContextKeyExpr.not('searchBuiltInExtensions'), ContextKeyExpr.not('recommendedExtensions'), ContextKeyExpr.not('groupByServersContext')), weight: 100 }; @@ -127,7 +127,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio return { id, name: viewIdNameMappings[id], - ctor: EnabledExtensionsView, + ctorDescriptor: { ctor: EnabledExtensionsView }, when: ContextKeyExpr.and(ContextKeyExpr.not('searchExtensions'), ContextKeyExpr.has('hasInstalledExtensions')), weight: 40, canToggleVisibility: true, @@ -142,7 +142,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio return { id, name: viewIdNameMappings[id], - ctor: DisabledExtensionsView, + ctorDescriptor: { ctor: DisabledExtensionsView }, when: ContextKeyExpr.and(ContextKeyExpr.not('searchExtensions'), ContextKeyExpr.has('hasInstalledExtensions')), weight: 10, canToggleVisibility: true, @@ -158,7 +158,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio return { id, name: viewIdNameMappings[id], - ctor: ExtensionsListView, + ctorDescriptor: { ctor: ExtensionsListView }, when: ContextKeyExpr.and(ContextKeyExpr.not('searchExtensions'), ContextKeyExpr.not('hasInstalledExtensions')), weight: 60, order: 1 @@ -169,7 +169,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio return [{ id: `server.extensionsList.${server.authority}`, name: server.label, - ctor: GroupByServerExtensionsView, + ctorDescriptor: { ctor: GroupByServerExtensionsView }, when: ContextKeyExpr.has('groupByServersContext'), weight: 100 }]; @@ -183,7 +183,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio return { id, name: viewIdNameMappings[id], - ctor: DefaultRecommendedExtensionsView, + ctorDescriptor: { ctor: DefaultRecommendedExtensionsView }, when: ContextKeyExpr.and(ContextKeyExpr.not('searchExtensions'), ContextKeyExpr.has('defaultRecommendedExtensions')), weight: 40, order: 2, @@ -198,7 +198,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio return { id, name: viewIdNameMappings[id], - ctor: RecommendedExtensionsView, + ctorDescriptor: { ctor: RecommendedExtensionsView }, when: ContextKeyExpr.has('recommendedExtensions'), weight: 50, canToggleVisibility: true, @@ -213,7 +213,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio return { id, name: viewIdNameMappings[id], - ctor: WorkspaceRecommendedExtensionsView, + ctorDescriptor: { ctor: WorkspaceRecommendedExtensionsView }, when: ContextKeyExpr.and(ContextKeyExpr.has('recommendedExtensions'), ContextKeyExpr.has('nonEmptyWorkspace')), weight: 50, canToggleVisibility: true, @@ -226,7 +226,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio return { id, name: viewIdNameMappings[id], - ctor: BuiltInExtensionsView, + ctorDescriptor: { ctor: BuiltInExtensionsView }, when: ContextKeyExpr.has('searchBuiltInExtensions'), weight: 100, canToggleVisibility: true @@ -238,7 +238,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio return { id, name: viewIdNameMappings[id], - ctor: BuiltInThemesExtensionsView, + ctorDescriptor: { ctor: BuiltInThemesExtensionsView }, when: ContextKeyExpr.has('searchBuiltInExtensions'), weight: 100, canToggleVisibility: true @@ -250,7 +250,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio return { id, name: viewIdNameMappings[id], - ctor: BuiltInBasicsExtensionsView, + ctorDescriptor: { ctor: BuiltInBasicsExtensionsView }, when: ContextKeyExpr.has('searchBuiltInExtensions'), weight: 100, canToggleVisibility: true @@ -275,7 +275,7 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio private searchBox: SuggestEnabledInput; private extensionsBox: HTMLElement; private primaryActions: IAction[]; - private secondaryActions: IAction[]; + private secondaryActions: IAction[] | null; private disposables: IDisposable[] = []; private searchViewletState: object; diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionsViews.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionsViews.ts index 4d6d70c410e..696f13d9b52 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensionsViews.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensionsViews.ts @@ -40,10 +40,10 @@ import { alert } from 'vs/base/browser/ui/aria/aria'; 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/contrib/extensions/electron-browser/extensionsUtils'; import { IAction } from 'vs/base/common/actions'; import { ExtensionType } from 'vs/platform/extensions/common/extensions'; import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import product from 'vs/platform/product/node/product'; class ExtensionsViewState extends Disposable implements IExtensionsViewState { @@ -68,7 +68,7 @@ export class ExtensionsListView extends ViewletPanel { private extensionsList: HTMLElement; private badge: CountBadge; protected badgeContainer: HTMLElement; - private list: WorkbenchPagedList; + private list: WorkbenchPagedList | null; constructor( private options: IViewletViewOptions, @@ -133,7 +133,9 @@ export class ExtensionsListView extends ViewletPanel { protected layoutBody(height: number, width: number): void { this.extensionsList.style.height = height + 'px'; - this.list.layout(height, width); + if (this.list) { + this.list.layout(height, width); + } } async show(query: string): Promise> { @@ -168,7 +170,7 @@ export class ExtensionsListView extends ViewletPanel { } count(): number { - return this.list.length; + return this.list ? this.list.length : 0; } protected showEmptyModel(): Promise> { @@ -366,7 +368,8 @@ export class ExtensionsListView extends ViewletPanel { text = query.value.replace(extensionRegex, (m, ext) => { // Get curated keywords - const keywords = getKeywordsForExtension(ext); + const lookup = product.extensionKeywords || {}; + const keywords = lookup[ext] || []; // Get mode name const modeId = this.modeService.getModeIdByFilepathOrFirstLine(`.${ext}`); @@ -727,6 +730,10 @@ export class ExtensionsListView extends ViewletPanel { focus(): void { super.focus(); + if (!this.list) { + return; + } + if (!(this.list.getFocus().length || this.list.getSelection().length)) { this.list.focusNext(); } diff --git a/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts b/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts index 6bfbb3da3c9..dcc921a7168 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts @@ -100,7 +100,7 @@ export class RuntimeExtensionsEditor extends BaseEditor { public static readonly ID: string = 'workbench.editor.runtimeExtensions'; private _list: WorkbenchList | null; - private _profileInfo: IExtensionHostProfile; + private _profileInfo: IExtensionHostProfile | null; private _elements: IRuntimeExtension[] | null; private _extensionsDescriptions: IExtensionDescription[]; @@ -385,7 +385,7 @@ export class RuntimeExtensionsEditor extends BaseEditor { } } - if (this._profileInfo) { + if (this._profileInfo && element.profileInfo) { data.profileTime.textContent = `Profile: ${(element.profileInfo.totalTime / 1000).toFixed(2)}ms`; } else { data.profileTime.textContent = ''; @@ -471,18 +471,18 @@ export class ReportExtensionIssueAction extends Action { private static _label = nls.localize('reportExtensionIssue', "Report Issue"); private readonly _url: string; - private readonly _task: () => Promise; + private readonly _task?: () => Promise; constructor(extension: { description: IExtensionDescription; marketplaceInfo: IExtension; - status: IExtensionsStatus; + status?: IExtensionsStatus; unresponsiveProfile?: IExtensionHostProfile }) { super(ReportExtensionIssueAction._id, ReportExtensionIssueAction._label, 'extension-action report-issue'); this.enabled = extension.marketplaceInfo && extension.marketplaceInfo.type === ExtensionType.User - && Boolean(extension.description.repository) && Boolean(extension.description.repository.url); + && !!extension.description.repository && !!extension.description.repository.url; const { url, task } = ReportExtensionIssueAction._generateNewIssueUrl(extension); this._url = url; @@ -499,11 +499,11 @@ export class ReportExtensionIssueAction extends Action { private static _generateNewIssueUrl(extension: { description: IExtensionDescription; marketplaceInfo: IExtension; - status: IExtensionsStatus; + status?: IExtensionsStatus; unresponsiveProfile?: IExtensionHostProfile }): { url: string, task?: () => Promise } { - let task: () => Promise | undefined; + let task: (() => Promise) | undefined; let baseUrl = extension.marketplaceInfo && extension.marketplaceInfo.type === ExtensionType.User && extension.description.repository ? extension.description.repository.url : undefined; if (!!baseUrl) { baseUrl = `${baseUrl.indexOf('.git') !== -1 ? baseUrl.substr(0, baseUrl.length - 4) : baseUrl}/issues/new/`; @@ -521,10 +521,10 @@ export class ReportExtensionIssueAction extends Action { 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'); + const data = profiler.rewriteAbsolutePaths({ profile: extension.unresponsiveProfile!.data }, 'pii_removed'); profiler.writeProfile(data, path).then(undefined, onUnexpectedError); }; - message = `:warning: Make sure to **attach** this file from your *home*-directory: \`${path}\` :warning:\n\nFind more details here: https://github.com/Microsoft/vscode/wiki/Explain:-extension-causes-high-cpu-load`; + message = `:warning: Make sure to **attach** this file from your *home*-directory:\n:warning:\`${path}\`\n\nFind more details here: https://github.com/Microsoft/vscode/wiki/Explain:-extension-causes-high-cpu-load`; } else { // generic @@ -661,7 +661,7 @@ export class SaveExtensionHostProfileAction extends Action { } const profileInfo = this._extensionHostProfileService.lastProfile; - let dataToWrite: object = profileInfo.data; + let dataToWrite: object = profileInfo ? profileInfo.data : {}; if (this._environmentService.isBuilt) { const profiler = await import('v8-inspect-profiler'); @@ -676,6 +676,6 @@ export class SaveExtensionHostProfileAction extends Action { picked = picked + '.txt'; } - return writeFile(picked, JSON.stringify(profileInfo.data, null, '\t')); + return writeFile(picked, JSON.stringify(profileInfo ? profileInfo.data : {}, null, '\t')); } } diff --git a/src/vs/workbench/contrib/execution/common/execution.ts b/src/vs/workbench/contrib/externalTerminal/common/externalTerminal.ts similarity index 65% rename from src/vs/workbench/contrib/execution/common/execution.ts rename to src/vs/workbench/contrib/externalTerminal/common/externalTerminal.ts index 4be8a75555e..8b7f051b339 100644 --- a/src/vs/workbench/contrib/execution/common/execution.ts +++ b/src/vs/workbench/contrib/externalTerminal/common/externalTerminal.ts @@ -6,10 +6,21 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IProcessEnvironment } from 'vs/base/common/platform'; -export const ITerminalService = createDecorator('nativeTerminalService'); +export const IExternalTerminalService = createDecorator('nativeTerminalService'); -export interface ITerminalService { +export interface IExternalTerminalService { _serviceBrand: any; openTerminal(path: string): void; runInTerminal(title: string, cwd: string, args: string[], env: IProcessEnvironment): Promise; +} + +export interface IExternalTerminalConfiguration { + terminal: { + explorerKind: 'integrated' | 'external', + external: { + linuxExec: string, + osxExec: string, + windowsExec: string + } + }; } \ No newline at end of file diff --git a/src/vs/workbench/contrib/execution/electron-browser/TerminalHelper.scpt b/src/vs/workbench/contrib/externalTerminal/electron-browser/TerminalHelper.scpt similarity index 100% rename from src/vs/workbench/contrib/execution/electron-browser/TerminalHelper.scpt rename to src/vs/workbench/contrib/externalTerminal/electron-browser/TerminalHelper.scpt diff --git a/src/vs/workbench/contrib/execution/electron-browser/execution.contribution.ts b/src/vs/workbench/contrib/externalTerminal/electron-browser/externalTerminal.contribution.ts similarity index 84% rename from src/vs/workbench/contrib/execution/electron-browser/execution.contribution.ts rename to src/vs/workbench/contrib/externalTerminal/electron-browser/externalTerminal.contribution.ts index b7993790f8c..34c5c845166 100644 --- a/src/vs/workbench/contrib/execution/electron-browser/execution.contribution.ts +++ b/src/vs/workbench/contrib/externalTerminal/electron-browser/externalTerminal.contribution.ts @@ -10,13 +10,13 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import * as paths from 'vs/base/common/path'; import { URI as uri } from 'vs/base/common/uri'; -import { ITerminalService } from 'vs/workbench/contrib/execution/common/execution'; +import { IExternalTerminalConfiguration, IExternalTerminalService } from 'vs/workbench/contrib/externalTerminal/common/externalTerminal'; import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { Extensions, IConfigurationRegistry, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; import { ITerminalService as IIntegratedTerminalService, KEYBINDING_CONTEXT_TERMINAL_NOT_FOCUSED } from 'vs/workbench/contrib/terminal/common/terminal'; -import { getDefaultTerminalWindows, getDefaultTerminalLinuxReady, DEFAULT_TERMINAL_OSX, ITerminalConfiguration } from 'vs/workbench/contrib/execution/electron-browser/terminal'; -import { WinTerminalService, MacTerminalService, LinuxTerminalService } from 'vs/workbench/contrib/execution/electron-browser/terminalService'; +import { getDefaultTerminalWindows, getDefaultTerminalLinuxReady, DEFAULT_TERMINAL_OSX } from 'vs/workbench/contrib/externalTerminal/electron-browser/externalTerminal'; +import { WindowsExternalTerminalService, MacExternalTerminalService, LinuxExternalTerminalService } from 'vs/workbench/contrib/externalTerminal/electron-browser/externalTerminalService'; import { IHistoryService } from 'vs/workbench/services/history/common/history'; import { ResourceContextKey } from 'vs/workbench/common/resources'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; @@ -29,11 +29,11 @@ import { distinct } from 'vs/base/common/arrays'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; if (env.isWindows) { - registerSingleton(ITerminalService, WinTerminalService, true); + registerSingleton(IExternalTerminalService, WindowsExternalTerminalService, true); } else if (env.isMacintosh) { - registerSingleton(ITerminalService, MacTerminalService, true); + registerSingleton(IExternalTerminalService, MacExternalTerminalService, true); } else if (env.isLinux) { - registerSingleton(ITerminalService, LinuxTerminalService, true); + registerSingleton(IExternalTerminalService, LinuxExternalTerminalService, true); } getDefaultTerminalLinuxReady().then(defaultTerminalLinux => { @@ -87,13 +87,13 @@ CommandsRegistry.registerCommand({ const editorService = accessor.get(IEditorService); const fileService = accessor.get(IFileService); const integratedTerminalService = accessor.get(IIntegratedTerminalService); - const terminalService = accessor.get(ITerminalService); + const terminalService = accessor.get(IExternalTerminalService); const resources = getMultiSelectedResources(resource, accessor.get(IListService), editorService); return fileService.resolveFiles(resources.map(r => ({ resource: r }))).then(stats => { - const directoriesToOpen = distinct(stats.map(({ stat }) => stat.isDirectory ? stat.resource.fsPath : paths.dirname(stat.resource.fsPath))); + const directoriesToOpen = distinct(stats.filter(data => data.success).map(({ stat }) => stat!.isDirectory ? stat!.resource.fsPath : paths.dirname(stat!.resource.fsPath))); return directoriesToOpen.map(dir => { - if (configurationService.getValue().terminal.explorerKind === 'integrated') { + if (configurationService.getValue().terminal.explorerKind === 'integrated') { const instance = integratedTerminalService.createTerminal({ cwd: dir }, true); if (instance && (resources.length === 1 || !resource || dir === resource.fsPath || dir === paths.dirname(resource.fsPath))) { integratedTerminalService.setActiveInstance(instance); @@ -115,7 +115,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ weight: KeybindingWeight.WorkbenchContrib, handler: (accessor) => { const historyService = accessor.get(IHistoryService); - const terminalService = accessor.get(ITerminalService); + const terminalService = accessor.get(IExternalTerminalService); const root = historyService.getLastActiveWorkspaceRoot(Schemas.file); if (root) { terminalService.openTerminal(root.fsPath); diff --git a/src/vs/workbench/contrib/execution/electron-browser/terminal.ts b/src/vs/workbench/contrib/externalTerminal/electron-browser/externalTerminal.ts similarity index 90% rename from src/vs/workbench/contrib/execution/electron-browser/terminal.ts rename to src/vs/workbench/contrib/externalTerminal/electron-browser/externalTerminal.ts index 64ca19ecb76..ccbfeed874f 100644 --- a/src/vs/workbench/contrib/execution/electron-browser/terminal.ts +++ b/src/vs/workbench/contrib/externalTerminal/electron-browser/externalTerminal.ts @@ -45,14 +45,3 @@ export function getDefaultTerminalWindows(): string { } return _DEFAULT_TERMINAL_WINDOWS; } - -export interface ITerminalConfiguration { - terminal: { - explorerKind: 'integrated' | 'external', - external: { - linuxExec: string, - osxExec: string, - windowsExec: string - } - }; -} diff --git a/src/vs/workbench/contrib/execution/electron-browser/terminalService.ts b/src/vs/workbench/contrib/externalTerminal/electron-browser/externalTerminalService.ts similarity index 82% rename from src/vs/workbench/contrib/execution/electron-browser/terminalService.ts rename to src/vs/workbench/contrib/externalTerminal/electron-browser/externalTerminalService.ts index 0462e10ec86..e463d6893ce 100644 --- a/src/vs/workbench/contrib/execution/electron-browser/terminalService.ts +++ b/src/vs/workbench/contrib/externalTerminal/electron-browser/externalTerminalService.ts @@ -8,9 +8,9 @@ import * as path from 'vs/base/common/path'; import * as processes from 'vs/base/node/processes'; import * as nls from 'vs/nls'; import { assign } from 'vs/base/common/objects'; -import { ITerminalService } from 'vs/workbench/contrib/execution/common/execution'; +import { IExternalTerminalService, IExternalTerminalConfiguration } from 'vs/workbench/contrib/externalTerminal/common/externalTerminal'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ITerminalConfiguration, getDefaultTerminalWindows, getDefaultTerminalLinuxReady, DEFAULT_TERMINAL_OSX } from 'vs/workbench/contrib/execution/electron-browser/terminal'; +import { getDefaultTerminalWindows, getDefaultTerminalLinuxReady, DEFAULT_TERMINAL_OSX } from 'vs/workbench/contrib/externalTerminal/electron-browser/externalTerminal'; import { IProcessEnvironment } from 'vs/base/common/platform'; import { getPathFromAmdModule } from 'vs/base/common/amd'; @@ -21,7 +21,7 @@ enum WinSpawnType { CMDER } -export class WinTerminalService implements ITerminalService { +export class WindowsExternalTerminalService implements IExternalTerminalService { public _serviceBrand: any; private static readonly CMD = 'cmd.exe'; @@ -32,14 +32,14 @@ export class WinTerminalService implements ITerminalService { } public openTerminal(cwd?: string): void { - const configuration = this._configurationService.getValue(); + const configuration = this._configurationService.getValue(); this.spawnTerminal(cp, configuration, processes.getWindowsShell(), cwd); } public runInTerminal(title: string, dir: string, args: string[], envVars: IProcessEnvironment): Promise { - const configuration = this._configurationService.getValue(); + const configuration = this._configurationService.getValue(); const terminalConfig = configuration.terminal.external; const exec = terminalConfig.windowsExec || getDefaultTerminalWindows(); @@ -64,14 +64,14 @@ export class WinTerminalService implements ITerminalService { windowsVerbatimArguments: true }; - const cmd = cp.spawn(WinTerminalService.CMD, cmdArgs, options); + const cmd = cp.spawn(WindowsExternalTerminalService.CMD, cmdArgs, options); cmd.on('error', e); c(undefined); }); } - private spawnTerminal(spawner, configuration: ITerminalConfiguration, command: string, cwd?: string): Promise { + private spawnTerminal(spawner, configuration: IExternalTerminalConfiguration, command: string, cwd?: string): Promise { const terminalConfig = configuration.terminal.external; const exec = terminalConfig.windowsExec || getDefaultTerminalWindows(); const spawnType = this.getSpawnType(exec); @@ -113,7 +113,7 @@ export class WinTerminalService implements ITerminalService { } } -export class MacTerminalService implements ITerminalService { +export class MacExternalTerminalService implements IExternalTerminalService { public _serviceBrand: any; private static readonly OSASCRIPT = '/usr/bin/osascript'; // osascript is the AppleScript interpreter on OS X @@ -123,14 +123,14 @@ export class MacTerminalService implements ITerminalService { ) { } public openTerminal(cwd?: string): void { - const configuration = this._configurationService.getValue(); + const configuration = this._configurationService.getValue(); this.spawnTerminal(cp, configuration, cwd); } public runInTerminal(title: string, dir: string, args: string[], envVars: IProcessEnvironment): Promise { - const configuration = this._configurationService.getValue(); + const configuration = this._configurationService.getValue(); const terminalConfig = configuration.terminal.external; const terminalApp = terminalConfig.osxExec || DEFAULT_TERMINAL_OSX; @@ -142,7 +142,7 @@ export class MacTerminalService implements ITerminalService { // and then launches the program inside that window. const script = terminalApp === DEFAULT_TERMINAL_OSX ? 'TerminalHelper' : 'iTermHelper'; - const scriptpath = getPathFromAmdModule(require, `vs/workbench/contrib/execution/electron-browser/${script}.scpt`); + const scriptpath = getPathFromAmdModule(require, `vs/workbench/contrib/externalTerminal/electron-browser/${script}.scpt`); const osaArgs = [ scriptpath, @@ -169,7 +169,7 @@ export class MacTerminalService implements ITerminalService { } let stderr = ''; - const osa = cp.spawn(MacTerminalService.OSASCRIPT, osaArgs); + const osa = cp.spawn(MacExternalTerminalService.OSASCRIPT, osaArgs); osa.on('error', e); osa.stderr.on('data', (data) => { stderr += data.toString(); @@ -192,7 +192,7 @@ export class MacTerminalService implements ITerminalService { }); } - private spawnTerminal(spawner, configuration: ITerminalConfiguration, cwd?: string): Promise { + private spawnTerminal(spawner, configuration: IExternalTerminalConfiguration, cwd?: string): Promise { const terminalConfig = configuration.terminal.external; const terminalApp = terminalConfig.osxExec || DEFAULT_TERMINAL_OSX; @@ -204,7 +204,7 @@ export class MacTerminalService implements ITerminalService { } } -export class LinuxTerminalService implements ITerminalService { +export class LinuxExternalTerminalService implements IExternalTerminalService { public _serviceBrand: any; private static readonly WAIT_MESSAGE = nls.localize('press.any.key', "Press any key to continue..."); @@ -215,14 +215,14 @@ export class LinuxTerminalService implements ITerminalService { public openTerminal(cwd?: string): void { - const configuration = this._configurationService.getValue(); + const configuration = this._configurationService.getValue(); this.spawnTerminal(cp, configuration, cwd); } public runInTerminal(title: string, dir: string, args: string[], envVars: IProcessEnvironment): Promise { - const configuration = this._configurationService.getValue(); + const configuration = this._configurationService.getValue(); const terminalConfig = configuration.terminal.external; const execPromise = terminalConfig.linuxExec ? Promise.resolve(terminalConfig.linuxExec) : getDefaultTerminalLinuxReady(); @@ -240,7 +240,7 @@ export class LinuxTerminalService implements ITerminalService { termArgs.push('bash'); termArgs.push('-c'); - const bashCommand = `${quote(args)}; echo; read -p "${LinuxTerminalService.WAIT_MESSAGE}" -n1;`; + const bashCommand = `${quote(args)}; echo; read -p "${LinuxExternalTerminalService.WAIT_MESSAGE}" -n1;`; termArgs.push(`''${bashCommand}''`); // wrapping argument in two sets of ' because node is so "friendly" that it removes one set... // merge environment variables into a copy of the process.env @@ -276,7 +276,7 @@ export class LinuxTerminalService implements ITerminalService { }); } - private spawnTerminal(spawner, configuration: ITerminalConfiguration, cwd?: string): Promise { + private spawnTerminal(spawner, configuration: IExternalTerminalConfiguration, cwd?: string): Promise { const terminalConfig = configuration.terminal.external; const execPromise = terminalConfig.linuxExec ? Promise.resolve(terminalConfig.linuxExec) : getDefaultTerminalLinuxReady(); const env = cwd ? { cwd: cwd } : undefined; diff --git a/src/vs/workbench/contrib/execution/electron-browser/iTermHelper.scpt b/src/vs/workbench/contrib/externalTerminal/electron-browser/iTermHelper.scpt similarity index 100% rename from src/vs/workbench/contrib/execution/electron-browser/iTermHelper.scpt rename to src/vs/workbench/contrib/externalTerminal/electron-browser/iTermHelper.scpt diff --git a/src/vs/workbench/contrib/execution/test/electron-browser/terminalService.test.ts b/src/vs/workbench/contrib/externalTerminal/test/electron-browser/externalTerminalService.test.ts similarity index 86% rename from src/vs/workbench/contrib/execution/test/electron-browser/terminalService.test.ts rename to src/vs/workbench/contrib/externalTerminal/test/electron-browser/externalTerminalService.test.ts index 7795082c1fa..8690ad8ae34 100644 --- a/src/vs/workbench/contrib/execution/test/electron-browser/terminalService.test.ts +++ b/src/vs/workbench/contrib/externalTerminal/test/electron-browser/externalTerminalService.test.ts @@ -4,10 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import { deepEqual, equal } from 'assert'; -import { WinTerminalService, LinuxTerminalService, MacTerminalService } from 'vs/workbench/contrib/execution/electron-browser/terminalService'; -import { getDefaultTerminalWindows, getDefaultTerminalLinuxReady, DEFAULT_TERMINAL_OSX } from 'vs/workbench/contrib/execution/electron-browser/terminal'; +import { WindowsExternalTerminalService, LinuxExternalTerminalService, MacExternalTerminalService } from 'vs/workbench/contrib/externalTerminal/electron-browser/externalTerminalService'; +import { getDefaultTerminalWindows, getDefaultTerminalLinuxReady, DEFAULT_TERMINAL_OSX } from 'vs/workbench/contrib/externalTerminal/electron-browser/externalTerminal'; -suite('Execution - TerminalService', () => { +suite('ExternalTerminalService', () => { let mockOnExit: Function; let mockOnError: Function; let mockConfig: any; @@ -42,7 +42,7 @@ suite('Execution - TerminalService', () => { }; } }; - let testService = new WinTerminalService(mockConfig); + let testService = new WindowsExternalTerminalService(mockConfig); (testService).spawnTerminal( mockSpawner, mockConfig, @@ -67,7 +67,7 @@ suite('Execution - TerminalService', () => { } }; mockConfig.terminal.external.windowsExec = undefined; - let testService = new WinTerminalService(mockConfig); + let testService = new WindowsExternalTerminalService(mockConfig); (testService).spawnTerminal( mockSpawner, mockConfig, @@ -91,7 +91,7 @@ suite('Execution - TerminalService', () => { }; } }; - let testService = new WinTerminalService(mockConfig); + let testService = new WindowsExternalTerminalService(mockConfig); (testService).spawnTerminal( mockSpawner, mockConfig, @@ -115,7 +115,7 @@ suite('Execution - TerminalService', () => { return { on: (evt: any) => evt }; } }; - let testService = new WinTerminalService(mockConfig); + let testService = new WindowsExternalTerminalService(mockConfig); (testService).spawnTerminal( mockSpawner, mockConfig, @@ -138,7 +138,7 @@ suite('Execution - TerminalService', () => { }; } }; - let testService = new MacTerminalService(mockConfig); + let testService = new MacExternalTerminalService(mockConfig); (testService).spawnTerminal( mockSpawner, mockConfig, @@ -161,7 +161,7 @@ suite('Execution - TerminalService', () => { } }; mockConfig.terminal.external.osxExec = undefined; - let testService = new MacTerminalService(mockConfig); + let testService = new MacExternalTerminalService(mockConfig); (testService).spawnTerminal( mockSpawner, mockConfig, @@ -184,7 +184,7 @@ suite('Execution - TerminalService', () => { }; } }; - let testService = new LinuxTerminalService(mockConfig); + let testService = new LinuxExternalTerminalService(mockConfig); (testService).spawnTerminal( mockSpawner, mockConfig, @@ -208,7 +208,7 @@ suite('Execution - TerminalService', () => { } }; mockConfig.terminal.external.linuxExec = undefined; - let testService = new LinuxTerminalService(mockConfig); + let testService = new LinuxExternalTerminalService(mockConfig); (testService).spawnTerminal( mockSpawner, mockConfig, diff --git a/src/vs/workbench/contrib/feedback/electron-browser/feedback.ts b/src/vs/workbench/contrib/feedback/electron-browser/feedback.ts index 9175a111ac2..51c206a57f7 100644 --- a/src/vs/workbench/contrib/feedback/electron-browser/feedback.ts +++ b/src/vs/workbench/contrib/feedback/electron-browser/feedback.ts @@ -15,10 +15,10 @@ import { IIntegrityService } from 'vs/workbench/services/integrity/common/integr import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; import { attachButtonStyler, attachStylerCallback } from 'vs/platform/theme/common/styler'; import { editorWidgetBackground, widgetShadow, inputBorder, inputForeground, inputBackground, inputActiveOptionBorder, editorBackground, buttonBackground, contrastBorder, darken } from 'vs/platform/theme/common/colorRegistry'; -import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { IAnchor } from 'vs/base/browser/ui/contextview/contextview'; import { Button } from 'vs/base/browser/ui/button/button'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; export const FEEDBACK_VISIBLE_CONFIG = 'workbench.statusBar.feedback.visible'; @@ -66,7 +66,7 @@ export class FeedbackDropdown extends Dropdown { @ITelemetryService private readonly telemetryService: ITelemetryService, @IIntegrityService private readonly integrityService: IIntegrityService, @IThemeService private readonly themeService: IThemeService, - @IWorkspaceConfigurationService private readonly configurationService: IWorkspaceConfigurationService + @IConfigurationService private readonly configurationService: IConfigurationService ) { super(container, { contextViewProvider: options.contextViewProvider, diff --git a/src/vs/workbench/contrib/feedback/electron-browser/feedbackStatusbarItem.ts b/src/vs/workbench/contrib/feedback/electron-browser/feedbackStatusbarItem.ts index d03f6921014..7a904adc838 100644 --- a/src/vs/workbench/contrib/feedback/electron-browser/feedbackStatusbarItem.ts +++ b/src/vs/workbench/contrib/feedback/electron-browser/feedbackStatusbarItem.ts @@ -12,8 +12,7 @@ import product from 'vs/platform/product/node/product'; import { Themable, STATUS_BAR_FOREGROUND, STATUS_BAR_NO_FOLDER_FOREGROUND, STATUS_BAR_ITEM_HOVER_BACKGROUND } from 'vs/workbench/common/theme'; import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; -import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; -import { IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationChangeEvent, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { clearNode, EventHelper, addClass, removeClass, addDisposableListener } from 'vs/base/browser/dom'; import { localize } from 'vs/nls'; import { Action } from 'vs/base/common/actions'; @@ -62,7 +61,7 @@ export class FeedbackStatusbarItem extends Themable implements IStatusbarItem { @IContextViewService private readonly contextViewService: IContextViewService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @IContextMenuService private readonly contextMenuService: IContextMenuService, - @IWorkspaceConfigurationService private readonly configurationService: IWorkspaceConfigurationService, + @IConfigurationService private readonly configurationService: IConfigurationService, @IThemeService themeService: IThemeService ) { super(themeService); @@ -155,7 +154,7 @@ export class FeedbackStatusbarItem extends Themable implements IStatusbarItem { class HideAction extends Action { constructor( - @IWorkspaceConfigurationService private readonly configurationService: IWorkspaceConfigurationService + @IConfigurationService private readonly configurationService: IConfigurationService ) { super('feedback.hide', localize('hide', "Hide")); } diff --git a/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor.ts b/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor.ts index a928e36712a..854da7251dc 100644 --- a/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor.ts +++ b/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor.ts @@ -57,14 +57,14 @@ export class BinaryFileEditor extends BaseBinaryResourceEditor { private openExternal(resource: URI): void { this.windowsService.openExternal(resource.toString()).then(didOpen => { if (!didOpen) { - return this.windowsService.showItemInFolder(resource.fsPath); + return this.windowsService.showItemInFolder(resource); } return undefined; }); } - getTitle(): string { + getTitle(): string | null { return this.input ? this.input.getName() : nls.localize('binaryFileEditor', "Binary File Viewer"); } } diff --git a/src/vs/workbench/contrib/files/browser/editors/fileEditorTracker.ts b/src/vs/workbench/contrib/files/browser/editors/fileEditorTracker.ts index 5b62bd8c09b..cb15ee33d26 100644 --- a/src/vs/workbench/contrib/files/browser/editors/fileEditorTracker.ts +++ b/src/vs/workbench/contrib/files/browser/editors/fileEditorTracker.ts @@ -13,7 +13,7 @@ import { FileOperationEvent, FileOperation, IFileService, FileChangeType, FileCh import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { Disposable } from 'vs/base/common/lifecycle'; -import { distinct } from 'vs/base/common/arrays'; +import { distinct, coalesce } from 'vs/base/common/arrays'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { isLinux } from 'vs/base/common/platform'; @@ -92,12 +92,12 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut // are visible in any editor. since this is a fast operation in the case nothing has changed, // we tolerate the additional work. distinct( - this.editorService.visibleEditors + coalesce(this.editorService.visibleEditors .map(editorInput => { const resource = toResource(editorInput, { supportSideBySide: true }); return resource ? this.textFileService.models.get(resource) : undefined; - }) - .filter(model => model && !model.isDirty()), + })) + .filter(model => !model.isDirty()), m => m.getResource().toString() ).forEach(model => this.queueModelLoad(model)); } @@ -110,7 +110,7 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut private onFileOperation(e: FileOperationEvent): void { // Handle moves specially when file is opened - if (e.operation === FileOperation.MOVE) { + if (e.operation === FileOperation.MOVE && e.target) { this.handleMovedFileInOpenedEditors(e.resource, e.target.resource); } @@ -276,7 +276,7 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut if (editorResource && resource.toString() === editorResource.toString()) { const control = editor.getControl(); if (isCodeEditor(control)) { - return control.saveViewState(); + return control.saveViewState() || undefined; } } } @@ -300,8 +300,8 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut // // Note: we also consider the added event because it could be that a file was added // and updated right after. - distinct([...e.getUpdated(), ...e.getAdded()] - .map(u => this.textFileService.models.get(u.resource)) + distinct(coalesce([...e.getUpdated(), ...e.getAdded()] + .map(u => this.textFileService.models.get(u.resource))) .filter(model => model && !model.isDirty()), m => m.getResource().toString()) .forEach(model => this.queueModelLoad(model)); } @@ -320,18 +320,19 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut private handleUpdatesToVisibleBinaryEditors(e: FileChangesEvent): void { const editors = this.editorService.visibleControls; editors.forEach(editor => { - const resource = toResource(editor.input, { supportSideBySide: true }); + const resource = editor.input ? toResource(editor.input, { supportSideBySide: true }) : undefined; // Support side-by-side binary editors too let isBinaryEditor = false; if (editor instanceof SideBySideEditor) { - isBinaryEditor = editor.getMasterEditor().getId() === BINARY_FILE_EDITOR_ID; + const masterEditor = editor.getMasterEditor(); + isBinaryEditor = !!masterEditor && masterEditor.getId() === BINARY_FILE_EDITOR_ID; } else { isBinaryEditor = editor.getId() === BINARY_FILE_EDITOR_ID; } // Binary editor that should reload from event - if (resource && isBinaryEditor && (e.contains(resource, FileChangeType.UPDATED) || e.contains(resource, FileChangeType.ADDED))) { + if (resource && editor.input && isBinaryEditor && (e.contains(resource, FileChangeType.UPDATED) || e.contains(resource, FileChangeType.ADDED))) { this.editorService.openEditor(editor.input, { forceReload: true, preserveFocus: true }, editor.group); } }); @@ -339,10 +340,10 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut private handleOutOfWorkspaceWatchers(): void { const visibleOutOfWorkspacePaths = new ResourceMap(); - this.editorService.visibleEditors.map(editorInput => { + coalesce(this.editorService.visibleEditors.map(editorInput => { return toResource(editorInput, { supportSideBySide: true }); - }).filter(resource => { - return !!resource && this.fileService.canHandleResource(resource) && !this.contextService.isInsideWorkspace(resource); + })).filter(resource => { + return this.fileService.canHandleResource(resource) && !this.contextService.isInsideWorkspace(resource); }).forEach(resource => { visibleOutOfWorkspacePaths.set(resource, resource); }); diff --git a/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts b/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts index 8d1c0fbc798..c8241f591cb 100644 --- a/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts +++ b/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts @@ -82,7 +82,7 @@ export class TextFileEditor extends BaseTextEditor { } private updateRestoreViewStateConfiguration(): void { - this.restoreViewState = this.configurationService.getValue(null, 'workbench.editor.restoreViewState'); + this.restoreViewState = this.configurationService.getValue(undefined, 'workbench.editor.restoreViewState'); } getTitle(): string { @@ -185,7 +185,7 @@ export class TextFileEditor extends BaseTextEditor { if ((error).fileOperationResult === FileOperationResult.FILE_NOT_FOUND && isValidBasename(basename(input.getResource()))) { return Promise.reject(createErrorWithActions(toErrorMessage(error), { actions: [ - new Action('workbench.files.action.createMissingFile', nls.localize('createFile', "Create File"), null, true, () => { + new Action('workbench.files.action.createMissingFile', nls.localize('createFile', "Create File"), undefined, true, () => { return this.fileService.updateContent(input.getResource(), '').then(() => this.editorService.openEditor({ resource: input.getResource(), options: { @@ -198,18 +198,18 @@ export class TextFileEditor extends BaseTextEditor { } if ((error).fileOperationResult === FileOperationResult.FILE_EXCEED_MEMORY_LIMIT) { - const memoryLimit = Math.max(MIN_MAX_MEMORY_SIZE_MB, +this.configurationService.getValue(null, 'files.maxMemoryForLargeFilesMB') || FALLBACK_MAX_MEMORY_SIZE_MB); + const memoryLimit = Math.max(MIN_MAX_MEMORY_SIZE_MB, +this.configurationService.getValue(undefined, 'files.maxMemoryForLargeFilesMB') || FALLBACK_MAX_MEMORY_SIZE_MB); return Promise.reject(createErrorWithActions(toErrorMessage(error), { actions: [ - new Action('workbench.window.action.relaunchWithIncreasedMemoryLimit', nls.localize('relaunchWithIncreasedMemoryLimit', "Restart with {0} MB", memoryLimit), null, true, () => { + new Action('workbench.window.action.relaunchWithIncreasedMemoryLimit', nls.localize('relaunchWithIncreasedMemoryLimit', "Restart with {0} MB", memoryLimit), undefined, true, () => { return this.windowsService.relaunch({ addArgs: [ `--max-memory=${memoryLimit}` ] }); }), - new Action('workbench.window.action.configureMemoryLimit', nls.localize('configureMemoryLimit', 'Configure Memory Limit'), null, true, () => { + new Action('workbench.window.action.configureMemoryLimit', nls.localize('configureMemoryLimit', 'Configure Memory Limit'), undefined, true, () => { return this.preferencesService.openGlobalSettings(undefined, { query: 'files.maxMemoryForLargeFilesMB' }); }) ] @@ -228,13 +228,16 @@ export class TextFileEditor extends BaseTextEditor { } private openAsFolder(input: FileEditorInput): void { + if (!this.group) { + return; + } // Since we cannot open a folder, we have to restore the previous input if any and close the editor this.group.closeEditor(this.input).then(() => { // Best we can do is to reveal the folder in the explorer if (this.contextService.isInsideWorkspace(input.getResource())) { - this.viewletService.openViewlet(VIEWLET_ID, true).then(() => { + this.viewletService.openViewlet(VIEWLET_ID).then(() => { this.explorerService.select(input.getResource(), true); }); } diff --git a/src/vs/workbench/contrib/files/browser/explorerViewlet.ts b/src/vs/workbench/contrib/files/browser/explorerViewlet.ts index b8c1d34f383..4ba23bcf745 100644 --- a/src/vs/workbench/contrib/files/browser/explorerViewlet.ts +++ b/src/vs/workbench/contrib/files/browser/explorerViewlet.ts @@ -99,7 +99,7 @@ export class ExplorerViewletViewsContribution extends Disposable implements IWor return { id: OpenEditorsView.ID, name: OpenEditorsView.NAME, - ctor: OpenEditorsView, + ctorDescriptor: { ctor: OpenEditorsView }, order: 0, when: OpenEditorsVisibleCondition, canToggleVisibility: true, @@ -114,7 +114,7 @@ export class ExplorerViewletViewsContribution extends Disposable implements IWor return { id: EmptyView.ID, name: EmptyView.NAME, - ctor: EmptyView, + ctorDescriptor: { ctor: EmptyView }, order: 1, canToggleVisibility: false }; @@ -124,7 +124,7 @@ export class ExplorerViewletViewsContribution extends Disposable implements IWor return { id: ExplorerView.ID, name: localize('folders', "Folders"), - ctor: ExplorerView, + ctorDescriptor: { ctor: ExplorerView }, order: 1, canToggleVisibility: false }; diff --git a/src/vs/workbench/contrib/files/browser/fileActions.ts b/src/vs/workbench/contrib/files/browser/fileActions.ts index 92f15346a27..c8f8f3e9d7b 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.ts @@ -832,7 +832,7 @@ export class ShowActiveFileInExplorer extends Action { } public run(): Promise { - const resource = toResource(this.editorService.activeEditor, { supportSideBySide: true }); + const resource = toResource(this.editorService.activeEditor || null, { supportSideBySide: true }); if (resource) { this.commandService.executeCommand(REVEAL_IN_EXPLORER_COMMAND_ID, resource); } else { @@ -904,7 +904,7 @@ export class ShowOpenedFileInNewWindow extends Action { } public run(): Promise { - const fileResource = toResource(this.editorService.activeEditor, { supportSideBySide: true }); + const fileResource = toResource(this.editorService.activeEditor || null, { supportSideBySide: true }); if (fileResource) { if (this.fileService.canHandleResource(fileResource)) { this.windowService.openWindow([{ uri: fileResource, typeHint: 'file' }], { forceNewWindow: true, forceOpenWorkspaceAsFile: true }); @@ -1007,7 +1007,7 @@ export class CompareWithClipboardAction extends Action { } public run(): Promise { - const resource = toResource(this.editorService.activeEditor, { supportSideBySide: true }); + const resource = toResource(this.editorService.activeEditor || null, { supportSideBySide: true }); if (resource && (this.fileService.canHandleResource(resource) || resource.scheme === Schemas.untitled)) { if (!this.registrationDisposal) { const provider = this.instantiationService.createInstance(ClipboardContentProvider); @@ -1076,7 +1076,7 @@ function openExplorerAndRunAction(accessor: ServicesAccessor, constructor: ICons return explorerPromise.then((explorer: ExplorerViewlet) => { const explorerView = explorer.getExplorerView(); - if (explorerView && explorerView.isBodyVisible()) { + if (explorerView && explorerView.isBodyVisible() && listService.lastFocusedList) { explorerView.focus(); const { stat } = getContext(listService.lastFocusedList); const action = instantationService.createInstance(constructor, () => stat); @@ -1106,6 +1106,10 @@ export const renameHandler = (accessor: ServicesAccessor) => { const listService = accessor.get(IListService); const explorerService = accessor.get(IExplorerService); const textFileService = accessor.get(ITextFileService); + if (!listService.lastFocusedList) { + return; + } + const { stat } = getContext(listService.lastFocusedList); explorerService.setEditable(stat, { @@ -1124,6 +1128,9 @@ export const renameHandler = (accessor: ServicesAccessor) => { export const moveFileToTrashHandler = (accessor: ServicesAccessor) => { const instantationService = accessor.get(IInstantiationService); const listService = accessor.get(IListService); + if (!listService.lastFocusedList) { + return Promise.resolve(); + } const explorerContext = getContext(listService.lastFocusedList); const stats = explorerContext.selection.length > 1 ? explorerContext.selection : [explorerContext.stat]; @@ -1134,6 +1141,9 @@ export const moveFileToTrashHandler = (accessor: ServicesAccessor) => { export const deleteFileHandler = (accessor: ServicesAccessor) => { const instantationService = accessor.get(IInstantiationService); const listService = accessor.get(IListService); + if (!listService.lastFocusedList) { + return Promise.resolve(); + } const explorerContext = getContext(listService.lastFocusedList); const stats = explorerContext.selection.length > 1 ? explorerContext.selection : [explorerContext.stat]; @@ -1143,6 +1153,9 @@ export const deleteFileHandler = (accessor: ServicesAccessor) => { export const copyFileHandler = (accessor: ServicesAccessor) => { const listService = accessor.get(IListService); + if (!listService.lastFocusedList) { + return; + } const explorerContext = getContext(listService.lastFocusedList); const explorerService = accessor.get(IExplorerService); const stats = explorerContext.selection.length > 1 ? explorerContext.selection : [explorerContext.stat]; @@ -1152,6 +1165,9 @@ export const copyFileHandler = (accessor: ServicesAccessor) => { export const cutFileHandler = (accessor: ServicesAccessor) => { const listService = accessor.get(IListService); + if (!listService.lastFocusedList) { + return; + } const explorerContext = getContext(listService.lastFocusedList); const explorerService = accessor.get(IExplorerService); const stats = explorerContext.selection.length > 1 ? explorerContext.selection : [explorerContext.stat]; @@ -1163,6 +1179,9 @@ export const pasteFileHandler = (accessor: ServicesAccessor) => { const instantationService = accessor.get(IInstantiationService); const listService = accessor.get(IListService); const clipboardService = accessor.get(IClipboardService); + if (!listService.lastFocusedList) { + return Promise.resolve(); + } const explorerContext = getContext(listService.lastFocusedList); return sequence(resources.distinctParents(clipboardService.readResources(), r => r).map(toCopy => { diff --git a/src/vs/workbench/contrib/files/browser/fileCommands.ts b/src/vs/workbench/contrib/files/browser/fileCommands.ts index 136bbc9d387..734b84326b6 100644 --- a/src/vs/workbench/contrib/files/browser/fileCommands.ts +++ b/src/vs/workbench/contrib/files/browser/fileCommands.ts @@ -111,7 +111,7 @@ function save( // Save As (or Save untitled with associated path) if (isSaveAs || resource.scheme === Schemas.untitled) { - let encodingOfSource: string; + let encodingOfSource: string | undefined; if (resource.scheme === Schemas.untitled) { encodingOfSource = untitledEditorService.getEncoding(resource); } else if (fileService.canHandleResource(resource)) { @@ -119,17 +119,17 @@ function save( encodingOfSource = textModel && textModel.getEncoding(); // text model can be null e.g. if this is a binary file! } - let viewStateOfSource: IEditorViewState; + let viewStateOfSource: IEditorViewState | null; const activeTextEditorWidget = getCodeEditor(editorService.activeTextEditorWidget); if (activeTextEditorWidget) { - const activeResource = toResource(editorService.activeEditor, { supportSideBySide: true }); + const activeResource = toResource(editorService.activeEditor || null, { supportSideBySide: true }); if (activeResource && (fileService.canHandleResource(activeResource) || resource.scheme === Schemas.untitled) && activeResource.toString() === resource.toString()) { viewStateOfSource = activeTextEditorWidget.saveViewState(); } } // Special case: an untitled file with associated path gets saved directly unless "saveAs" is true - let savePromise: Promise; + let savePromise: Promise; if (!isSaveAs && resource.scheme === Schemas.untitled && untitledEditorService.hasAssociatedFilePath(resource)) { savePromise = textFileService.save(resource, options).then((result) => { if (result) { @@ -152,7 +152,7 @@ function save( return savePromise.then((target) => { if (!target || target.toString() === resource.toString()) { - return undefined; // save canceled or same resource used + return false; // save canceled or same resource used } const replacement: IResourceInput = { @@ -175,7 +175,7 @@ function save( // Pin the active editor if we are saving it const activeControl = editorService.activeControl; const activeEditorResource = activeControl && activeControl.input && activeControl.input.getResource(); - if (activeEditorResource && activeEditorResource.toString() === resource.toString()) { + if (activeControl && activeEditorResource && activeEditorResource.toString() === resource.toString()) { activeControl.group.pinEditor(activeControl.input); } @@ -203,7 +203,7 @@ function saveAll(saveAllArguments: any, editorService: IEditorService, untitledE groupIdToUntitledResourceInput.set(g.id, []); } - groupIdToUntitledResourceInput.get(g.id).push({ + groupIdToUntitledResourceInput.get(g.id)!.push({ encoding: untitledEditorService.getEncoding(resource), resource, options: { @@ -357,9 +357,9 @@ CommandsRegistry.registerCommand({ function revealResourcesInOS(resources: URI[], windowsService: IWindowsService, notificationService: INotificationService, workspaceContextService: IWorkspaceContextService): void { if (resources.length) { - sequence(resources.map(r => () => windowsService.showItemInFolder(r.fsPath))); + sequence(resources.map(r => () => windowsService.showItemInFolder(r))); } else if (workspaceContextService.getWorkspace().folders.length) { - windowsService.showItemInFolder(workspaceContextService.getWorkspace().folders[0].uri.fsPath); + windowsService.showItemInFolder(workspaceContextService.getWorkspace().folders[0].uri); } else { notificationService.info(nls.localize('openFileToReveal', "Open a file first to reveal")); } diff --git a/src/vs/workbench/contrib/files/browser/media/explorerviewlet.css b/src/vs/workbench/contrib/files/browser/media/explorerviewlet.css index a0c0f8de7f4..17b272cef79 100644 --- a/src/vs/workbench/contrib/files/browser/media/explorerviewlet.css +++ b/src/vs/workbench/contrib/files/browser/media/explorerviewlet.css @@ -18,8 +18,8 @@ padding-left: 4px; /* align top level twistie with `Explorer` title label */ } -.explorer-viewlet .monaco-list.highlight .explorer-item:not(.explorer-item-edited), -.explorer-viewlet .monaco-list.highlight .monaco-tl-twistie { +.explorer-viewlet .explorer-folders-view.highlight .monaco-list .explorer-item:not(.explorer-item-edited), +.explorer-viewlet .explorer-folders-view.highlight .monaco-list .monaco-tl-twistie { opacity: 0.3; } diff --git a/src/vs/workbench/contrib/files/browser/saveErrorHandler.ts b/src/vs/workbench/contrib/files/browser/saveErrorHandler.ts index 76ea4afea5f..2da2752ef21 100644 --- a/src/vs/workbench/contrib/files/browser/saveErrorHandler.ts +++ b/src/vs/workbench/contrib/files/browser/saveErrorHandler.ts @@ -9,7 +9,7 @@ import { basename } from 'vs/base/common/resources'; import { Action } from 'vs/base/common/actions'; import { URI } from 'vs/base/common/uri'; import { FileOperationError, FileOperationResult } from 'vs/platform/files/common/files'; -import { ITextFileService, ISaveErrorHandler, ITextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles'; +import { ITextFileService, ISaveErrorHandler, ITextFileEditorModel, IResolvedTextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles'; import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; @@ -314,11 +314,14 @@ export const acceptLocalChangesCommand = (accessor: ServicesAccessor, resource: const modelService = accessor.get(IModelService); const control = editorService.activeControl; + if (!control) { + return; + } const editor = control.input; const group = control.group; resolverService.createModelReference(resource).then(reference => { - const model = reference.object as ITextFileEditorModel; + const model = reference.object as IResolvedTextFileEditorModel; const localModelSnapshot = model.createSnapshot(); clearPendingResolveSaveConflictMessages(); // hide any previously shown message about how to use these actions @@ -350,6 +353,9 @@ export const revertLocalChangesCommand = (accessor: ServicesAccessor, resource: const resolverService = accessor.get(ITextModelService); const control = editorService.activeControl; + if (!control) { + return; + } const editor = control.input; const group = control.group; diff --git a/src/vs/workbench/contrib/files/browser/views/emptyView.ts b/src/vs/workbench/contrib/files/browser/views/emptyView.ts index 40315768e67..89e2def4556 100644 --- a/src/vs/workbench/contrib/files/browser/views/emptyView.ts +++ b/src/vs/workbench/contrib/files/browser/views/emptyView.ts @@ -70,6 +70,9 @@ export class EmptyView extends ViewletPanel { attachButtonStyler(this.button, this.themeService); this.disposables.push(this.button.onDidClick(() => { + if (!this.actionRunner) { + return; + } const actionClass = this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE ? AddRootFolderAction : env.isMacintosh ? OpenFileFolderAction : OpenFolderAction; const action = this.instantiationService.createInstance(actionClass, actionClass.ID, actionClass.LABEL); this.actionRunner.run(action).then(() => { @@ -82,21 +85,25 @@ export class EmptyView extends ViewletPanel { this.disposables.push(new DragAndDropObserver(container, { onDrop: e => { - container.style.backgroundColor = this.themeService.getTheme().getColor(SIDE_BAR_BACKGROUND).toString(); + const color = this.themeService.getTheme().getColor(SIDE_BAR_BACKGROUND); + container.style.backgroundColor = color ? color.toString() : ''; const dropHandler = this.instantiationService.createInstance(ResourcesDropHandler, { allowWorkspaceOpen: true }); dropHandler.handleDrop(e, () => undefined, targetGroup => undefined); }, onDragEnter: (e) => { - container.style.backgroundColor = this.themeService.getTheme().getColor(listDropBackground).toString(); + const color = this.themeService.getTheme().getColor(listDropBackground); + container.style.backgroundColor = color ? color.toString() : ''; }, onDragEnd: () => { - container.style.backgroundColor = this.themeService.getTheme().getColor(SIDE_BAR_BACKGROUND).toString(); + const color = this.themeService.getTheme().getColor(SIDE_BAR_BACKGROUND); + container.style.backgroundColor = color ? color.toString() : ''; }, onDragLeave: () => { - container.style.backgroundColor = this.themeService.getTheme().getColor(SIDE_BAR_BACKGROUND).toString(); + const color = this.themeService.getTheme().getColor(SIDE_BAR_BACKGROUND); + container.style.backgroundColor = color ? color.toString() : ''; }, onDragOver: e => { - e.dataTransfer.dropEffect = 'copy'; + e.dataTransfer!.dropEffect = 'copy'; } })); diff --git a/src/vs/workbench/contrib/files/browser/views/explorerView.ts b/src/vs/workbench/contrib/files/browser/views/explorerView.ts index 2f4cf5df114..94c206c6ebf 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerView.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerView.ts @@ -46,6 +46,9 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag import { IAsyncDataTreeViewState } from 'vs/base/browser/ui/tree/asyncDataTree'; import { FuzzyScore } from 'vs/base/common/filters'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { isMacintosh } from 'vs/base/common/platform'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; export class ExplorerView extends ViewletPanel { static readonly ID: string = 'workbench.explorer.fileView'; @@ -61,7 +64,6 @@ export class ExplorerView extends ViewletPanel { // Refresh is needed on the initial explorer open private shouldRefresh = true; - private setTreeInputPromise = Promise.resolve(undefined); private dragHandler: DelayedDragHandler; private decorationProvider: ExplorerDecorationsProvider; private autoReveal = false; @@ -166,7 +168,7 @@ export class ExplorerView extends ViewletPanel { this.refresh(); })); - this.disposables.push(this.explorerService.onDidChangeRoots(() => this.setTreeInputPromise = this.setTreeInput())); + this.disposables.push(this.explorerService.onDidChangeRoots(() => this.setTreeInput())); this.disposables.push(this.explorerService.onDidChangeItem(e => this.refresh(e))); this.disposables.push(this.explorerService.onDidChangeEditable(async e => { const isEditing = !!this.explorerService.getEditableData(e); @@ -174,13 +176,13 @@ export class ExplorerView extends ViewletPanel { if (isEditing) { await this.tree.expand(e.parent); } else { - DOM.removeClass(this.tree.getHTMLElement(), 'highlight'); + DOM.removeClass(treeContainer, 'highlight'); } await this.refresh(e.parent); if (isEditing) { - DOM.addClass(this.tree.getHTMLElement(), 'highlight'); + DOM.addClass(treeContainer, 'highlight'); this.tree.reveal(e); } else { this.tree.domFocus(); @@ -206,8 +208,7 @@ export class ExplorerView extends ViewletPanel { // If a refresh was requested and we are now visible, run it if (this.shouldRefresh) { this.shouldRefresh = false; - this.setTreeInputPromise = this.setTreeInput(); - await this.setTreeInputPromise; + await this.setTreeInput(); } // Find resource to focus from active editor input if set this.selectActiveFile(false, true); @@ -231,22 +232,21 @@ export class ExplorerView extends ViewletPanel { } focus(): void { - this.setTreeInputPromise.then(() => { - this.tree.domFocus(); - const focused = this.tree.getFocus(); - if (focused.length === 1) { - if (this.autoReveal) { - this.tree.reveal(focused[0], 0.5); - } + this.tree.domFocus(); - const activeFile = this.getActiveFile(); - if (!activeFile && !focused[0].isDirectory) { - // Open the focused element in the editor if there is currently no file opened #67708 - this.editorService.openEditor({ resource: focused[0].resource, options: { preserveFocus: true, revealIfVisible: true } }) - .then(undefined, onUnexpectedError); - } + const focused = this.tree.getFocus(); + if (focused.length === 1) { + if (this.autoReveal) { + this.tree.reveal(focused[0], 0.5); } - }); + + const activeFile = this.getActiveFile(); + if (!activeFile && !focused[0].isDirectory) { + // Open the focused element in the editor if there is currently no file opened #67708 + this.editorService.openEditor({ resource: focused[0].resource, options: { preserveFocus: true, revealIfVisible: true } }) + .then(undefined, onUnexpectedError); + } + } } private selectActiveFile(deselect?: boolean, reveal = this.autoReveal): void { @@ -327,14 +327,9 @@ export class ExplorerView extends ViewletPanel { const shiftDown = e.browserEvent instanceof KeyboardEvent && e.browserEvent.shiftKey; if (selection.length === 1 && !shiftDown) { // Do not react if user is clicking on explorer items which are input placeholders - if (!selection[0].name) { + if (!selection[0].name || selection[0].isDirectory) { // Do not react if user is clicking on explorer items which are input placeholders - return; - } - if (selection[0].isDirectory) { - if (e.browserEvent instanceof KeyboardEvent) { - this.tree.toggleCollapsed(selection[0]); - } + // Do not react if clicking on directories return; } @@ -350,6 +345,17 @@ export class ExplorerView extends ViewletPanel { })); this.disposables.push(this.tree.onContextMenu(e => this.onContextMenu(e))); + this.disposables.push(this.tree.onKeyDown(e => { + const event = new StandardKeyboardEvent(e); + const toggleCollapsed = isMacintosh ? (event.keyCode === KeyCode.DownArrow && event.metaKey) : event.keyCode === KeyCode.Enter; + if (toggleCollapsed) { + const focus = this.tree.getFocus(); + if (focus.length === 1 && focus[0].isDirectory) { + this.tree.toggleCollapsed(focus[0]); + } + } + })); + // save view state on shutdown this.storageService.onWillSaveState(() => { @@ -487,7 +493,7 @@ export class ExplorerView extends ViewletPanel { return promise; } - private getActiveFile(): URI { + private getActiveFile(): URI | undefined { const input = this.editorService.activeEditor; // ignore diff editor inputs (helps to get out of diffing when returning to explorer) diff --git a/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts b/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts index 3b7916fe08f..ba125a48dba 100644 --- a/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts +++ b/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts @@ -28,7 +28,7 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput { private preferredEncoding: string; private forceOpenAsBinary: boolean; private forceOpenAsText: boolean; - private textModelReference: Promise>; + private textModelReference: Promise> | null; private name: string; /** diff --git a/src/vs/workbench/contrib/files/common/explorerService.ts b/src/vs/workbench/contrib/files/common/explorerService.ts index 4db163ecab6..7ce193360b2 100644 --- a/src/vs/workbench/contrib/files/common/explorerService.ts +++ b/src/vs/workbench/contrib/files/common/explorerService.ts @@ -37,7 +37,7 @@ export class ExplorerService implements IExplorerService { private _onDidSelectItem = new Emitter<{ item?: ExplorerItem, reveal?: boolean }>(); private _onDidCopyItems = new Emitter<{ items: ExplorerItem[], cut: boolean, previouslyCutItems: ExplorerItem[] | undefined }>(); private disposables: IDisposable[] = []; - private editableStats = new Map(); + private editable: { stat: ExplorerItem, data: IEditableData } | undefined; private _sortOrder: SortOrder; private cutItems: ExplorerItem[] | undefined; @@ -112,11 +112,10 @@ export class ExplorerService implements IExplorerService { setEditable(stat: ExplorerItem, data: IEditableData | null): void { if (!data) { - this.editableStats.delete(stat); + this.editable = undefined; } else { - this.editableStats.set(stat, data); + this.editable = { stat, data }; } - this._onDidChangeEditable.fire(stat); } @@ -133,11 +132,11 @@ export class ExplorerService implements IExplorerService { } getEditableData(stat: ExplorerItem): IEditableData | undefined { - return this.editableStats.get(stat); + return this.editable && this.editable.stat === stat ? this.editable.data : undefined; } isEditable(stat: ExplorerItem): boolean { - return this.editableStats.has(stat); + return !!this.editable && this.editable.stat === stat; } select(resource: URI, reveal?: boolean): Promise { @@ -266,72 +265,74 @@ export class ExplorerService implements IExplorerService { // be fired first over the other or not at all. setTimeout(() => { // Filter to the ones we care - e = this.filterToViewRelevantEvents(e); - const changedItems: ExplorerItem[] = []; + const shouldRefresh = () => { + e = this.filterToViewRelevantEvents(e); + // Handle added files/folders + const added = e.getAdded(); + if (added.length) { - // Handle added files/folders - const added = e.getAdded(); - if (added.length) { + // Check added: Refresh if added file/folder is not part of resolved root and parent is part of it + const ignoredPaths: { [resource: string]: boolean } = <{ [resource: string]: boolean }>{}; + for (let i = 0; i < added.length; i++) { + const change = added[i]; - // Check added: Refresh if added file/folder is not part of resolved root and parent is part of it - const ignoredPaths: { [resource: string]: boolean } = <{ [resource: string]: boolean }>{}; - for (let i = 0; i < added.length; i++) { - const change = added[i]; + // Find parent + const parent = dirname(change.resource); - // Find parent - const parent = dirname(change.resource); + // Continue if parent was already determined as to be ignored + if (ignoredPaths[parent.toString()]) { + continue; + } - // Continue if parent was already determined as to be ignored - if (ignoredPaths[parent.toString()]) { - continue; - } + // Compute if parent is visible and added file not yet part of it + const parentStat = this.model.findClosest(parent); + if (parentStat && parentStat.isDirectoryResolved && !this.model.findClosest(change.resource)) { + return true; + } - // Compute if parent is visible and added file not yet part of it - const parentStat = this.model.findClosest(parent); - if (parentStat && parentStat.isDirectoryResolved && !this.model.findClosest(change.resource)) { - changedItems.push(parentStat); - } - - // Keep track of path that can be ignored for faster lookup - if (!parentStat || !parentStat.isDirectoryResolved) { - ignoredPaths[parent.toString()] = true; + // Keep track of path that can be ignored for faster lookup + if (!parentStat || !parentStat.isDirectoryResolved) { + ignoredPaths[parent.toString()] = true; + } } } - } - // Handle deleted files/folders - const deleted = e.getDeleted(); - if (deleted.length) { + // Handle deleted files/folders + const deleted = e.getDeleted(); + if (deleted.length) { - // Check deleted: Refresh if deleted file/folder part of resolved root - for (let j = 0; j < deleted.length; j++) { - const del = deleted[j]; - const item = this.model.findClosest(del.resource); - if (item && item.parent) { - changedItems.push(item.parent); + // Check deleted: Refresh if deleted file/folder part of resolved root + for (let j = 0; j < deleted.length; j++) { + const del = deleted[j]; + const item = this.model.findClosest(del.resource); + if (item && item.parent) { + return true; + } } } - } - // Handle updated files/folders if we sort by modified - if (this._sortOrder === SortOrderConfiguration.MODIFIED) { - const updated = e.getUpdated(); + // Handle updated files/folders if we sort by modified + if (this._sortOrder === SortOrderConfiguration.MODIFIED) { + const updated = e.getUpdated(); - // Check updated: Refresh if updated file/folder part of resolved root - for (let j = 0; j < updated.length; j++) { - const upd = updated[j]; - const item = this.model.findClosest(upd.resource); + // Check updated: Refresh if updated file/folder part of resolved root + for (let j = 0; j < updated.length; j++) { + const upd = updated[j]; + const item = this.model.findClosest(upd.resource); - if (item && item.parent) { - changedItems.push(item.parent); + if (item && item.parent) { + return true; + } } } - } - changedItems.forEach(item => { - item.forgetChildren(); - this._onDidChangeItem.fire(item); - }); + return false; + }; + + if (shouldRefresh()) { + this.roots.forEach(r => r.forgetChildren()); + this._onDidChangeItem.fire(undefined); + } }, ExplorerService.EXPLORER_FILE_CHANGES_REACT_DELAY); } diff --git a/src/vs/workbench/contrib/html/common/htmlInput.ts b/src/vs/workbench/contrib/html/common/htmlInput.ts deleted file mode 100644 index e70b08d999a..00000000000 --- a/src/vs/workbench/contrib/html/common/htmlInput.ts +++ /dev/null @@ -1,32 +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 { URI } from 'vs/base/common/uri'; -import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; -import { ITextModelService } from 'vs/editor/common/services/resolverService'; -import { IHashService } from 'vs/workbench/services/hash/common/hashService'; - -export interface HtmlInputOptions { - readonly allowScripts?: boolean; - readonly allowSvgs?: boolean; - readonly svgWhiteList?: string[]; -} - -export function areHtmlInputOptionsEqual(left: HtmlInputOptions, right: HtmlInputOptions) { - return left.allowScripts === right.allowScripts && left.allowSvgs === right.allowSvgs; -} - -export class HtmlInput extends ResourceEditorInput { - constructor( - name: string, - description: string, - resource: URI, - public readonly options: HtmlInputOptions, - @ITextModelService textModelResolverService: ITextModelService, - @IHashService hashService: IHashService - ) { - super(name, description, resource, textModelResolverService, hashService); - } -} diff --git a/src/vs/workbench/contrib/html/electron-browser/html.contribution.ts b/src/vs/workbench/contrib/html/electron-browser/html.contribution.ts deleted file mode 100644 index 693109a0624..00000000000 --- a/src/vs/workbench/contrib/html/electron-browser/html.contribution.ts +++ /dev/null @@ -1,81 +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 { URI } from 'vs/base/common/uri'; -import { localize } from 'vs/nls'; -import { CommandsRegistry } from 'vs/platform/commands/common/commands'; -import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { EditorViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/shared/editor'; -import { HtmlInput, HtmlInputOptions } from '../common/htmlInput'; -import { HtmlPreviewPart } from './htmlPreviewPart'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; -import { IEditorGroupsService, IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions'; -import { IEditorRegistry, EditorDescriptor, Extensions as EditorExtensions } from 'vs/workbench/browser/editor'; -import { registerWebViewCommands } from 'vs/workbench/contrib/webview/electron-browser/webview.contribution'; - -// --- Register Editor - -(Registry.as(EditorExtensions.Editors)).registerEditor(new EditorDescriptor( - HtmlPreviewPart, - HtmlPreviewPart.ID, - localize('html.editor.label', "Html Preview")), - [new SyncDescriptor(HtmlInput)]); - -// --- Register Commands - -CommandsRegistry.registerCommand('_workbench.previewHtml', function ( - accessor: ServicesAccessor, - resource: URI | string, - position?: EditorViewColumn, - label?: string -) { - const uri = resource instanceof URI ? resource : URI.parse(resource); - label = label || uri.fsPath; - - let input: HtmlInput | undefined; - - const editorGroupService = accessor.get(IEditorGroupsService); - - let targetGroup: IEditorGroup = editorGroupService.getGroup(viewColumnToEditorGroup(editorGroupService, position)); - if (!targetGroup) { - targetGroup = editorGroupService.activeGroup; - } - - // Find already opened HTML input if any - if (targetGroup) { - const editors = targetGroup.editors; - for (const editor of editors) { - const editorResource = editor.getResource(); - if (editor instanceof HtmlInput && editorResource && editorResource.toString() === resource.toString()) { - input = editor; - break; - } - } - } - - const extensionsWorkbenchService = accessor.get(IExtensionsWorkbenchService); - - const inputOptions: HtmlInputOptions = { - allowScripts: true, - allowSvgs: true, - svgWhiteList: extensionsWorkbenchService.allowedBadgeProviders - }; - - // Otherwise, create new input and open it - if (!input) { - input = accessor.get(IInstantiationService).createInstance(HtmlInput, label, '', uri, inputOptions); - } else { - input.setName(label); // make sure to use passed in label - } - - return accessor.get(IEditorService) - .openEditor(input, { pinned: true }, viewColumnToEditorGroup(editorGroupService, position)) - .then(editor => true); -}); - -registerWebViewCommands(HtmlPreviewPart.ID); \ No newline at end of file diff --git a/src/vs/workbench/contrib/html/electron-browser/htmlPreviewPart.ts b/src/vs/workbench/contrib/html/electron-browser/htmlPreviewPart.ts deleted file mode 100644 index 8f86431edbb..00000000000 --- a/src/vs/workbench/contrib/html/electron-browser/htmlPreviewPart.ts +++ /dev/null @@ -1,256 +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 { localize } from 'vs/nls'; -import { Disposable, IDisposable, dispose, IReference } from 'vs/base/common/lifecycle'; -import { EditorOptions, EditorInput, IEditorMemento } from 'vs/workbench/common/editor'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel'; -import { HtmlInput, HtmlInputOptions, areHtmlInputOptionsEqual } from 'vs/workbench/contrib/html/common/htmlInput'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { ITextModelService, ITextEditorModel } from 'vs/editor/common/services/resolverService'; -import { Parts, IPartService } from 'vs/workbench/services/part/common/partService'; -import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IStorageService } from 'vs/platform/storage/common/storage'; -import { Dimension } from 'vs/base/browser/dom'; -import { BaseWebviewEditor } from 'vs/workbench/contrib/webview/electron-browser/baseWebviewEditor'; -import { WebviewElement, WebviewContentOptions } from 'vs/workbench/contrib/webview/electron-browser/webviewElement'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IEditorGroupsService, IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { CancellationToken } from 'vs/base/common/cancellation'; -import { Event, Emitter } from 'vs/base/common/event'; - -export interface HtmlPreviewEditorViewState { - scrollYPercentage: number; -} - -/** - * An implementation of editor for showing HTML content in an IFrame by leveraging the HTML input. - */ -export class HtmlPreviewPart extends BaseWebviewEditor { - - static readonly ID: string = 'workbench.editor.htmlPreviewPart'; - static class: string = 'htmlPreviewPart'; - - private _webviewDisposables: IDisposable[]; - - private _modelRef?: IReference; - public get model() { return this._modelRef ? this._modelRef.object.textEditorModel : undefined; } - private _modelChangeSubscription = Disposable.None; - private _themeChangeSubscription = Disposable.None; - - private _content: HTMLElement; - private _scrollYPercentage: number = 0; - - private editorMemento: IEditorMemento; - - private readonly _onDidFocusWebview = this._register(new Emitter()); - public get onDidFocus(): Event { return this._onDidFocusWebview.event; } - - constructor( - @ITelemetryService telemetryService: ITelemetryService, - @IThemeService themeService: IThemeService, - @IContextKeyService contextKeyService: IContextKeyService, - @IOpenerService private readonly _openerService: IOpenerService, - @IPartService private readonly _partService: IPartService, - @IStorageService readonly _storageService: IStorageService, - @ITextModelService private readonly _textModelResolverService: ITextModelService, - @IInstantiationService private readonly _instantiationService: IInstantiationService, - @IEditorGroupsService readonly editorGroupService: IEditorGroupsService - ) { - super(HtmlPreviewPart.ID, telemetryService, themeService, contextKeyService, _storageService); - - this.editorMemento = this.getEditorMemento(editorGroupService, this.viewStateStorageKey); - } - - dispose(): void { - // remove from dom - this._webviewDisposables = dispose(this._webviewDisposables); - - // unhook listeners - this._themeChangeSubscription.dispose(); - this._modelChangeSubscription.dispose(); - - // dispose model ref - dispose(this._modelRef); - super.dispose(); - } - - protected createEditor(parent: HTMLElement): void { - this._content = document.createElement('div'); - this._content.style.position = 'absolute'; - this._content.classList.add(HtmlPreviewPart.class); - parent.appendChild(this._content); - } - - private get webview(): WebviewElement { - if (!this._webview) { - let webviewOptions: WebviewContentOptions = {}; - if (this.input && this.input instanceof HtmlInput) { - webviewOptions = this.input.options; - } - - this._webview = this._instantiationService.createInstance(WebviewElement, - this._partService.getContainer(Parts.EDITOR_PART), - { - useSameOriginForRoot: true - }, - webviewOptions); - this._webview.mountTo(this._content); - - if (this.input && this.input instanceof HtmlInput) { - const state = this.loadHTMLPreviewViewState(this.input); - this._scrollYPercentage = state ? state.scrollYPercentage : 0; - this.webview.initialScrollProgress = this._scrollYPercentage; - - const resourceUri = this.input.getResource(); - this.webview.baseUrl = resourceUri.toString(true); - } - this._webviewDisposables = [ - this._webview, - this._webview.onDidClickLink(uri => this._openerService.open(uri)), - this._webview.onDidScroll(data => { - this._scrollYPercentage = data.scrollYPercentage; - }), - ]; - - this._register(this._webview.onDidFocus(() => this._onDidFocusWebview.fire())); - } - return this._webview; - } - - protected setEditorVisible(visible: boolean, group: IEditorGroup): void { - this._doSetVisible(visible); - super.setEditorVisible(visible, group); - } - - private _doSetVisible(visible: boolean): void { - if (!visible) { - this._themeChangeSubscription.dispose(); - this._modelChangeSubscription.dispose(); - this._webviewDisposables = dispose(this._webviewDisposables); - this._webview = undefined; - } else { - this._themeChangeSubscription = this.themeService.onThemeChange(this.onThemeChange.bind(this)); - - if (this._hasValidModel()) { - this._modelChangeSubscription = this.model!.onDidChangeContent(() => this.webview.contents = this.model!.getLinesContent().join('\n')); - this.webview.contents = this.model!.getLinesContent().join('\n'); - } - } - } - - private _hasValidModel(): boolean { - return !!(this._modelRef && this.model && !this.model.isDisposed()); - } - - public layout(dimension: Dimension): void { - const { width, height } = dimension; - this._content.style.width = `${width}px`; - this._content.style.height = `${height}px`; - - super.layout(dimension); - } - - public clearInput(): void { - if (this.input instanceof HtmlInput) { - this.saveHTMLPreviewViewState(this.input, { - scrollYPercentage: this._scrollYPercentage - }); - } - dispose(this._modelRef); - this._modelRef = undefined; - super.clearInput(); - } - - protected saveState(): void { - if (this.input instanceof HtmlInput) { - this.saveHTMLPreviewViewState(this.input, { - scrollYPercentage: this._scrollYPercentage - }); - } - - super.saveState(); - } - - public sendMessage(data: any): void { - this.webview.sendMessage(data); - } - - public setInput(input: EditorInput, options: EditorOptions, token: CancellationToken): Promise { - - if (this.input && this.input.matches(input) && this._hasValidModel() && this.input instanceof HtmlInput && input instanceof HtmlInput && areHtmlInputOptionsEqual(this.input.options, input.options)) { - return Promise.resolve(undefined); - } - - let oldOptions: HtmlInputOptions | undefined = undefined; - - if (this.input instanceof HtmlInput) { - oldOptions = this.input.options; - this.saveHTMLPreviewViewState(this.input, { - scrollYPercentage: this._scrollYPercentage - }); - } - - if (this._modelRef) { - this._modelRef.dispose(); - } - this._modelChangeSubscription.dispose(); - - if (!(input instanceof HtmlInput)) { - return Promise.reject(new Error('Invalid input')); - } - - return super.setInput(input, options, token).then(() => { - const resourceUri = input.getResource(); - return this._textModelResolverService.createModelReference(resourceUri).then(ref => { - if (token.isCancellationRequested) { - return undefined; - } - - const model = ref.object; - if (model instanceof BaseTextEditorModel) { - this._modelRef = ref; - } - - if (!this.model) { - return Promise.reject(new Error(localize('html.voidInput', "Invalid editor input."))); - } - - if (oldOptions && !areHtmlInputOptionsEqual(oldOptions, input.options)) { - this._doSetVisible(false); - } - - this._modelChangeSubscription = this.model.onDidChangeContent(() => { - if (this.model) { - this._scrollYPercentage = 0; - this.webview.contents = this.model.getLinesContent().join('\n'); - } - }); - const state = this.loadHTMLPreviewViewState(input); - this._scrollYPercentage = state ? state.scrollYPercentage : 0; - this.webview.baseUrl = resourceUri.toString(true); - this.webview.options = input.options; - this.webview.contents = this.model.getLinesContent().join('\n'); - this.webview.initialScrollProgress = this._scrollYPercentage; - return undefined; - }); - }); - } - - - private get viewStateStorageKey(): string { - return this.getId() + '.editorViewState'; - } - - private saveHTMLPreviewViewState(input: HtmlInput, editorViewState: HtmlPreviewEditorViewState): void { - this.editorMemento.saveEditorState(this.group!, input, editorViewState); - } - - private loadHTMLPreviewViewState(input: HtmlInput): HtmlPreviewEditorViewState | undefined { - return this.editorMemento.loadEditorState(this.group!, input); - } -} diff --git a/src/vs/workbench/contrib/localizations/browser/localizationsActions.ts b/src/vs/workbench/contrib/localizations/browser/localizationsActions.ts index 62f41d1a3fd..bbd759cb98d 100644 --- a/src/vs/workbench/contrib/localizations/browser/localizationsActions.ts +++ b/src/vs/workbench/contrib/localizations/browser/localizationsActions.ts @@ -5,50 +5,83 @@ import { localize } from 'vs/nls'; import { Action } from 'vs/base/common/actions'; -import { IFileService } from 'vs/platform/files/common/files'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { IEditor } from 'vs/workbench/common/editor'; import { join } from 'vs/base/common/path'; import { URI } from 'vs/base/common/uri'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { ILocalizationsService, LanguageType } from 'vs/platform/localizations/common/localizations'; +import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; +import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing'; +import { IWindowsService } from 'vs/platform/windows/common/windows'; +import { INotificationService } from 'vs/platform/notification/common/notification'; import { language } from 'vs/base/common/platform'; -import { ILabelService } from 'vs/platform/label/common/label'; +import { firstIndex } from 'vs/base/common/arrays'; +import { IExtensionsViewlet, VIEWLET_ID as EXTENSIONS_VIEWLET_ID } from 'vs/workbench/contrib/extensions/common/extensions'; +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; export class ConfigureLocaleAction extends Action { public static readonly ID = 'workbench.action.configureLocale'; public static readonly LABEL = localize('configureLocale', "Configure Display Language"); - private static DEFAULT_CONTENT: string = [ - '{', - `\t// ${localize('displayLanguage', 'Defines VS Code\'s display language.')}`, - `\t// ${localize('doc', 'See {0} for a list of supported languages.', 'https://go.microsoft.com/fwlink/?LinkId=761051')}`, - `\t`, - `\t"locale":"${language}" // ${localize('restart', 'Changes will not take effect until VS Code has been restarted.')}`, - '}' - ].join('\n'); - constructor(id: string, label: string, - @IFileService private readonly fileService: IFileService, @IEnvironmentService private readonly environmentService: IEnvironmentService, - @IEditorService private readonly editorService: IEditorService, - @ILabelService private readonly labelService: ILabelService + @ILocalizationsService private readonly localizationService: ILocalizationsService, + @IQuickInputService private readonly quickInputService: IQuickInputService, + @IJSONEditingService private readonly jsonEditingService: IJSONEditingService, + @IWindowsService private readonly windowsService: IWindowsService, + @INotificationService private readonly notificationService: INotificationService, + @IViewletService private readonly viewletService: IViewletService, + @IDialogService private readonly dialogService: IDialogService ) { super(id, label); } - public run(event?: any): Promise { - const file = URI.file(join(this.environmentService.appSettingsHome, 'locale.json')); - return this.fileService.resolveFile(file).then(undefined, (error) => { - return this.fileService.createFile(file, ConfigureLocaleAction.DEFAULT_CONTENT); - }).then((stat): Promise | undefined => { - if (!stat) { - return undefined; + private async getLanguageOptions(): Promise { + // Contributed languages are those installed via extension packs, so does not include English + const availableLanguages = ['en', ...await this.localizationService.getLanguageIds(LanguageType.Contributed)]; + availableLanguages.sort(); + + return availableLanguages + .map(language => { return { label: language }; }) + .concat({ label: localize('installAdditionalLanguages', "Install additional languages...") }); + } + + public async run(event?: any): Promise { + const languageOptions = await this.getLanguageOptions(); + const currentLanguageIndex = firstIndex(languageOptions, l => l.label === language); + + try { + const selectedLanguage = await this.quickInputService.pick(languageOptions, + { + canPickMany: false, + placeHolder: localize('chooseDisplayLanguage', "Select Display Language"), + activeItem: languageOptions[currentLanguageIndex] + }); + + if (selectedLanguage === languageOptions[languageOptions.length - 1]) { + return this.viewletService.openViewlet(EXTENSIONS_VIEWLET_ID, true) + .then((viewlet: IExtensionsViewlet) => { + viewlet.search('@category:"language packs"'); + viewlet.focus(); + }); } - return this.editorService.openEditor({ - resource: stat.resource - }); - }, (error) => { - throw new Error(localize('fail.createSettings', "Unable to create '{0}' ({1}).", this.labelService.getUriLabel(file, { relative: true }), error)); - }); + + if (selectedLanguage) { + const file = URI.file(join(this.environmentService.appSettingsHome, 'locale.json')); + await this.jsonEditingService.write(file, { key: 'locale', value: selectedLanguage.label }, true); + const restart = await this.dialogService.confirm({ + type: 'info', + message: localize('relaunchDisplayLanguageMessage', "A restart is required for the change in display language to take effect."), + detail: localize('relaunchDisplayLanguageDetail', "Press the restart button to restart {0} and change the display language.", this.environmentService.appNameLong), + primaryButton: localize('restart', "&&Restart") + }); + + if (restart.confirmed) { + this.windowsService.relaunch({}); + } + } + } catch (e) { + this.notificationService.error(e); + } } } diff --git a/src/vs/workbench/contrib/logs/common/logsActions.ts b/src/vs/workbench/contrib/logs/common/logsActions.ts index faa6e33ec1e..56b54a4c89c 100644 --- a/src/vs/workbench/contrib/logs/common/logsActions.ts +++ b/src/vs/workbench/contrib/logs/common/logsActions.ts @@ -10,6 +10,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' import { IWindowsService } from 'vs/platform/windows/common/windows'; import { ILogService, LogLevel, DEFAULT_LOG_LEVEL } from 'vs/platform/log/common/log'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; +import { URI } from 'vs/base/common/uri'; export class OpenLogsFolderAction extends Action { @@ -24,7 +25,7 @@ export class OpenLogsFolderAction extends Action { } run(): Promise { - return this.windowsService.showItemInFolder(join(this.environmentService.logsPath, 'main.log')); + return this.windowsService.showItemInFolder(URI.file(join(this.environmentService.logsPath, 'main.log'))); } } diff --git a/src/vs/workbench/contrib/markers/browser/markers.contribution.ts b/src/vs/workbench/contrib/markers/browser/markers.contribution.ts index 7d759e601b8..5bd59feaed1 100644 --- a/src/vs/workbench/contrib/markers/browser/markers.contribution.ts +++ b/src/vs/workbench/contrib/markers/browser/markers.contribution.ts @@ -3,17 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CommandsRegistry, ICommandHandler } from 'vs/platform/commands/common/commands'; -import { ContextKeyExpr, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import 'vs/workbench/contrib/markers/browser/markersFileDecorations'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; -import { KeybindingsRegistry, KeybindingWeight, IKeybindings } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { localize } from 'vs/nls'; import { Marker, RelatedInformation } from 'vs/workbench/contrib/markers/browser/markersModel'; import { MarkersPanel } from 'vs/workbench/contrib/markers/browser/markersPanel'; -import { MenuId, MenuRegistry, SyncActionDescriptor, ILocalizedString } from 'vs/platform/actions/common/actions'; +import { MenuId, MenuRegistry, SyncActionDescriptor, registerAction } from 'vs/platform/actions/common/actions'; import { PanelRegistry, Extensions as PanelExtensions, PanelDescriptor } from 'vs/workbench/browser/panel'; import { Registry } from 'vs/platform/registry/common/platform'; import { ToggleMarkersPanelAction, ShowProblemsPanelAction } from 'vs/workbench/contrib/markers/browser/markersPanelActions'; @@ -23,9 +23,8 @@ import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } fr import { IMarkersWorkbenchService, MarkersWorkbenchService, ActivityUpdater } from 'vs/workbench/contrib/markers/browser/markers'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; - -import './markersFileDecorations'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { ActivePanelContext } from 'vs/workbench/common/panel'; registerSingleton(IMarkersWorkbenchService, MarkersWorkbenchService, false); @@ -182,7 +181,7 @@ registerAction({ category: localize('problems', "Problems"), menu: { menuId: MenuId.CommandPalette, - when: new RawContextKey('activePanel', Constants.MARKERS_PANEL_ID) + when: ActivePanelContext.isEqualTo(Constants.MARKERS_PANEL_ID) } }); registerAction({ @@ -198,7 +197,7 @@ registerAction({ category: localize('problems', "Problems"), menu: { menuId: MenuId.CommandPalette, - when: new RawContextKey('activePanel', Constants.MARKERS_PANEL_ID) + when: ActivePanelContext.isEqualTo(Constants.MARKERS_PANEL_ID) } }); @@ -246,64 +245,6 @@ function focusProblemsFilter(panelService: IPanelService) { } } -interface IActionDescriptor { - id: string; - handler: ICommandHandler; - - // ICommandUI - title?: ILocalizedString; - category?: string; - f1?: boolean; - - // - menu?: { - menuId: MenuId, - when?: ContextKeyExpr; - group?: string; - }; - - // - keybinding?: { - when?: ContextKeyExpr; - weight?: number; - keys: IKeybindings; - }; -} - -function registerAction(desc: IActionDescriptor) { - - const { id, handler, title, category, menu, keybinding } = desc; - - // 1) register as command - CommandsRegistry.registerCommand(id, handler); - - // 2) menus - let command = { id, title, category }; - if (menu) { - let { menuId, when, group } = menu; - MenuRegistry.appendMenuItem(menuId, { - command, - when, - group - }); - } - - // 3) keybindings - if (keybinding) { - let { when, weight, keys } = keybinding; - KeybindingsRegistry.registerKeybindingRule({ - id, - when, - weight, - primary: keys.primary, - secondary: keys.secondary, - linux: keys.linux, - mac: keys.mac, - win: keys.win - }); - } -} - MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { group: '4_panels', command: { diff --git a/src/vs/workbench/contrib/markers/browser/markersPanel.ts b/src/vs/workbench/contrib/markers/browser/markersPanel.ts index 435e6ed6500..3c7a8de1582 100644 --- a/src/vs/workbench/contrib/markers/browser/markersPanel.ts +++ b/src/vs/workbench/contrib/markers/browser/markersPanel.ts @@ -187,7 +187,7 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { public openFileAtElement(element: any, preserveFocus: boolean, sideByside: boolean, pinned: boolean): boolean { const { resource, selection, event, data } = element instanceof Marker ? { resource: element.resource, selection: element.range, event: 'problems.selectDiagnostic', data: this.getTelemetryData(element.marker) } : element instanceof RelatedInformation ? { resource: element.raw.resource, selection: element.raw, event: 'problems.selectRelatedInformation', data: this.getTelemetryData(element.marker) } : { resource: null, selection: null, event: null, data: null }; - if (resource && selection) { + if (resource && selection && event) { /* __GDPR__ "problems.selectDiagnostic" : { "source": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, @@ -465,7 +465,7 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { private onSelected(): void { let selection = this.tree.getSelection(); if (selection && selection.length > 0) { - this.lastSelectedRelativeTop = this.tree.getRelativeTop(selection[0]); + this.lastSelectedRelativeTop = this.tree.getRelativeTop(selection[0]) || 0; } } @@ -612,7 +612,8 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { } private onContextMenu(e: ITreeContextMenuEvent): void { - if (!e.element) { + const element = e.element; + if (!element) { return; } @@ -620,8 +621,8 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { e.browserEvent.stopPropagation(); this.contextMenuService.showContextMenu({ - getAnchor: () => e.anchor, - getActions: () => this.getMenuActions(e.element), + getAnchor: () => e.anchor!, + getActions: () => this.getMenuActions(element), getActionItem: (action) => { const keybinding = this.keybindingService.lookupKeybinding(action.id); if (keybinding) { @@ -665,7 +666,7 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { return result; } - public getFocusElement(): TreeElement { + public getFocusElement() { return this.tree.getFocus()[0]; } diff --git a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts index c7e69cc7584..b61a76f1283 100644 --- a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts +++ b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts @@ -67,7 +67,7 @@ export class MarkersTreeAccessibilityProvider implements IAccessibilityProvider< constructor(@ILabelService private readonly labelService: ILabelService) { } - public getAriaLabel(element: TreeElement): string { + public getAriaLabel(element: TreeElement): string | null { if (element instanceof ResourceMarkers) { const path = this.labelService.getUriLabel(element.resource, { relative: true }) || element.resource.fsPath; return Messages.MARKERS_TREE_ARIA_LABEL_RESOURCE(element.markers.length, element.name, paths.dirname(path)); @@ -262,7 +262,7 @@ class MarkerWidget extends Disposable { this._register(toDisposable(() => this.disposables = dispose(this.disposables))); } - render(element: Marker, filterData: MarkerFilterData): void { + render(element: Marker, filterData: MarkerFilterData | undefined): void { this.actionBar.clear(); this.multilineActionbar.clear(); if (this.disposables.length) { @@ -310,7 +310,7 @@ class MarkerWidget extends Disposable { this.multilineActionbar.push([action], { icon: true, label: false }); } - private renderMessageAndDetails(element: Marker, filterData: MarkerFilterData) { + private renderMessageAndDetails(element: Marker, filterData: MarkerFilterData | undefined) { const { marker, lines } = element; const viewState = this.markersViewModel.getViewModel(element); const multiline = !viewState || viewState.multiline; @@ -330,7 +330,7 @@ class MarkerWidget extends Disposable { this.renderDetails(marker, filterData, multiline ? lastLineElement : this.messageAndDetailsContainer); } - private renderDetails(marker: IMarker, filterData: MarkerFilterData, parent: HTMLElement): void { + private renderDetails(marker: IMarker, filterData: MarkerFilterData | undefined, parent: HTMLElement): void { dom.addClass(parent, 'details-container'); const sourceMatches = filterData && filterData.sourceMatches || []; const codeMatches = filterData && filterData.codeMatches || []; @@ -557,7 +557,7 @@ export class MarkerViewModel extends Disposable { return this.codeActionsPromise; } return this.getModel(waitForModel) - .then(model => { + .then(model => { if (model) { if (!this.codeActionsPromise) { this.codeActionsPromise = createCancelablePromise(cancellationToken => { @@ -629,7 +629,7 @@ export class MarkersViewModel extends Disposable { private bulkUpdate: boolean = false; - private hoveredMarker: Marker; + private hoveredMarker: Marker | null; private hoverDelayer: Delayer = new Delayer(300); constructor( diff --git a/src/vs/workbench/contrib/outline/browser/outline.contribution.ts b/src/vs/workbench/contrib/outline/browser/outline.contribution.ts index a53f3eae4d7..8e787b1d4a6 100644 --- a/src/vs/workbench/contrib/outline/browser/outline.contribution.ts +++ b/src/vs/workbench/contrib/outline/browser/outline.contribution.ts @@ -14,7 +14,7 @@ import { OutlineConfigKeys, OutlineViewId } from 'vs/editor/contrib/documentSymb const _outlineDesc = { id: OutlineViewId, name: localize('name', "Outline"), - ctor: OutlinePanel, + ctorDescriptor: { ctor: OutlinePanel }, canToggleVisibility: true, hideByDefault: false, collapsed: true, @@ -41,11 +41,6 @@ Registry.as(ConfigurationExtensions.Configuration).regis 'type': 'boolean', 'default': true }, - [OutlineConfigKeys.problemsEnabled]: { - 'description': localize('outline.showProblem', "Show Errors & Warnings on Outline Elements."), - 'type': 'boolean', - 'default': true - }, [OutlineConfigKeys.problemsColors]: { 'description': localize('outline.problem.colors', "Use colors for Errors & Warnings."), 'type': 'boolean', diff --git a/src/vs/workbench/contrib/outline/browser/outlinePanel.ts b/src/vs/workbench/contrib/outline/browser/outlinePanel.ts index 99d9907e974..a083b522f01 100644 --- a/src/vs/workbench/contrib/outline/browser/outlinePanel.ts +++ b/src/vs/workbench/contrib/outline/browser/outlinePanel.ts @@ -7,7 +7,6 @@ import * as dom from 'vs/base/browser/dom'; import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; import { Action, IAction, RadioGroup } from 'vs/base/common/actions'; -import { firstIndex } from 'vs/base/common/arrays'; import { createCancelablePromise, TimeoutTimer } from 'vs/base/common/async'; import { isPromiseCanceledError } from 'vs/base/common/errors'; import { Emitter } from 'vs/base/common/event'; @@ -15,7 +14,6 @@ import { defaultGenerator } from 'vs/base/common/idGenerator'; import { dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { LRUCache } from 'vs/base/common/map'; import { escape } from 'vs/base/common/strings'; -import { URI } from 'vs/base/common/uri'; import 'vs/css!./outlinePanel'; import { ICodeEditor, isCodeEditor, isDiffEditor } from 'vs/editor/browser/editorBrowser'; import { Range } from 'vs/editor/common/core/range'; @@ -24,7 +22,7 @@ import { ITextModel } from 'vs/editor/common/model'; import { IModelContentChangedEvent } from 'vs/editor/common/model/textModelEvents'; import { DocumentSymbolProviderRegistry } from 'vs/editor/common/modes'; import { LanguageFeatureRegistry } from 'vs/editor/common/modes/languageFeatureRegistry'; -import { OutlineElement, OutlineModel, TreeElement } from 'vs/editor/contrib/documentSymbols/outlineModel'; +import { OutlineElement, OutlineModel, TreeElement, IOutlineMarker } from 'vs/editor/contrib/documentSymbols/outlineModel'; import { localize } from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; @@ -33,7 +31,6 @@ import { IResourceInput } from 'vs/platform/editor/common/editor'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { WorkbenchDataTree } from 'vs/platform/list/browser/listService'; -import { IMarkerService, MarkerSeverity } from 'vs/platform/markers/common/markers'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { attachProgressBarStyler } from 'vs/platform/theme/common/styler'; import { IThemeService } from 'vs/platform/theme/common/themeService'; @@ -47,6 +44,8 @@ import { OutlineDataSource, OutlineItemComparator, OutlineSortOrder, OutlineVirt import { IDataTreeViewState } from 'vs/base/browser/ui/tree/dataTree'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { basename } from 'vs/base/common/resources'; +import { IDataSource } from 'vs/base/browser/ui/tree/tree'; +import { IMarkerDecorationsService } from 'vs/editor/common/services/markersDecorationService'; class RequestState { @@ -239,7 +238,6 @@ export class OutlinePanel extends ViewletPanel { private _editorDisposables = new Array(); private _outlineViewState = new OutlineViewState(); private _requestOracle?: RequestOracle; - private _cachedHeight: number; private _domNode: HTMLElement; private _message: HTMLDivElement; private _inputContainer: HTMLDivElement; @@ -261,7 +259,7 @@ export class OutlinePanel extends ViewletPanel { @IThemeService private readonly _themeService: IThemeService, @IStorageService private readonly _storageService: IStorageService, @IEditorService private readonly _editorService: IEditorService, - @IMarkerService private readonly _markerService: IMarkerService, + @IMarkerDecorationsService private readonly _markerDecorationService: IMarkerDecorationsService, @IConfigurationService private readonly _configurationService: IConfigurationService, @IKeybindingService keybindingService: IKeybindingService, @IConfigurationService configurationService: IConfigurationService, @@ -321,7 +319,7 @@ export class OutlinePanel extends ViewletPanel { treeContainer, new OutlineVirtualDelegate(), [new OutlineGroupRenderer(), this._treeRenderer], - this._treeDataSource, + this._treeDataSource as IDataSource, { expandOnlyOnTwistieClick: true, multipleSelectionSupport: false, @@ -335,15 +333,14 @@ export class OutlinePanel extends ViewletPanel { this._disposables.push(this._tree); this._disposables.push(this._outlineViewState.onDidChange(this._onDidChangeUserState, this)); - // todo@joh workaournd for the tree resetting the filter behaviour - // to something globally defined + // override the globally defined behaviour this._tree.updateOptions({ filterOnType: this._outlineViewState.filterOnType }); // feature: filter on type - keep tree and menu in sync this.disposables.push(this._tree.onDidUpdateOptions(e => { - this._outlineViewState.filterOnType = e.filterOnType; + this._outlineViewState.filterOnType = Boolean(e.filterOnType); })); // feature: expand all nodes when filtering (not when finding) @@ -356,7 +353,7 @@ export class OutlinePanel extends ViewletPanel { viewState = this._tree.getViewState(); this._tree.expandAll(); } else if (!pattern && viewState) { - this._tree.setInput(this._tree.getInput(), viewState); + this._tree.setInput(this._tree.getInput()!, viewState); viewState = undefined; } })); @@ -379,10 +376,8 @@ export class OutlinePanel extends ViewletPanel { })); } - protected layoutBody(height: number): void { - if (height !== this._cachedHeight) { - this._tree.layout(height); - } + protected layoutBody(height: number, width: number): void { + this._tree.layout(height, width); } getActions(): IAction[] { @@ -429,7 +424,7 @@ export class OutlinePanel extends ViewletPanel { private _showMessage(message: string) { dom.addClass(this._domNode, 'message'); - this._tree.setInput(undefined); + this._tree.setInput(undefined!); this._progressBar.stop().hide(); this._message.innerText = escape(message); } @@ -525,8 +520,6 @@ export class OutlinePanel extends ViewletPanel { await this._tree.setInput(newModel, state); } - this.layoutBody(this._cachedHeight); - // transfer focus from domNode to the tree if (this._domNode === document.activeElement) { this._tree.domFocus(); @@ -574,21 +567,23 @@ export class OutlinePanel extends ViewletPanel { })); // feature: show markers in outline - const updateMarker = (e: URI[], ignoreEmpty?: boolean) => { + const updateMarker = (model: ITextModel, ignoreEmpty?: boolean) => { if (!this._configurationService.getValue(OutlineConfigKeys.problemsEnabled)) { return; } - if (firstIndex(e, a => a.toString() === textModel.uri.toString()) < 0) { + if (model !== textModel) { return; } - const marker = this._markerService.read({ resource: textModel.uri, severities: MarkerSeverity.Error | MarkerSeverity.Warning }); + const marker = this._markerDecorationService.getLiveMarkers(textModel).map(([range, marker]) => { + return { ...range, severity: marker.severity } as IOutlineMarker; + }); if (marker.length > 0 || !ignoreEmpty) { newModel.updateMarker(marker); this._tree.updateChildren(); } }; - updateMarker([textModel.uri], true); - this._editorDisposables.push(this._markerService.onMarkerChanged(updateMarker)); + updateMarker(textModel, true); + this._editorDisposables.push(this._markerDecorationService.onDidChangeMarker(updateMarker)); this._editorDisposables.push(this.configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration(OutlineConfigKeys.problemsBadges) || e.affectsConfiguration(OutlineConfigKeys.problemsColors)) { @@ -602,7 +597,7 @@ export class OutlinePanel extends ViewletPanel { newModel.updateMarker([]); this._tree.updateChildren(); } else { - updateMarker([textModel.uri], true); + updateMarker(textModel, true); } })); } diff --git a/src/vs/workbench/contrib/output/browser/logViewer.ts b/src/vs/workbench/contrib/output/browser/logViewer.ts index a09231f1d15..b9819992434 100644 --- a/src/vs/workbench/contrib/output/browser/logViewer.ts +++ b/src/vs/workbench/contrib/output/browser/logViewer.ts @@ -17,7 +17,7 @@ import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorIn import { URI } from 'vs/base/common/uri'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { IHashService } from 'vs/workbench/services/hash/common/hashService'; -import { LOG_SCHEME, IOutputChannelDescriptor } from 'vs/workbench/contrib/output/common/output'; +import { LOG_SCHEME, IFileOutputChannelDescriptor } from 'vs/workbench/contrib/output/common/output'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IWindowService } from 'vs/platform/windows/common/windows'; @@ -26,7 +26,7 @@ export class LogViewerInput extends ResourceEditorInput { public static readonly ID = 'workbench.editorinputs.output'; - constructor(private outputChannelDescriptor: IOutputChannelDescriptor, + constructor(private outputChannelDescriptor: IFileOutputChannelDescriptor, @ITextModelService textModelResolverService: ITextModelService, @IHashService hashService: IHashService ) { diff --git a/src/vs/workbench/contrib/output/browser/outputActions.ts b/src/vs/workbench/contrib/output/browser/outputActions.ts index 934be8edfdd..28e23670269 100644 --- a/src/vs/workbench/contrib/output/browser/outputActions.ts +++ b/src/vs/workbench/contrib/output/browser/outputActions.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import * as aria from 'vs/base/browser/ui/aria/aria'; import { IAction, Action } from 'vs/base/common/actions'; -import { IOutputService, OUTPUT_PANEL_ID, IOutputChannelRegistry, Extensions as OutputExt, IOutputChannelDescriptor } from 'vs/workbench/contrib/output/common/output'; +import { IOutputService, OUTPUT_PANEL_ID, IOutputChannelRegistry, Extensions as OutputExt, IOutputChannelDescriptor, IFileOutputChannelDescriptor } from 'vs/workbench/contrib/output/common/output'; import { SelectActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { IPartService } from 'vs/workbench/services/part/common/partService'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; @@ -50,9 +50,11 @@ export class ClearOutputAction extends Action { } public run(): Promise { - this.outputService.getActiveChannel().clear(); - aria.status(nls.localize('outputCleared', "Output was cleared")); - + const activeChannel = this.outputService.getActiveChannel(); + if (activeChannel) { + activeChannel.clear(); + aria.status(nls.localize('outputCleared', "Output was cleared")); + } return Promise.resolve(true); } } @@ -70,7 +72,12 @@ export class ToggleOrSetOutputScrollLockAction extends Action { constructor(id: string, label: string, @IOutputService private readonly outputService: IOutputService) { super(id, label, 'output-action output-scroll-unlock'); - this.toDispose.push(this.outputService.onActiveOutputChannel(channel => this.setClass(this.outputService.getActiveChannel().scrollLock))); + this.toDispose.push(this.outputService.onActiveOutputChannel(channel => { + const activeChannel = this.outputService.getActiveChannel(); + if (activeChannel) { + this.setClass(activeChannel.scrollLock); + } + })); } public run(newLockState?: boolean): Promise { @@ -113,7 +120,7 @@ export class SwitchOutputAction extends Action { this.class = 'output-action switch-to-output'; } - public run(channelId?: string): Promise { + public run(channelId: string): Promise { return this.outputService.showChannel(channelId); } } @@ -134,12 +141,12 @@ export class SwitchOutputActionItem extends SelectActionItem { super(null, action, [], 0, contextViewService, { ariaLabel: nls.localize('outputChannels', 'Output Channels.') }); let outputChannelRegistry = Registry.as(OutputExt.OutputChannels); - this.toDispose.push(outputChannelRegistry.onDidRegisterChannel(() => this.updateOtions(this.outputService.getActiveChannel().id))); - this.toDispose.push(outputChannelRegistry.onDidRemoveChannel(() => this.updateOtions(this.outputService.getActiveChannel().id))); - this.toDispose.push(this.outputService.onActiveOutputChannel(activeChannelId => this.updateOtions(activeChannelId))); + this.toDispose.push(outputChannelRegistry.onDidRegisterChannel(() => this.updateOtions())); + this.toDispose.push(outputChannelRegistry.onDidRemoveChannel(() => this.updateOtions())); + this.toDispose.push(this.outputService.onActiveOutputChannel(() => this.updateOtions())); this.toDispose.push(attachSelectBoxStyler(this.selectBox, themeService)); - this.updateOtions(this.outputService.getActiveChannel().id); + this.updateOtions(); } protected getActionContext(option: string, index: number): string { @@ -147,30 +154,34 @@ export class SwitchOutputActionItem extends SelectActionItem { return channel ? channel.id : option; } - private updateOtions(selectedChannel: string): void { - const groups = groupBy(this.outputService.getChannelDescriptors(), (c1: IOutputChannelDescriptor, c2: IOutputChannelDescriptor) => { - if (!c1.log && c2.log) { - return -1; - } - if (c1.log && !c2.log) { - return 1; - } - return 0; - }); - this.outputChannels = groups[0] || []; - this.logChannels = groups[1] || []; - const showSeparator = this.outputChannels.length && this.logChannels.length; - const separatorIndex = showSeparator ? this.outputChannels.length : -1; - const options: string[] = [...this.outputChannels.map(c => c.label), ...(showSeparator ? [SwitchOutputActionItem.SEPARATOR] : []), ...this.logChannels.map(c => nls.localize('logChannel', "Log ({0})", c.label))]; - let selected = 0; - if (selectedChannel) { - selected = this.outputChannels.map(c => c.id).indexOf(selectedChannel); - if (selected === -1) { - const logChannelIndex = this.logChannels.map(c => c.id).indexOf(selectedChannel); - selected = logChannelIndex !== -1 ? separatorIndex + 1 + logChannelIndex : 0; + private updateOtions(): void { + const activeChannel = this.outputService.getActiveChannel(); + if (activeChannel) { + const selectedChannel = activeChannel.id; + const groups = groupBy(this.outputService.getChannelDescriptors(), (c1: IOutputChannelDescriptor, c2: IOutputChannelDescriptor) => { + if (!c1.log && c2.log) { + return -1; + } + if (c1.log && !c2.log) { + return 1; + } + return 0; + }); + this.outputChannels = groups[0] || []; + this.logChannels = groups[1] || []; + const showSeparator = this.outputChannels.length && this.logChannels.length; + const separatorIndex = showSeparator ? this.outputChannels.length : -1; + const options: string[] = [...this.outputChannels.map(c => c.label), ...(showSeparator ? [SwitchOutputActionItem.SEPARATOR] : []), ...this.logChannels.map(c => nls.localize('logChannel', "Log ({0})", c.label))]; + let selected = 0; + if (selectedChannel) { + selected = this.outputChannels.map(c => c.id).indexOf(selectedChannel); + if (selected === -1) { + const logChannelIndex = this.logChannels.map(c => c.id).indexOf(selectedChannel); + selected = logChannelIndex !== -1 ? separatorIndex + 1 + logChannelIndex : 0; + } } + this.setOptions(options.map((label, index) => { text: label, isDisabled: (index === separatorIndex ? true : undefined) }), Math.max(0, selected)); } - this.setOptions(options.map((label, index) => { text: label, isDisabled: (index === separatorIndex ? true : undefined) }), Math.max(0, selected)); } } @@ -192,17 +203,23 @@ export class OpenLogOutputFile extends Action { } private update(): void { - const outputChannelDescriptor = this.getOutputChannelDescriptor(); - this.enabled = !!(outputChannelDescriptor && outputChannelDescriptor.file && outputChannelDescriptor.log); + this.enabled = !!this.getLogFileOutputChannelDescriptor(); } public run(): Promise { - return this.enabled ? this.editorService.openEditor(this.instantiationService.createInstance(LogViewerInput, this.getOutputChannelDescriptor())).then(() => null) : Promise.resolve(null); + const logFileOutputChannelDescriptor = this.getLogFileOutputChannelDescriptor(); + return logFileOutputChannelDescriptor ? this.editorService.openEditor(this.instantiationService.createInstance(LogViewerInput, logFileOutputChannelDescriptor)).then(() => null) : Promise.resolve(null); } - private getOutputChannelDescriptor(): IOutputChannelDescriptor { + private getLogFileOutputChannelDescriptor(): IFileOutputChannelDescriptor | null { const channel = this.outputService.getActiveChannel(); - return channel ? this.outputService.getChannelDescriptors().filter(c => c.id === channel.id)[0] : null; + if (channel) { + const descriptor = this.outputService.getChannelDescriptors().filter(c => c.id === channel.id)[0]; + if (descriptor && descriptor.file && descriptor.log) { + return descriptor; + } + } + return null; } } @@ -219,15 +236,15 @@ export class ShowLogsOutputChannelAction extends Action { } run(): Promise { - const entries: IQuickPickItem[] = this.outputService.getChannelDescriptors().filter(c => c.file && c.log) - .map(({ id, label }) => ({ id, label })); + const entries: { id: string, label: string }[] = this.outputService.getChannelDescriptors().filter(c => c.file && c.log) + .map(({ id, label }) => ({ id, label })); return this.quickInputService.pick(entries, { placeHolder: nls.localize('selectlog', "Select Log") }) .then(entry => { if (entry) { return this.outputService.showChannel(entry.id); } - return null; + return undefined; }); } } @@ -257,9 +274,9 @@ export class OpenOutputLogFileAction extends Action { return this.quickInputService.pick(entries, { placeHolder: nls.localize('selectlogFile', "Select Log file") }) .then(entry => { if (entry) { - return this.editorService.openEditor(this.instantiationService.createInstance(LogViewerInput, entry.channel)).then(() => null); + return this.editorService.openEditor(this.instantiationService.createInstance(LogViewerInput, entry.channel)).then(() => undefined); } - return null; + return undefined; }); } } \ No newline at end of file diff --git a/src/vs/workbench/contrib/output/browser/outputPanel.ts b/src/vs/workbench/contrib/output/browser/outputPanel.ts index d1e887071e8..944b643be3c 100644 --- a/src/vs/workbench/contrib/output/browser/outputPanel.ts +++ b/src/vs/workbench/contrib/output/browser/outputPanel.ts @@ -75,7 +75,7 @@ export class OutputPanel extends AbstractTextResourceEditor { return this.actions; } - public getActionItem(action: Action): IActionItem { + public getActionItem(action: Action): IActionItem | null { if (action.id === SwitchOutputAction.ID) { return this.instantiationService.createInstance(SwitchOutputActionItem, action); } @@ -154,11 +154,14 @@ export class OutputPanel extends AbstractTextResourceEditor { return; } - const newPositionLine = e.position.lineNumber; - const lastLine = codeEditor.getModel().getLineCount(); - const newLockState = lastLine !== newPositionLine; - const lockAction = this.actions.filter((action) => action.id === ToggleOrSetOutputScrollLockAction.ID)[0]; - lockAction.run(newLockState); + const model = codeEditor.getModel(); + if (model) { + const newPositionLine = e.position.lineNumber; + const lastLine = model.getLineCount(); + const newLockState = lastLine !== newPositionLine; + const lockAction = this.actions.filter((action) => action.id === ToggleOrSetOutputScrollLockAction.ID)[0]; + lockAction.run(newLockState); + } }); } diff --git a/src/vs/workbench/contrib/output/common/output.ts b/src/vs/workbench/contrib/output/common/output.ts index 6fee8571bbe..08e2a7a363e 100644 --- a/src/vs/workbench/contrib/output/common/output.ts +++ b/src/vs/workbench/contrib/output/common/output.ts @@ -68,7 +68,7 @@ export interface IOutputService { * Given the channel id returns the output channel instance. * Channel should be first registered via OutputChannelRegistry. */ - getChannel(id: string): IOutputChannel; + getChannel(id: string): IOutputChannel | null; /** * Returns an array of all known output channels descriptors. @@ -79,7 +79,7 @@ export interface IOutputService { * Returns the currently active channel. * Only one channel can be active at a given moment. */ - getActiveChannel(): IOutputChannel; + getActiveChannel(): IOutputChannel | null; /** * Show the channel with the passed id. @@ -137,6 +137,10 @@ export interface IOutputChannelDescriptor { file?: URI; } +export interface IFileOutputChannelDescriptor extends IOutputChannelDescriptor { + file: URI; +} + export interface IOutputChannelRegistry { readonly onDidRegisterChannel: Event; diff --git a/src/vs/workbench/contrib/output/electron-browser/output.contribution.ts b/src/vs/workbench/contrib/output/electron-browser/output.contribution.ts index d759e524208..65e36ae6aab 100644 --- a/src/vs/workbench/contrib/output/electron-browser/output.contribution.ts +++ b/src/vs/workbench/contrib/output/electron-browser/output.contribution.ts @@ -7,16 +7,13 @@ import * as nls from 'vs/nls'; import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; import { ModesRegistry } from 'vs/editor/common/modes/modesRegistry'; import { Registry } from 'vs/platform/registry/common/platform'; -import { MenuId, MenuRegistry, SyncActionDescriptor, ILocalizedString } from 'vs/platform/actions/common/actions'; -import { KeybindingsRegistry, IKeybindings } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { MenuId, MenuRegistry, SyncActionDescriptor, registerAction } from 'vs/platform/actions/common/actions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; import { OutputService, LogContentProvider } from 'vs/workbench/contrib/output/electron-browser/outputServices'; import { ToggleOutputAction, ClearOutputAction, OpenLogOutputFile, ShowLogsOutputChannelAction, OpenOutputLogFileAction } from 'vs/workbench/contrib/output/browser/outputActions'; import { OUTPUT_MODE_ID, OUTPUT_MIME, OUTPUT_PANEL_ID, IOutputService, CONTEXT_IN_OUTPUT, LOG_SCHEME, LOG_MODE_ID, LOG_MIME, CONTEXT_ACTIVE_LOG_OUTPUT } from 'vs/workbench/contrib/output/common/output'; import { PanelRegistry, Extensions, PanelDescriptor } from 'vs/workbench/browser/panel'; -import { CommandsRegistry, ICommandHandler } from 'vs/platform/commands/common/commands'; -import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { OutputPanel } from 'vs/workbench/contrib/output/browser/outputPanel'; import { IEditorRegistry, Extensions as EditorExtensions, EditorDescriptor } from 'vs/workbench/browser/editor'; import { LogViewer, LogViewerInput } from 'vs/workbench/contrib/output/browser/logViewer'; @@ -33,7 +30,6 @@ registerSingleton(IOutputService, OutputService); ModesRegistry.registerLanguage({ id: OUTPUT_MODE_ID, extensions: [], - aliases: [null], mimetypes: [OUTPUT_MIME] }); @@ -41,7 +37,6 @@ ModesRegistry.registerLanguage({ ModesRegistry.registerLanguage({ id: LOG_MODE_ID, extensions: [], - aliases: [null], mimetypes: [LOG_MIME] }); @@ -93,69 +88,6 @@ const devCategory = nls.localize('developer', "Developer"); actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ShowLogsOutputChannelAction, ShowLogsOutputChannelAction.ID, ShowLogsOutputChannelAction.LABEL), 'Developer: Show Logs...', devCategory); actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenOutputLogFileAction, OpenOutputLogFileAction.ID, OpenOutputLogFileAction.LABEL), 'Developer: Open Log File...', devCategory); -interface IActionDescriptor { - id: string; - handler: ICommandHandler; - - // ICommandUI - title: ILocalizedString; - category?: string; - f1?: boolean; - - // menus - menu?: { - menuId: MenuId, - when?: ContextKeyExpr; - group?: string; - }; - - // keybindings - keybinding?: { - when?: ContextKeyExpr; - weight: number; - keys: IKeybindings; - }; -} - -function registerAction(desc: IActionDescriptor) { - - const { id, handler, title, category, f1, menu, keybinding } = desc; - - // 1) register as command - CommandsRegistry.registerCommand(id, handler); - - // 2) command palette - let command = { id, title, category }; - if (f1) { - MenuRegistry.addCommand(command); - } - - // 3) menus - if (menu) { - let { menuId, when, group } = menu; - MenuRegistry.appendMenuItem(menuId, { - command, - when, - group - }); - } - - // 4) keybindings - if (keybinding) { - let { when, weight, keys } = keybinding; - KeybindingsRegistry.registerKeybindingRule({ - id, - when, - weight, - primary: keys.primary, - secondary: keys.secondary, - linux: keys.linux, - mac: keys.mac, - win: keys.win - }); - } -} - // Define clear command, contribute to editor context menu registerAction({ id: 'editor.action.clearoutput', @@ -165,7 +97,10 @@ registerAction({ when: CONTEXT_IN_OUTPUT }, handler(accessor) { - accessor.get(IOutputService).getActiveChannel().clear(); + const activeChannel = accessor.get(IOutputService).getActiveChannel(); + if (activeChannel) { + activeChannel.clear(); + } } }); diff --git a/src/vs/workbench/contrib/output/electron-browser/outputServices.ts b/src/vs/workbench/contrib/output/electron-browser/outputServices.ts index 19a30ee0d38..60db5a4544d 100644 --- a/src/vs/workbench/contrib/output/electron-browser/outputServices.ts +++ b/src/vs/workbench/contrib/output/electron-browser/outputServices.ts @@ -14,7 +14,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { Registry } from 'vs/platform/registry/common/platform'; import { EditorOptions } from 'vs/workbench/common/editor'; -import { IOutputChannelDescriptor, IOutputChannel, IOutputService, Extensions, OUTPUT_PANEL_ID, IOutputChannelRegistry, OUTPUT_SCHEME, OUTPUT_MIME, LOG_SCHEME, LOG_MIME, CONTEXT_ACTIVE_LOG_OUTPUT, MAX_OUTPUT_LENGTH } from 'vs/workbench/contrib/output/common/output'; +import { IOutputChannelDescriptor, IOutputChannel, IOutputService, Extensions, OUTPUT_PANEL_ID, IOutputChannelRegistry, OUTPUT_SCHEME, OUTPUT_MIME, LOG_SCHEME, LOG_MIME, CONTEXT_ACTIVE_LOG_OUTPUT, MAX_OUTPUT_LENGTH, IFileOutputChannelDescriptor } from 'vs/workbench/contrib/output/common/output'; import { OutputPanel } from 'vs/workbench/contrib/output/browser/outputPanel'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IModelService } from 'vs/editor/common/services/modelService'; @@ -26,7 +26,7 @@ import { IModeService } from 'vs/editor/common/services/modeService'; import { RunOnceScheduler, ThrottledDelayer } from 'vs/base/common/async'; import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Position } from 'vs/editor/common/core/position'; -import { IFileService, FileListener } from 'vs/platform/files/common/files'; +import { IFileService } from 'vs/platform/files/common/files'; import { IPanel } from 'vs/workbench/common/panel'; import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; @@ -44,7 +44,7 @@ import { isNumber } from 'vs/base/common/types'; const OUTPUT_ACTIVE_CHANNEL_KEY = 'output.activechannel'; let watchingOutputDir = false; -let callbacks: ((eventType: string, fileName: string) => void)[] = []; +let callbacks: ((eventType: string, fileName?: string) => void)[] = []; function watchOutputDirectory(outputDir: string, logService: ILogService, onChange: (eventType: string, fileName: string) => void): IDisposable { callbacks.push(onChange); if (!watchingOutputDir) { @@ -65,7 +65,7 @@ function watchOutputDirectory(outputDir: string, logService: ILogService, onChan } interface OutputChannel extends IOutputChannel { - readonly file: URI; + readonly file: URI | null; readonly onDidAppendedContent: Event; readonly onDispose: Event; loadModel(): Promise; @@ -83,14 +83,14 @@ abstract class AbstractFileOutputChannel extends Disposable implements OutputCha private readonly mimeType: string; protected modelUpdater: RunOnceScheduler; - protected model: ITextModel; + protected model: ITextModel | null; readonly file: URI; protected startOffset: number = 0; protected endOffset: number = 0; constructor( - readonly outputChannelDescriptor: IOutputChannelDescriptor, + readonly outputChannelDescriptor: IFileOutputChannelDescriptor, private readonly modelUri: URI, protected fileService: IFileService, protected modelService: IModelService, @@ -154,7 +154,7 @@ abstract class AbstractFileOutputChannel extends Disposable implements OutputCha abstract append(message: string); protected onModelCreated(model: ITextModel) { } - protected onModelWillDispose(model: ITextModel) { } + protected onModelWillDispose(model: ITextModel | null) { } protected onUpdateModelCancelled() { } protected updateModel() { } @@ -176,15 +176,14 @@ class OutputChannelBackedByFile extends AbstractFileOutputChannel implements Out private readonly rotatingFilePath: string; constructor( - outputChannelDescriptor: IOutputChannelDescriptor, - outputDir: string, + outputChannelDescriptor: IFileOutputChannelDescriptor, modelUri: URI, @IFileService fileService: IFileService, @IModelService modelService: IModelService, @IModeService modeService: IModeService, @ILogService logService: ILogService ) { - super({ ...outputChannelDescriptor, file: URI.file(join(outputDir, `${outputChannelDescriptor.id}.log`)) }, modelUri, fileService, modelService, modeService); + super(outputChannelDescriptor, modelUri, fileService, modelService, modeService); // Use one rotating file to check for main file reset this.appender = new OutputAppender(this.id, this.file.fsPath); @@ -262,7 +261,7 @@ class OutputChannelBackedByFile extends AbstractFileOutputChannel implements Out } } - private onFileChangedInOutputDirector(eventType: string, fileName: string): void { + private onFileChangedInOutputDirector(eventType: string, fileName?: string): void { // Check if rotating file has changed. It changes only when the main file exceeds its limit. if (this.rotatingFilePath === fileName) { this.resettingDelayer.trigger(() => this.resetModel()); @@ -278,19 +277,72 @@ class OutputChannelBackedByFile extends AbstractFileOutputChannel implements Out } } +class OutputFileListener extends Disposable { + + private readonly _onDidContentChange = new Emitter(); + readonly onDidContentChange: Event = this._onDidContentChange.event; + + private watching: boolean = false; + private syncDelayer: ThrottledDelayer; + private etag: string | undefined; + + constructor( + private readonly file: URI, + private readonly fileService: IFileService + ) { + super(); + this.syncDelayer = new ThrottledDelayer(500); + } + + watch(eTag: string | undefined): void { + if (!this.watching) { + this.etag = eTag; + this.poll(); + this.watching = true; + } + } + + private poll(): void { + const loop = () => this.doWatch().then(() => this.poll()); + this.syncDelayer.trigger(loop); + } + + private doWatch(): Promise { + return this.fileService.resolveFile(this.file) + .then(stat => { + if (stat.etag !== this.etag) { + this.etag = stat.etag; + this._onDidContentChange.fire(stat.size); + } + }); + } + + unwatch(): void { + if (this.watching) { + this.syncDelayer.cancel(); + this.watching = false; + } + } + + dispose(): void { + this.unwatch(); + super.dispose(); + } +} + /** * An output channel driven by a file and does not support appending messages. */ class FileOutputChannel extends AbstractFileOutputChannel implements OutputChannel { - private readonly fileHandler: FileListener; + private readonly fileHandler: OutputFileListener; private updateInProgress: boolean = false; - private etag: string = ''; - private loadModelPromise: Promise = Promise.resolve(undefined); + private etag: string | undefined = ''; + private loadModelPromise: Promise | null = null; constructor( - outputChannelDescriptor: IOutputChannelDescriptor, + outputChannelDescriptor: IFileOutputChannelDescriptor, modelUri: URI, @IFileService fileService: IFileService, @IModelService modelService: IModelService, @@ -298,8 +350,8 @@ class FileOutputChannel extends AbstractFileOutputChannel implements OutputChann ) { super(outputChannelDescriptor, modelUri, fileService, modelService, modeService); - this.fileHandler = this._register(new FileListener(this.file, this.fileService)); - this._register(this.fileHandler.onDidContentChange(({ size }) => this.update(size))); + this.fileHandler = this._register(new OutputFileListener(this.file, this.fileService)); + this._register(this.fileHandler.onDidContentChange(size => this.update(size))); this._register(toDisposable(() => this.fileHandler.unwatch())); } @@ -314,7 +366,8 @@ class FileOutputChannel extends AbstractFileOutputChannel implements OutputChann } clear(till?: number): void { - this.loadModelPromise.then(() => { + const loadModelPromise: Promise = this.loadModelPromise ? this.loadModelPromise : Promise.resolve(); + loadModelPromise.then(() => { super.clear(till); this.update(); }); @@ -344,7 +397,7 @@ class FileOutputChannel extends AbstractFileOutputChannel implements OutputChann this.fileHandler.watch(this.etag); } - protected onModelWillDispose(model: ITextModel): void { + protected onModelWillDispose(model: ITextModel | null): void { this.fileHandler.unwatch(); } @@ -353,13 +406,15 @@ class FileOutputChannel extends AbstractFileOutputChannel implements OutputChann } update(size?: number): void { - if (!this.updateInProgress) { - this.updateInProgress = true; - if (isNumber(size) && this.endOffset > size) { // Reset - Content is removed - this.startOffset = this.endOffset = 0; - this.model.setValue(''); + if (this.model) { + if (!this.updateInProgress) { + this.updateInProgress = true; + if (isNumber(size) && this.endOffset > size) { // Reset - Content is removed + this.startOffset = this.endOffset = 0; + this.model.setValue(''); + } + this.modelUpdater.schedule(); } - this.modelUpdater.schedule(); } } } @@ -370,7 +425,7 @@ export class OutputService extends Disposable implements IOutputService, ITextMo private channels: Map = new Map(); private activeChannelIdInStorage: string; - private activeChannel: IOutputChannel; + private activeChannel: IOutputChannel | null; private readonly outputDir: string; private readonly _onActiveOutputChannel = new Emitter(); @@ -392,7 +447,7 @@ export class OutputService extends Disposable implements IOutputService, ITextMo @IContextKeyService private readonly contextKeyService: IContextKeyService, ) { super(); - this.activeChannelIdInStorage = this.storageService.get(OUTPUT_ACTIVE_CHANNEL_KEY, StorageScope.WORKSPACE, null); + this.activeChannelIdInStorage = this.storageService.get(OUTPUT_ACTIVE_CHANNEL_KEY, StorageScope.WORKSPACE, ''); this.outputDir = join(environmentService.logsPath, `output_${windowService.getCurrentWindowId()}_${toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')}`); // Register as text model content provider for output @@ -419,7 +474,7 @@ export class OutputService extends Disposable implements IOutputService, ITextMo this._register(this.storageService.onWillSaveState(() => this.saveState())); } - provideTextContent(resource: URI): Promise { + provideTextContent(resource: URI): Promise | null { const channel = this.getChannel(resource.path); if (channel) { return channel.loadModel(); @@ -447,15 +502,15 @@ export class OutputService extends Disposable implements IOutputService, ITextMo return promise.then(() => this._onActiveOutputChannel.fire(id)); } - getChannel(id: string): IOutputChannel { - return this.channels.get(id); + getChannel(id: string): IOutputChannel | null { + return this.channels.get(id) || null; } getChannelDescriptors(): IOutputChannelDescriptor[] { return Registry.as(Extensions.OutputChannels).getChannels(); } - getActiveChannel(): IOutputChannel { + getActiveChannel(): IOutputChannel | null { return this.activeChannel; } @@ -469,7 +524,7 @@ export class OutputService extends Disposable implements IOutputService, ITextMo } } - private onDidPanelOpen(panel: IPanel, preserveFocus: boolean): Promise { + private onDidPanelOpen(panel: IPanel | null, preserveFocus: boolean): Promise { if (panel && panel.getId() === OUTPUT_PANEL_ID) { this._outputPanel = this.panelService.getActivePanel(); if (this.activeChannel) { @@ -506,7 +561,9 @@ export class OutputService extends Disposable implements IOutputService, ITextMo this.showChannel(channel.id, true); } else { this.activeChannel = channel; - this._onActiveOutputChannel.fire(channel ? channel.id : undefined); + if (this.activeChannel) { + this._onActiveOutputChannel.fire(this.activeChannel.id); + } } } Registry.as(Extensions.OutputChannels).removeChannel(id); @@ -528,7 +585,8 @@ export class OutputService extends Disposable implements IOutputService, ITextMo return this.instantiationService.createInstance(FileOutputChannel, channelData, uri); } try { - return this.instantiationService.createInstance(OutputChannelBackedByFile, { id, label: channelData ? channelData.label : '' }, this.outputDir, uri); + const channelDescriptor: IFileOutputChannelDescriptor = { id, label: channelData ? channelData.label : '', log: false, file: URI.file(join(this.outputDir, `${id}.log`)) }; + return this.instantiationService.createInstance(OutputChannelBackedByFile, channelDescriptor, uri); } catch (e) { // Do not crash if spdlog rotating logger cannot be loaded (workaround for https://github.com/Microsoft/vscode/issues/47883) this.logService.error(e); @@ -584,7 +642,7 @@ export class LogContentProvider { ) { } - provideTextContent(resource: URI): Promise { + provideTextContent(resource: URI): Promise | null { if (resource.scheme === LOG_SCHEME) { let channel = this.getChannel(resource); if (channel) { @@ -594,7 +652,7 @@ export class LogContentProvider { return null; } - private getChannel(resource: URI): OutputChannel { + private getChannel(resource: URI): OutputChannel | undefined { const channelId = resource.path; let channel = this.channels.get(channelId); if (!channel) { @@ -624,9 +682,9 @@ class BufferredOutputChannel extends Disposable implements OutputChannel { readonly onDispose: Event = this._onDispose.event; private modelUpdater: RunOnceScheduler; - private model: ITextModel; + private model: ITextModel | null; private readonly bufferredContent: BufferedContent; - private lastReadId: number = undefined; + private lastReadId: number | undefined = undefined; constructor( protected readonly outputChannelIdentifier: IOutputChannelDescriptor, @@ -731,7 +789,9 @@ class BufferedContent { while (this.length > MAX_OUTPUT_LENGTH) { this.dataIds.shift(); const removed = this.data.shift(); - this.length -= removed.length; + if (removed) { + this.length -= removed.length; + } } } diff --git a/src/vs/workbench/contrib/performance/electron-browser/startupProfiler.ts b/src/vs/workbench/contrib/performance/electron-browser/startupProfiler.ts index b6c1119841a..8927080ea73 100644 --- a/src/vs/workbench/contrib/performance/electron-browser/startupProfiler.ts +++ b/src/vs/workbench/contrib/performance/electron-browser/startupProfiler.ts @@ -16,6 +16,7 @@ import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { PerfviewInput } from 'vs/workbench/contrib/performance/electron-browser/perfviewEditor'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { URI } from 'vs/base/common/uri'; export class StartupProfiler implements IWorkbenchContribution { @@ -78,7 +79,7 @@ export class StartupProfiler implements IWorkbenchContribution { }).then(res => { if (res.confirmed) { Promise.all([ - this._windowsService.showItemInFolder(join(dir, files[0])), + this._windowsService.showItemInFolder(URI.file(join(dir, files[0]))), this._createPerfIssue(files) ]).then(() => { // keep window stable until restart is selected @@ -87,7 +88,7 @@ export class StartupProfiler implements IWorkbenchContribution { message: localize('prof.thanks', "Thanks for helping us."), detail: localize('prof.detail.restart', "A final restart is required to continue to use '{0}'. Again, thank you for your contribution.", this._environmentService.appNameLong), primaryButton: localize('prof.restart', "Restart"), - secondaryButton: null + secondaryButton: undefined }).then(() => { // now we are ready to restart this._windowsService.relaunch({ removeArgs }); diff --git a/src/vs/workbench/contrib/performance/electron-browser/startupTimings.ts b/src/vs/workbench/contrib/performance/electron-browser/startupTimings.ts index 55020ca1872..2af7ea08c5b 100644 --- a/src/vs/workbench/contrib/performance/electron-browser/startupTimings.ts +++ b/src/vs/workbench/contrib/performance/electron-browser/startupTimings.ts @@ -9,7 +9,6 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ILifecycleService, StartupKind } from 'vs/platform/lifecycle/common/lifecycle'; -import { ILogService } from 'vs/platform/log/common/log'; import product from 'vs/platform/product/node/product'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IUpdateService } from 'vs/platform/update/common/update'; @@ -20,11 +19,11 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { didUseCachedData, ITimerService } from 'vs/workbench/services/timer/electron-browser/timerService'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { getEntries } from 'vs/base/common/performance'; export class StartupTimings implements IWorkbenchContribution { constructor( - @ILogService private readonly _logService: ILogService, @ITimerService private readonly _timerService: ITimerService, @IWindowsService private readonly _windowsService: IWindowsService, @IEditorService private readonly _editorService: IEditorService, @@ -43,6 +42,7 @@ export class StartupTimings implements IWorkbenchContribution { const isStandardStartup = await this._isStandardStartup(); this._reportStartupTimes().catch(onUnexpectedError); this._appendStartupTimes(isStandardStartup).catch(onUnexpectedError); + this._reportPerfTicks(); } private async _reportStartupTimes(): Promise { @@ -88,37 +88,35 @@ export class StartupTimings implements IWorkbenchContribution { // * one text editor (not multiple, not webview, welcome etc...) // * cached data present (not rejected, not created) if (this._lifecycleService.startupKind !== StartupKind.NewWindow) { - this._logService.info('no standard startup: not a new window'); return false; } if (await this._windowsService.getWindowCount() !== 1) { - this._logService.info('no standard startup: not just one window'); return false; } const activeViewlet = this._viewletService.getActiveViewlet(); if (!activeViewlet || activeViewlet.getId() !== files.VIEWLET_ID) { - this._logService.info('no standard startup: not the explorer viewlet'); return false; } const visibleControls = this._editorService.visibleControls; if (visibleControls.length !== 1 || !isCodeEditor(visibleControls[0].getControl())) { - this._logService.info('no standard startup: not just one text editor'); return false; } if (this._panelService.getActivePanel()) { - this._logService.info('no standard startup: panel is active'); return false; } if (!didUseCachedData()) { - this._logService.info('no standard startup: not using cached data'); return false; } if (!await this._updateService.isLatestVersion()) { - this._logService.info('no standard startup: not running latest version'); return false; } - this._logService.info('standard startup'); return true; } + + private _reportPerfTicks(): void { + const entries = getEntries(); + //todo@joh proper data declare + this._telemetryService.publicLog('startupRawTimers', entries); + } } diff --git a/src/vs/workbench/contrib/preferences/browser/keybindingsEditorContribution.ts b/src/vs/workbench/contrib/preferences/browser/keybindingsEditorContribution.ts index 35f7916e436..06dec075f30 100644 --- a/src/vs/workbench/contrib/preferences/browser/keybindingsEditorContribution.ts +++ b/src/vs/workbench/contrib/preferences/browser/keybindingsEditorContribution.ts @@ -14,7 +14,7 @@ import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { Range } from 'vs/editor/common/core/range'; import * as editorCommon from 'vs/editor/common/editorCommon'; import { registerEditorContribution, ServicesAccessor, registerEditorCommand, EditorCommand } from 'vs/editor/browser/editorExtensions'; -import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { ICodeEditor, IActiveCodeEditor } from 'vs/editor/browser/editorBrowser'; import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2'; import { SmartSnippetInserter } from 'vs/workbench/contrib/preferences/common/smartSnippetInserter'; import { DefineKeybindingOverlayWidget } from 'vs/workbench/contrib/preferences/browser/keybindingWidgets'; @@ -42,8 +42,8 @@ export class DefineKeybindingController extends Disposable implements editorComm return editor.getContribution(DefineKeybindingController.ID); } - private _keybindingWidgetRenderer: KeybindingWidgetRenderer; - private _keybindingDecorationRenderer: KeybindingEditorDecorationsRenderer; + private _keybindingWidgetRenderer?: KeybindingWidgetRenderer; + private _keybindingDecorationRenderer?: KeybindingEditorDecorationsRenderer; constructor( private _editor: ICodeEditor, @@ -51,9 +51,6 @@ export class DefineKeybindingController extends Disposable implements editorComm ) { super(); - this._keybindingWidgetRenderer = null; - this._keybindingDecorationRenderer = null; - this._register(this._editor.onDidChangeModel(e => this._update())); this._update(); } @@ -62,7 +59,7 @@ export class DefineKeybindingController extends Disposable implements editorComm return DefineKeybindingController.ID; } - get keybindingWidgetRenderer(): KeybindingWidgetRenderer { + get keybindingWidgetRenderer(): KeybindingWidgetRenderer | undefined { return this._keybindingWidgetRenderer; } @@ -99,7 +96,7 @@ export class DefineKeybindingController extends Disposable implements editorComm private _disposeKeybindingWidgetRenderer(): void { if (this._keybindingWidgetRenderer) { this._keybindingWidgetRenderer.dispose(); - this._keybindingWidgetRenderer = null; + this._keybindingWidgetRenderer = undefined; } } @@ -112,7 +109,7 @@ export class DefineKeybindingController extends Disposable implements editorComm private _disposeKeybindingDecorationRenderer(): void { if (this._keybindingDecorationRenderer) { this._keybindingDecorationRenderer.dispose(); - this._keybindingDecorationRenderer = null; + this._keybindingDecorationRenderer = undefined; } } } @@ -140,7 +137,7 @@ export class KeybindingWidgetRenderer extends Disposable { private _onAccepted(keybinding: string): void { this._editor.focus(); - if (keybinding) { + if (keybinding && this._editor.hasModel()) { const regexp = new RegExp(/\\/g); const backslash = regexp.test(keybinding); if (backslash) { @@ -169,7 +166,7 @@ export class KeybindingEditorDecorationsRenderer extends Disposable { private _dec: string[] = []; constructor( - private _editor: ICodeEditor, + private _editor: IActiveCodeEditor, @IKeybindingService private readonly _keybindingService: IKeybindingService, ) { super(); @@ -207,7 +204,7 @@ export class KeybindingEditorDecorationsRenderer extends Disposable { this._dec = this._editor.deltaDecorations(this._dec, newDecorations); } - private _getDecorationForEntry(model: ITextModel, entry: Node): IModelDeltaDecoration { + private _getDecorationForEntry(model: ITextModel, entry: Node): IModelDeltaDecoration | null { if (!Array.isArray(entry.children)) { return null; } @@ -239,7 +236,7 @@ export class KeybindingEditorDecorationsRenderer extends Disposable { } if (!resolvedKeybinding.isWYSIWYG()) { const uiLabel = resolvedKeybinding.getLabel(); - if (value.value.toLowerCase() === uiLabel.toLowerCase()) { + if (typeof uiLabel === 'string' && value.value.toLowerCase() === uiLabel.toLowerCase()) { // coincidentally, this is actually WYSIWYG return null; } @@ -249,7 +246,7 @@ export class KeybindingEditorDecorationsRenderer extends Disposable { return this._createDecoration(false, resolvedKeybinding.getLabel(), usLabel, model, value); } const expectedUserSettingsLabel = resolvedKeybinding.getUserSettingsLabel(); - if (!KeybindingEditorDecorationsRenderer._userSettingsFuzzyEquals(value.value, expectedUserSettingsLabel)) { + if (typeof expectedUserSettingsLabel === 'string' && !KeybindingEditorDecorationsRenderer._userSettingsFuzzyEquals(value.value, expectedUserSettingsLabel)) { return this._createDecoration(false, resolvedKeybinding.getLabel(), usLabel, model, value); } return null; @@ -300,7 +297,7 @@ export class KeybindingEditorDecorationsRenderer extends Disposable { return false; } - private _createDecoration(isError: boolean, uiLabel: string, usLabel: string, model: ITextModel, keyNode: Node): IModelDeltaDecoration { + private _createDecoration(isError: boolean, uiLabel: string | null, usLabel: string | null, model: ITextModel, keyNode: Node): IModelDeltaDecoration { let msg: MarkdownString; let className: string; let beforeContentClassName: string; diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesActions.ts b/src/vs/workbench/contrib/preferences/browser/preferencesActions.ts index 0a52f70fb78..fd8600260b5 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesActions.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesActions.ts @@ -260,7 +260,9 @@ export class ConfigureLanguageBasedSettingsAction extends Action { .then(pick => { if (pick) { const modeId = this.modeService.getModeIdForLanguageName(pick.label.toLowerCase()); - return this.preferencesService.configureSettingsForLanguage(modeId); + if (typeof modeId === 'string') { + return this.preferencesService.configureSettingsForLanguage(modeId); + } } return undefined; }); diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts b/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts index 04a1058cd26..55c8f055069 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts @@ -226,13 +226,17 @@ export class SettingsGroupTitleWidget extends Widget implements IViewZone { if (this.settingsGroup.range.startLineNumber - 3 !== 1) { this.editor.focus(); const lineNumber = this.settingsGroup.range.startLineNumber - 2; - this.editor.setPosition({ lineNumber, column: this.editor.getModel().getLineMinColumn(lineNumber) }); + if (this.editor.hasModel()) { + this.editor.setPosition({ lineNumber, column: this.editor.getModel().getLineMinColumn(lineNumber) }); + } } break; case KeyCode.DownArrow: const lineNumber = this.isCollapsed() ? this.settingsGroup.range.startLineNumber : this.settingsGroup.range.startLineNumber - 1; this.editor.focus(); - this.editor.setPosition({ lineNumber, column: this.editor.getModel().getLineMinColumn(lineNumber) }); + if (this.editor.hasModel()) { + this.editor.setPosition({ lineNumber, column: this.editor.getModel().getLineMinColumn(lineNumber) }); + } break; } } @@ -286,7 +290,7 @@ export class SettingsGroupTitleWidget extends Widget implements IViewZone { export class FolderSettingsActionItem extends BaseActionItem { - private _folder: IWorkspaceFolder; + private _folder: IWorkspaceFolder | null; private _folderSettingCounts = new Map(); private container: HTMLElement; @@ -308,17 +312,21 @@ export class FolderSettingsActionItem extends BaseActionItem { this.disposables.push(this.contextService.onDidChangeWorkspaceFolders(() => this.onWorkspaceFoldersChanged())); } - get folder(): IWorkspaceFolder { + get folder(): IWorkspaceFolder | null { return this._folder; } - set folder(folder: IWorkspaceFolder) { + set folder(folder: IWorkspaceFolder | null) { this._folder = folder; this.update(); } setCount(settingsTarget: URI, count: number): void { - const folder = this.contextService.getWorkspaceFolder(settingsTarget).uri; + const workspaceFolder = this.contextService.getWorkspaceFolder(settingsTarget); + if (!workspaceFolder) { + throw new Error('unknown folder'); + } + const folder = workspaceFolder.uri; this._folderSettingCounts.set(folder.toString(), count); this.update(); } @@ -374,8 +382,8 @@ export class FolderSettingsActionItem extends BaseActionItem { private onWorkspaceFoldersChanged(): void { const oldFolder = this._folder; const workspace = this.contextService.getWorkspace(); - if (this._folder) { - this._folder = workspace.folders.filter(folder => folder.uri.toString() === this._folder.uri.toString())[0] || workspace.folders[0]; + if (oldFolder) { + this._folder = workspace.folders.filter(folder => folder.uri.toString() === oldFolder.uri.toString())[0] || workspace.folders[0]; } this._folder = this._folder ? this._folder : workspace.folders.length === 1 ? workspace.folders[0] : null; @@ -384,7 +392,7 @@ export class FolderSettingsActionItem extends BaseActionItem { if (this._action.checked) { if ((oldFolder || !this._folder) || (!oldFolder || this._folder) - || (oldFolder && this._folder && oldFolder.uri.toString() === this._folder.uri.toString())) { + || (oldFolder && this._folder && (oldFolder as IWorkspaceFolder).uri.toString() === (this._folder as IWorkspaceFolder).uri.toString())) { this._action.run(this._folder); } } @@ -625,9 +633,10 @@ export class SearchWidget extends Widget { const focusTracker = this._register(DOM.trackFocus(this.inputBox.inputElement)); this._register(focusTracker.onDidFocus(() => this._onFocus.fire())); - if (this.options.focusKey) { - this._register(focusTracker.onDidFocus(() => this.options.focusKey.set(true))); - this._register(focusTracker.onDidBlur(() => this.options.focusKey.set(false))); + const focusKey = this.options.focusKey; + if (focusKey) { + this._register(focusTracker.onDidFocus(() => focusKey.set(true))); + this._register(focusTracker.onDidBlur(() => focusKey.set(false))); } } diff --git a/src/vs/workbench/contrib/preferences/common/preferences.ts b/src/vs/workbench/contrib/preferences/common/preferences.ts index 26985c7b847..bdc2c48a344 100644 --- a/src/vs/workbench/contrib/preferences/common/preferences.ts +++ b/src/vs/workbench/contrib/preferences/common/preferences.ts @@ -36,11 +36,11 @@ export interface IPreferencesSearchService { _serviceBrand: any; getLocalSearchProvider(filter: string): ISearchProvider; - getRemoteSearchProvider(filter: string, newExtensionsOnly?: boolean): ISearchProvider; + getRemoteSearchProvider(filter: string, newExtensionsOnly?: boolean): ISearchProvider | undefined; } export interface ISearchProvider { - searchModel(preferencesModel: ISettingsEditorModel, token?: CancellationToken): Promise; + searchModel(preferencesModel: ISettingsEditorModel, token?: CancellationToken): Promise; } export interface IKeybindingsEditor extends IEditor { diff --git a/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts b/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts index c1a65046bc1..4fe4d12d136 100644 --- a/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts +++ b/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts @@ -61,7 +61,7 @@ export class PreferencesContribution implements IWorkbenchContribution { } } - private onEditorOpening(editor: IEditorInput, options: IEditorOptions | ITextEditorOptions, group: IEditorGroup): IOpenEditorOverride { + private onEditorOpening(editor: IEditorInput, options: IEditorOptions | ITextEditorOptions | undefined, group: IEditorGroup): IOpenEditorOverride | undefined { const resource = editor.getResource(); if ( !resource || @@ -108,7 +108,7 @@ export class PreferencesContribution implements IWorkbenchContribution { private start(): void { this.textModelResolverService.registerTextModelContentProvider('vscode', { - provideTextContent: (uri: URI): Promise => { + provideTextContent: (uri: URI): Promise | null => { if (uri.scheme !== 'vscode') { return null; } @@ -123,7 +123,7 @@ export class PreferencesContribution implements IWorkbenchContribution { }); } - private getSchemaModel(uri: URI): ITextModel { + private getSchemaModel(uri: URI): ITextModel | null { let schema = schemaRegistry.getSchemaContributions().schemas[uri.toString()]; if (schema) { const modelContent = JSON.stringify(schema); diff --git a/src/vs/workbench/contrib/preferences/electron-browser/preferences.contribution.ts b/src/vs/workbench/contrib/preferences/electron-browser/preferences.contribution.ts index 6d2d5ab3bc1..e9af73dc862 100644 --- a/src/vs/workbench/contrib/preferences/electron-browser/preferences.contribution.ts +++ b/src/vs/workbench/contrib/preferences/electron-browser/preferences.contribution.ts @@ -477,7 +477,7 @@ MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: OpenFolderSettingsAction.ID, title: { value: `${category}: ${OpenFolderSettingsAction.LABEL}`, original: 'Preferences: Open Folder Settings' }, - category: nls.localize('preferencesCategory', "Prefernces") + category: nls.localize('preferencesCategory', "Preferences") }, when: WorkbenchStateContext.isEqualTo('workspace') }); @@ -489,7 +489,7 @@ MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: OpenWorkspaceSettingsAction.ID, title: { value: `${category}: ${OpenWorkspaceSettingsAction.LABEL}`, original: 'Preferences: Open Workspace Settings' }, - category: nls.localize('preferencesCategory', "Prefernces") + category: nls.localize('preferencesCategory', "Preferences") }, when: WorkbenchStateContext.notEqualsTo('empty') }); @@ -799,4 +799,4 @@ MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { title: OPEN_FOLDER_SETTINGS_LABEL }, when: ContextKeyExpr.and(ExplorerRootContext, ExplorerFolderContext) -}); \ No newline at end of file +}); diff --git a/src/vs/workbench/contrib/preferences/electron-browser/preferencesSearch.ts b/src/vs/workbench/contrib/preferences/electron-browser/preferencesSearch.ts index 42948953bfa..e01930681bd 100644 --- a/src/vs/workbench/contrib/preferences/electron-browser/preferencesSearch.ts +++ b/src/vs/workbench/contrib/preferences/electron-browser/preferencesSearch.ts @@ -11,7 +11,6 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema'; import { Registry } from 'vs/platform/registry/common/platform'; import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry'; import { IMatch, or, matchesContiguousSubString, matchesPrefix, matchesCamelCase, matchesWords } from 'vs/base/common/filters'; -import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IRequestService } from 'vs/platform/request/node/request'; @@ -23,9 +22,11 @@ import { IPreferencesSearchService, ISearchProvider, IWorkbenchSettingsConfigura import { CancellationToken } from 'vs/base/common/cancellation'; import { canceled } from 'vs/base/common/errors'; import { ExtensionType } from 'vs/platform/extensions/common/extensions'; +import { nullRange } from 'vs/workbench/services/preferences/common/preferencesModels'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; export interface IEndpointDetails { - urlBase: string; + urlBase?: string; key?: string; } @@ -35,7 +36,7 @@ export class PreferencesSearchService extends Disposable implements IPreferences private _installedExtensions: Promise; constructor( - @IWorkspaceConfigurationService private readonly configurationService: IWorkspaceConfigurationService, + @IConfigurationService private readonly configurationService: IConfigurationService, @IEnvironmentService private readonly environmentService: IEnvironmentService, @IInstantiationService private readonly instantiationService: IInstantiationService, @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, @@ -76,14 +77,14 @@ export class PreferencesSearchService extends Disposable implements IPreferences } } - getRemoteSearchProvider(filter: string, newExtensionsOnly = false): ISearchProvider { + getRemoteSearchProvider(filter: string, newExtensionsOnly = false): ISearchProvider | undefined { const opts: IRemoteSearchProviderOptions = { filter, newExtensionsOnly, endpoint: this._endpoint }; - return this.remoteSearchAllowed && this.instantiationService.createInstance(RemoteSearchProvider, opts, this._installedExtensions); + return this.remoteSearchAllowed ? this.instantiationService.createInstance(RemoteSearchProvider, opts, this._installedExtensions) : undefined; } getLocalSearchProvider(filter: string): LocalSearchProvider { @@ -164,7 +165,7 @@ class RemoteSearchProvider implements ISearchProvider { private static readonly MAX_REQUESTS = 10; private static readonly NEW_EXTENSIONS_MIN_SCORE = 1; - private _remoteSearchP: Promise; + private _remoteSearchP: Promise; constructor(private options: IRemoteSearchProviderOptions, private installedExtensions: Promise, @IEnvironmentService private readonly environmentService: IEnvironmentService, @@ -176,8 +177,8 @@ class RemoteSearchProvider implements ISearchProvider { Promise.resolve(null); } - searchModel(preferencesModel: ISettingsEditorModel, token?: CancellationToken): Promise { - return this._remoteSearchP.then(remoteResult => { + searchModel(preferencesModel: ISettingsEditorModel, token?: CancellationToken): Promise { + return this._remoteSearchP.then((remoteResult) => { if (!remoteResult) { return null; } @@ -260,20 +261,25 @@ class RemoteSearchProvider implements ISearchProvider { } const requestType = details.body ? 'post' : 'get'; + const headers = { + 'User-Agent': 'request', + 'Content-Type': 'application/json; charset=utf-8', + }; + + if (this.options.endpoint.key) { + headers['api-key'] = this.options.endpoint.key; + } + const start = Date.now(); return this.requestService.request({ type: requestType, url: details.url, data: details.body, - headers: { - 'User-Agent': 'request', - 'Content-Type': 'application/json; charset=utf-8', - 'api-key': this.options.endpoint.key - }, + headers, timeout: 5000 }, CancellationToken.None).then(context => { - if (context.res.statusCode >= 300) { - throw new Error(`${details} returned status code: ${context.res.statusCode}`); + if (typeof context.res.statusCode === 'number' && context.res.statusCode >= 300) { + throw new Error(`${JSON.stringify(details)} returned status code: ${context.res.statusCode}`); } return asJson(context); @@ -319,8 +325,7 @@ class RemoteSearchProvider implements ISearchProvider { duration, timestamp, scoredResults, - context: result['@odata.context'], - extensions: details.extensions + context: result['@odata.context'] }; }); } @@ -374,8 +379,7 @@ class RemoteSearchProvider implements ISearchProvider { return { url, body, - hasMoreFilters, - extensions + hasMoreFilters }; } @@ -423,12 +427,12 @@ function remoteSettingToISetting(remoteSetting: IRemoteSetting): IExtensionSetti return { description: remoteSetting.description.split('\n'), descriptionIsMarkdown: false, - descriptionRanges: null, + descriptionRanges: [], key: remoteSetting.key, - keyRange: null, + keyRange: nullRange, value: remoteSetting.defaultValue, - range: null, - valueRange: null, + range: nullRange, + valueRange: nullRange, overrides: [], extensionName: remoteSetting.extensionName, extensionPublisher: remoteSetting.extensionPublisher @@ -533,16 +537,6 @@ class SettingMatches { } private toKeyRange(setting: ISetting, match: IMatch): IRange { - if (!setting.keyRange) { - // No source range? Return fake range, don't care - return { - startLineNumber: 0, - startColumn: 0, - endLineNumber: 0, - endColumn: 0, - }; - } - return { startLineNumber: setting.keyRange.startLineNumber, startColumn: setting.keyRange.startColumn + match.start, @@ -552,16 +546,6 @@ class SettingMatches { } private toDescriptionRange(setting: ISetting, match: IMatch, lineIndex: number): IRange { - if (!setting.keyRange) { - // No source range? Return fake range, don't care - return { - startLineNumber: 0, - startColumn: 0, - endLineNumber: 0, - endColumn: 0, - }; - } - return { startLineNumber: setting.descriptionRanges[lineIndex].startLineNumber, startColumn: setting.descriptionRanges[lineIndex].startColumn + match.start, @@ -571,16 +555,6 @@ class SettingMatches { } private toValueRange(setting: ISetting, match: IMatch): IRange { - if (!setting.keyRange) { - // No source range? Return fake range, don't care - return { - startLineNumber: 0, - startColumn: 0, - endLineNumber: 0, - endColumn: 0, - }; - } - return { startLineNumber: setting.valueRange.startLineNumber, startColumn: setting.valueRange.startColumn + match.start + 1, diff --git a/src/vs/workbench/contrib/preferences/electron-browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/electron-browser/settingsEditor2.ts index 522b03a65a2..efdfdce5654 100644 --- a/src/vs/workbench/contrib/preferences/electron-browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/electron-browser/settingsEditor2.ts @@ -56,6 +56,10 @@ function createGroupIterator(group: SettingsTreeGroupElement): Iterator { if (DOM.findParentWithClass(e.relatedTarget, 'settings-editor-tree')) { if (this.settingsTree.scrollTop > 0) { - const firstElement = this.getFirstVisibleElement(); + const firstElement = this.settingsTree.firstVisibleElement; this.settingsTree.reveal(firstElement, 0.1); return true; } @@ -515,7 +516,7 @@ export class SettingsEditor2 extends BaseEditor { e => { if (DOM.findParentWithClass(e.relatedTarget, 'settings-editor-tree')) { if (this.settingsTree.scrollTop < this.settingsTree.scrollHeight) { - const lastElement = this.getLastVisibleElement(); + const lastElement = this.settingsTree.lastVisibleElement; this.settingsTree.reveal(lastElement, 0.9); return true; } @@ -527,30 +528,6 @@ export class SettingsEditor2 extends BaseEditor { ); } - private getFirstVisibleElement(nth = 0): SettingsTreeElement | null { - // Hack, see https://github.com/Microsoft/vscode/issues/64749 - const settingItems = this.settingsTree.getHTMLElement().querySelectorAll(AbstractSettingRenderer.CONTENTS_SELECTOR); - const firstEl = settingItems[nth] || settingItems[0]; - if (!firstEl) { - return null; - } - - const firstSettingId = this.settingRenderers.getIdForDOMElementInSetting(firstEl); - return this.settingsTreeModel.getElementById(firstSettingId); - } - - private getLastVisibleElement(): SettingsTreeElement | null { - // Hack, see https://github.com/Microsoft/vscode/issues/64749 - const settingItems = this.settingsTree.getHTMLElement().querySelectorAll(AbstractSettingRenderer.CONTENTS_SELECTOR); - const firstEl = settingItems[settingItems.length - 1]; - if (!firstEl) { - return null; - } - - const firstSettingId = this.settingRenderers.getIdForDOMElementInSetting(firstEl); - return this.settingsTreeModel.getElementById(firstSettingId); - } - private createFocusSink(container: HTMLElement, callback: (e: any) => boolean, label: string): HTMLElement { const listFocusSink = DOM.append(container, $('.settings-tree-focus-sink')); listFocusSink.setAttribute('aria-label', label); @@ -583,13 +560,10 @@ export class SettingsEditor2 extends BaseEditor { if (this.searchResultModel) { if (this.viewState.filterToCategory !== element) { this.viewState.filterToCategory = element; - // see https://github.com/Microsoft/vscode/issues/66796 - setTimeout(() => { - this.renderTree(); - this.settingsTree.scrollTop = 0; - }, 0); + this.renderTree(); + this.settingsTree.scrollTop = 0; } - } else if (element && (!e.browserEvent || !(e.browserEvent).fromScroll)) { + } else if (element && (!e.browserEvent || !(e.browserEvent).fromScroll)) { this.settingsTree.reveal(element, 0); } })); @@ -687,7 +661,7 @@ export class SettingsEditor2 extends BaseEditor { return; } - const elementToSync = this.getFirstVisibleElement(1); + const elementToSync = this.settingsTree.firstVisibleElement; const element = elementToSync instanceof SettingsTreeSettingElement ? elementToSync.parent : elementToSync instanceof SettingsTreeGroupElement ? elementToSync : null; @@ -712,7 +686,7 @@ export class SettingsEditor2 extends BaseEditor { this.tocTree.setSelection([element]); const fakeKeyboardEvent = new KeyboardEvent('keydown'); - (fakeKeyboardEvent).fromScroll = true; + (fakeKeyboardEvent).fromScroll = true; this.tocTree.setFocus([element], fakeKeyboardEvent); } } diff --git a/src/vs/workbench/contrib/quickopen/browser/commandsHandler.ts b/src/vs/workbench/contrib/quickopen/browser/commandsHandler.ts index 13019490a5b..0ebc16e91c7 100644 --- a/src/vs/workbench/contrib/quickopen/browser/commandsHandler.ts +++ b/src/vs/workbench/contrib/quickopen/browser/commandsHandler.ts @@ -111,7 +111,7 @@ class CommandsHistory extends Disposable { entries.forEach(entry => commandHistory.set(entry.key, entry.value)); } - commandCounter = this.storageService.getInteger(CommandsHistory.PREF_KEY_COUNTER, StorageScope.GLOBAL, commandCounter); + commandCounter = this.storageService.getNumber(CommandsHistory.PREF_KEY_COUNTER, StorageScope.GLOBAL, commandCounter); } push(commandId: string): void { diff --git a/src/vs/workbench/contrib/scm/electron-browser/dirtydiffDecorator.ts b/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts similarity index 100% rename from src/vs/workbench/contrib/scm/electron-browser/dirtydiffDecorator.ts rename to src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts diff --git a/src/vs/workbench/contrib/scm/electron-browser/media/check-inverse.svg b/src/vs/workbench/contrib/scm/browser/media/check-inverse.svg similarity index 100% rename from src/vs/workbench/contrib/scm/electron-browser/media/check-inverse.svg rename to src/vs/workbench/contrib/scm/browser/media/check-inverse.svg diff --git a/src/vs/workbench/contrib/scm/electron-browser/media/check.svg b/src/vs/workbench/contrib/scm/browser/media/check.svg similarity index 100% rename from src/vs/workbench/contrib/scm/electron-browser/media/check.svg rename to src/vs/workbench/contrib/scm/browser/media/check.svg diff --git a/src/vs/workbench/contrib/scm/electron-browser/media/dirtydiffDecorator.css b/src/vs/workbench/contrib/scm/browser/media/dirtydiffDecorator.css similarity index 100% rename from src/vs/workbench/contrib/scm/electron-browser/media/dirtydiffDecorator.css rename to src/vs/workbench/contrib/scm/browser/media/dirtydiffDecorator.css diff --git a/src/vs/workbench/contrib/scm/electron-browser/media/icon-dark.svg b/src/vs/workbench/contrib/scm/browser/media/icon-dark.svg similarity index 100% rename from src/vs/workbench/contrib/scm/electron-browser/media/icon-dark.svg rename to src/vs/workbench/contrib/scm/browser/media/icon-dark.svg diff --git a/src/vs/workbench/contrib/scm/electron-browser/media/icon-light.svg b/src/vs/workbench/contrib/scm/browser/media/icon-light.svg similarity index 100% rename from src/vs/workbench/contrib/scm/electron-browser/media/icon-light.svg rename to src/vs/workbench/contrib/scm/browser/media/icon-light.svg diff --git a/src/vs/workbench/contrib/scm/electron-browser/media/scmViewlet.css b/src/vs/workbench/contrib/scm/browser/media/scmViewlet.css similarity index 100% rename from src/vs/workbench/contrib/scm/electron-browser/media/scmViewlet.css rename to src/vs/workbench/contrib/scm/browser/media/scmViewlet.css diff --git a/src/vs/workbench/contrib/scm/electron-browser/scm.contribution.ts b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts similarity index 98% rename from src/vs/workbench/contrib/scm/electron-browser/scm.contribution.ts rename to src/vs/workbench/contrib/scm/browser/scm.contribution.ts index 70258e96a6a..9172c0ab736 100644 --- a/src/vs/workbench/contrib/scm/electron-browser/scm.contribution.ts +++ b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts @@ -14,7 +14,7 @@ import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { StatusUpdater, StatusBarController } from './scmActivity'; -import { SCMViewlet } from 'vs/workbench/contrib/scm/electron-browser/scmViewlet'; +import { SCMViewlet } from 'vs/workbench/contrib/scm/browser/scmViewlet'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; diff --git a/src/vs/workbench/contrib/scm/electron-browser/scmActivity.ts b/src/vs/workbench/contrib/scm/browser/scmActivity.ts similarity index 100% rename from src/vs/workbench/contrib/scm/electron-browser/scmActivity.ts rename to src/vs/workbench/contrib/scm/browser/scmActivity.ts diff --git a/src/vs/workbench/contrib/scm/electron-browser/scmMenus.ts b/src/vs/workbench/contrib/scm/browser/scmMenus.ts similarity index 100% rename from src/vs/workbench/contrib/scm/electron-browser/scmMenus.ts rename to src/vs/workbench/contrib/scm/browser/scmMenus.ts diff --git a/src/vs/workbench/contrib/scm/electron-browser/scmUtil.ts b/src/vs/workbench/contrib/scm/browser/scmUtil.ts similarity index 100% rename from src/vs/workbench/contrib/scm/electron-browser/scmUtil.ts rename to src/vs/workbench/contrib/scm/browser/scmUtil.ts diff --git a/src/vs/workbench/contrib/scm/electron-browser/scmViewlet.ts b/src/vs/workbench/contrib/scm/browser/scmViewlet.ts similarity index 98% rename from src/vs/workbench/contrib/scm/electron-browser/scmViewlet.ts rename to src/vs/workbench/contrib/scm/browser/scmViewlet.ts index 077334bb5ca..203d662dfb5 100644 --- a/src/vs/workbench/contrib/scm/electron-browser/scmViewlet.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewlet.ts @@ -84,11 +84,11 @@ class StatusBarAction extends Action { private commandService: ICommandService ) { super(`statusbaraction{${command.id}}`, command.title, '', true); - this.tooltip = command.tooltip; + this.tooltip = command.tooltip || ''; } run(): Promise { - return this.commandService.executeCommand(this.command.id, ...this.command.arguments); + return this.commandService.executeCommand(this.command.id, ...(this.command.arguments || [])); } } @@ -198,7 +198,7 @@ class ProviderRenderer implements IListRenderer if (icon) { template.decorationIcon.style.display = ''; template.decorationIcon.style.backgroundImage = `url('${icon}')`; - template.decorationIcon.title = resource.decorations.tooltip; + template.decorationIcon.title = resource.decorations.tooltip || ''; } else { template.decorationIcon.style.display = 'none'; template.decorationIcon.style.backgroundImage = ''; } - template.element.setAttribute('data-tooltip', resource.decorations.tooltip); + template.element.setAttribute('data-tooltip', resource.decorations.tooltip || ''); template.elementDisposable = combinedDisposable(disposables); } @@ -821,7 +821,7 @@ export class RepositoryPanel extends ViewletPanel { const validationDelayer = new ThrottledDelayer(200); const validate = () => { - return this.repository.input.validateInput(this.inputBox.value, this.inputBox.inputElement.selectionStart).then(result => { + return this.repository.input.validateInput(this.inputBox.value, this.inputBox.inputElement.selectionStart || 0).then(result => { if (!result) { this.inputBox.inputElement.removeAttribute('aria-invalid'); this.inputBox.hideMessage(); @@ -916,7 +916,7 @@ export class RepositoryPanel extends ViewletPanel { } } - layoutBody(height: number = this.cachedHeight, width: number = this.cachedWidth): void { + layoutBody(height: number | undefined = this.cachedHeight, width: number | undefined = this.cachedWidth): void { if (height === undefined) { return; } @@ -962,9 +962,9 @@ export class RepositoryPanel extends ViewletPanel { return this.menus.getTitleSecondaryActions(); } - getActionItem(action: IAction): IActionItem { + getActionItem(action: IAction): IActionItem | null { if (!(action instanceof MenuItemAction)) { - return undefined; + return null; } return new ContextAwareMenuItemActionItem(action, this.keybindingService, this.notificationService, this.contextMenuService); @@ -1176,9 +1176,9 @@ export class SCMViewlet extends PanelViewlet implements IViewModel, IViewsViewle this.onSelectionChange(this.mainPanel.getSelection()); this.mainPanelDisposable = toDisposable(() => { - this.removePanels([this.mainPanel]); + this.removePanels([this.mainPanel!]); selectionChangeDisposable.dispose(); - this.mainPanel.dispose(); + this.mainPanel!.dispose(); }); } else { this.mainPanelDisposable.dispose(); @@ -1203,7 +1203,7 @@ export class SCMViewlet extends PanelViewlet implements IViewModel, IViewsViewle super.setVisible(visible); if (!visible) { - this.cachedMainPanelHeight = this.getPanelSize(this.mainPanel); + this.cachedMainPanelHeight = this.mainPanel ? this.getPanelSize(this.mainPanel) : 0; } const start = this.getContributedViewsStartIndex(); @@ -1247,9 +1247,9 @@ export class SCMViewlet extends PanelViewlet implements IViewModel, IViewsViewle } } - getActionItem(action: IAction): IActionItem { + getActionItem(action: IAction): IActionItem | null { if (!(action instanceof MenuItemAction)) { - return undefined; + return null; } return new ContextAwareMenuItemActionItem(action, this.keybindingService, this.notificationService, this.contextMenuService); @@ -1318,14 +1318,14 @@ export class SCMViewlet extends PanelViewlet implements IViewModel, IViewsViewle this.removePanels(panelsToRemove); // Restore main panel height - if (this.isVisible() && typeof this.cachedMainPanelHeight === 'number') { + if (this.mainPanel && this.isVisible() && typeof this.cachedMainPanelHeight === 'number') { this.resizePanel(this.mainPanel, this.cachedMainPanelHeight); this.cachedMainPanelHeight = undefined; } // Resize all panels equally const height = typeof this.height === 'number' ? this.height : 1000; - const mainPanelHeight = this.getPanelSize(this.mainPanel); + const mainPanelHeight = this.mainPanel ? this.getPanelSize(this.mainPanel) : 0; const size = (height - mainPanelHeight - contributableViewsHeight) / repositories.length; for (const panel of this.repositoryPanels) { this.resizePanel(panel, size); @@ -1366,7 +1366,7 @@ export class SCMViewlet extends PanelViewlet implements IViewModel, IViewsViewle const panelsToAdd: { panel: ViewletPanel, size: number, index: number }[] = []; for (const { viewDescriptor, collapsed, index, size } of added) { - const panel = this.instantiationService.createInstance(viewDescriptor.ctor, { + const panel = this.instantiationService.createInstance(viewDescriptor.ctorDescriptor.ctor, { id: viewDescriptor.id, title: viewDescriptor.name, actionRunner: this.getActionRunner(), diff --git a/src/vs/workbench/contrib/search/common/queryBuilder.ts b/src/vs/workbench/contrib/search/common/queryBuilder.ts index ccf08d86b81..2cc16e6d342 100644 --- a/src/vs/workbench/contrib/search/common/queryBuilder.ts +++ b/src/vs/workbench/contrib/search/common/queryBuilder.ts @@ -152,14 +152,14 @@ export class QueryBuilder { if (options.includePattern) { includeSearchPathsInfo = options.expandPatterns ? this.parseSearchPaths(options.includePattern) : - { pattern: patternListToIExpression(...splitGlobPattern(options.includePattern)) }; + { pattern: patternListToIExpression(options.includePattern) }; } let excludeSearchPathsInfo: ISearchPathsInfo = {}; if (options.excludePattern) { excludeSearchPathsInfo = options.expandPatterns ? this.parseSearchPaths(options.excludePattern) : - { pattern: patternListToIExpression(...splitGlobPattern(options.excludePattern)) }; + { pattern: patternListToIExpression(options.excludePattern) }; } // Build folderQueries from searchPaths, if given, otherwise folderResources diff --git a/src/vs/workbench/contrib/search/test/common/queryBuilder.test.ts b/src/vs/workbench/contrib/search/test/common/queryBuilder.test.ts index 5fc42bc4901..aaa35bae3db 100644 --- a/src/vs/workbench/contrib/search/test/common/queryBuilder.test.ts +++ b/src/vs/workbench/contrib/search/test/common/queryBuilder.test.ts @@ -85,7 +85,7 @@ suite('QueryBuilder', () => { }); }); - test('splits glob pattern even with expandPatterns disabled', () => { + test('does not split glob pattern when expandPatterns disabled', () => { assertEqualQueries( queryBuilder.file([ROOT_1_URI], { includePattern: '**/foo, **/bar' }), { @@ -94,8 +94,7 @@ suite('QueryBuilder', () => { }], type: QueryType.File, includePattern: { - '**/foo': true, - '**/bar': true + '**/foo, **/bar': true } }); }); diff --git a/src/vs/workbench/contrib/splash/electron-browser/partsSplash.contribution.ts b/src/vs/workbench/contrib/splash/electron-browser/partsSplash.contribution.ts index b03134e1686..79dcaaae6d3 100644 --- a/src/vs/workbench/contrib/splash/electron-browser/partsSplash.contribution.ts +++ b/src/vs/workbench/contrib/splash/electron-browser/partsSplash.contribution.ts @@ -21,6 +21,8 @@ import { IPartService, Parts, Position } from 'vs/workbench/services/part/common import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IFileService } from 'vs/platform/files/common/files'; import { URI } from 'vs/base/common/uri'; +import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; class PartsSplash { @@ -28,6 +30,7 @@ class PartsSplash { private readonly _disposables: IDisposable[] = []; + private _didChangeTitleBarStyle: boolean; private _lastBaseTheme: string; private _lastBackground?: string; @@ -38,12 +41,18 @@ class PartsSplash { @IEnvironmentService private readonly _envService: IEnvironmentService, @IBroadcastService private readonly _broadcastService: IBroadcastService, @ILifecycleService lifecycleService: ILifecycleService, + @IEditorGroupsService editorGroupsService: IEditorGroupsService, + @IConfigurationService configService: IConfigurationService, ) { lifecycleService.when(LifecyclePhase.Restored).then(_ => this._removePartsSplash()); Event.debounce(Event.any( onDidChangeFullscreen, - _partService.onEditorLayout + editorGroupsService.onDidLayout ), () => { }, 800)(this._savePartsSplash, this, this._disposables); + + configService.onDidChangeConfiguration(e => { + this._didChangeTitleBarStyle = e.affectsConfiguration('window.titleBarStyle'); + }, this, this._disposables); } dispose(): void { @@ -98,7 +107,7 @@ class PartsSplash { } private _shouldSaveLayoutInfo(): boolean { - return !isFullscreen() && !this._envService.isExtensionDevelopment; + return !isFullscreen() && !this._envService.isExtensionDevelopment && !this._didChangeTitleBarStyle; } private _removePartsSplash(): void { diff --git a/src/vs/workbench/contrib/stats/node/workspaceStats.ts b/src/vs/workbench/contrib/stats/node/workspaceStats.ts index 10ea1691f99..ea4cc981bee 100644 --- a/src/vs/workbench/contrib/stats/node/workspaceStats.ts +++ b/src/vs/workbench/contrib/stats/node/workspaceStats.ts @@ -368,7 +368,7 @@ export class WorkspaceStats implements IWorkbenchContribution { } return this.fileService.resolveFiles(folders.map(resource => ({ resource }))).then((files: IResolveFileResult[]) => { - const names = ([]).concat(...files.map(result => result.success ? (result.stat.children || []) : [])).map(c => c.name); + const names = ([]).concat(...files.map(result => result.success ? (result.stat!.children || []) : [])).map(c => c.name); const nameSet = names.reduce((s, n) => s.add(n.toLowerCase()), new Set()); if (participant) { @@ -664,7 +664,7 @@ export class WorkspaceStats implements IWorkbenchContribution { }); return this.fileService.resolveFiles(uris.map(resource => ({ resource }))).then( results => { - const names = ([]).concat(...results.map(result => result.success ? (result.stat.children || []) : [])).map(c => c.name); + const names = ([]).concat(...results.map(result => result.success ? (result.stat!.children || []) : [])).map(c => c.name); const referencesAzure = WorkspaceStats.searchArray(names, /azure/i); if (referencesAzure) { tags['node'] = true; diff --git a/src/vs/workbench/contrib/surveys/electron-browser/languageSurveys.contribution.ts b/src/vs/workbench/contrib/surveys/electron-browser/languageSurveys.contribution.ts index 318047f643b..447a3565881 100644 --- a/src/vs/workbench/contrib/surveys/electron-browser/languageSurveys.contribution.ts +++ b/src/vs/workbench/contrib/surveys/electron-browser/languageSurveys.contribution.ts @@ -40,13 +40,13 @@ class LanguageSurvey { const date = new Date().toDateString(); - if (storageService.getInteger(EDITED_LANGUAGE_COUNT_KEY, StorageScope.GLOBAL, 0) < data.editCount) { + if (storageService.getNumber(EDITED_LANGUAGE_COUNT_KEY, StorageScope.GLOBAL, 0) < data.editCount) { textFileService.models.onModelsSaved(e => { e.forEach(event => { if (event.kind === StateChange.SAVED) { const model = modelService.getModel(event.resource); if (model && model.getModeId() === data.languageId && date !== storageService.get(EDITED_LANGUAGE_DATE_KEY, StorageScope.GLOBAL)) { - const editedCount = storageService.getInteger(EDITED_LANGUAGE_COUNT_KEY, StorageScope.GLOBAL, 0) + 1; + const editedCount = storageService.getNumber(EDITED_LANGUAGE_COUNT_KEY, StorageScope.GLOBAL, 0) + 1; storageService.store(EDITED_LANGUAGE_COUNT_KEY, editedCount, StorageScope.GLOBAL); storageService.store(EDITED_LANGUAGE_DATE_KEY, date, StorageScope.GLOBAL); } @@ -60,7 +60,7 @@ class LanguageSurvey { return; } - const sessionCount = storageService.getInteger(SESSION_COUNT_KEY, StorageScope.GLOBAL, 0) + 1; + const sessionCount = storageService.getNumber(SESSION_COUNT_KEY, StorageScope.GLOBAL, 0) + 1; storageService.store(LAST_SESSION_DATE_KEY, date, StorageScope.GLOBAL); storageService.store(SESSION_COUNT_KEY, sessionCount, StorageScope.GLOBAL); @@ -68,7 +68,7 @@ class LanguageSurvey { return; } - if (storageService.getInteger(EDITED_LANGUAGE_COUNT_KEY, StorageScope.GLOBAL, 0) < data.editCount) { + if (storageService.getNumber(EDITED_LANGUAGE_COUNT_KEY, StorageScope.GLOBAL, 0) < data.editCount) { return; } diff --git a/src/vs/workbench/contrib/surveys/electron-browser/nps.contribution.ts b/src/vs/workbench/contrib/surveys/electron-browser/nps.contribution.ts index 7253741210e..c11fd38db20 100644 --- a/src/vs/workbench/contrib/surveys/electron-browser/nps.contribution.ts +++ b/src/vs/workbench/contrib/surveys/electron-browser/nps.contribution.ts @@ -39,7 +39,7 @@ class NPSContribution implements IWorkbenchContribution { return; } - const sessionCount = (storageService.getInteger(SESSION_COUNT_KEY, StorageScope.GLOBAL, 0) || 0) + 1; + const sessionCount = (storageService.getNumber(SESSION_COUNT_KEY, StorageScope.GLOBAL, 0) || 0) + 1; storageService.store(LAST_SESSION_DATE_KEY, date, StorageScope.GLOBAL); storageService.store(SESSION_COUNT_KEY, sessionCount, StorageScope.GLOBAL); diff --git a/src/vs/workbench/contrib/tasks/common/taskService.ts b/src/vs/workbench/contrib/tasks/common/taskService.ts index 8e24623e7eb..c592c6d64f6 100644 --- a/src/vs/workbench/contrib/tasks/common/taskService.ts +++ b/src/vs/workbench/contrib/tasks/common/taskService.ts @@ -38,10 +38,10 @@ export interface TaskFilter { } interface WorkspaceTaskResult { - set: TaskSet; + set: TaskSet | undefined; configurations: { byIdentifier: IStringDictionary; - }; + } | undefined; hasErrors: boolean; } diff --git a/src/vs/workbench/contrib/tasks/common/taskSystem.ts b/src/vs/workbench/contrib/tasks/common/taskSystem.ts index 722fcffdc93..9a2fb0787e4 100644 --- a/src/vs/workbench/contrib/tasks/common/taskSystem.ts +++ b/src/vs/workbench/contrib/tasks/common/taskSystem.ts @@ -93,7 +93,7 @@ export interface ITaskExecuteResult { } export interface ITaskResolver { - resolve(workspaceFolder: IWorkspaceFolder, identifier: string | KeyedTaskIdentifier): Task; + resolve(workspaceFolder: IWorkspaceFolder, identifier: string | KeyedTaskIdentifier | undefined): Task | undefined; } export interface TaskTerminateResponse extends TerminateResponse { @@ -122,7 +122,7 @@ export interface TaskSystemInfo { } export interface TaskSystemInfoResovler { - (workspaceFolder: IWorkspaceFolder): TaskSystemInfo; + (workspaceFolder: IWorkspaceFolder): TaskSystemInfo | undefined; } export interface ITaskSystem { diff --git a/src/vs/workbench/contrib/tasks/common/tasks.ts b/src/vs/workbench/contrib/tasks/common/tasks.ts index 64284fe4728..e8489a6f345 100644 --- a/src/vs/workbench/contrib/tasks/common/tasks.ts +++ b/src/vs/workbench/contrib/tasks/common/tasks.ts @@ -514,7 +514,7 @@ export abstract class CommonTask { return 'unknown'; } - public matches(key: string | KeyedTaskIdentifier, compareId: boolean = false): boolean { + public matches(key: string | KeyedTaskIdentifier | undefined, compareId: boolean = false): boolean { if (key === undefined) { return false; } @@ -631,7 +631,7 @@ export class CustomTask extends CommonTask { return JSON.stringify(key); } - public getWorkspaceFolder(): IWorkspaceFolder | undefined { + public getWorkspaceFolder(): IWorkspaceFolder { return this._source.config.workspaceFolder; } diff --git a/src/vs/workbench/contrib/tasks/electron-browser/task.contribution.ts b/src/vs/workbench/contrib/tasks/electron-browser/task.contribution.ts index b23a34581ac..bdc28aa2723 100644 --- a/src/vs/workbench/contrib/tasks/electron-browser/task.contribution.ts +++ b/src/vs/workbench/contrib/tasks/electron-browser/task.contribution.ts @@ -382,7 +382,7 @@ class ProblemReporter implements TaskConfig.IProblemReporter { interface WorkspaceFolderConfigurationResult { workspaceFolder: IWorkspaceFolder; - config: TaskConfig.ExternalTaskRunnerConfiguration; + config: TaskConfig.ExternalTaskRunnerConfiguration | undefined; hasErrors: boolean; } @@ -426,7 +426,7 @@ class TaskMap { } interface TaskQuickPickEntry extends IQuickPickItem { - task: Task | null; + task: Task | undefined | null; } class TaskService extends Disposable implements ITaskService { @@ -493,7 +493,7 @@ class TaskService extends Disposable implements ITaskService { this._workspaceTasksPromise = undefined; this._taskSystem = undefined; this._taskSystemListener = undefined; - this._outputChannel = this.outputService.getChannel(TaskService.OutputChannelId); + this._outputChannel = this.outputService.getChannel(TaskService.OutputChannelId)!; this._providers = new Map(); this._taskSystemInfos = new Map(); this._register(this.contextService.onDidChangeWorkspaceFolders(() => { @@ -865,22 +865,21 @@ class TaskService extends Disposable implements ITaskService { } public run(task: Task | undefined, options?: ProblemMatcherRunOptions): Promise { + if (!task) { + throw new TaskError(Severity.Info, nls.localize('TaskServer.noTask', 'Task to execute is undefined'), TaskErrors.TaskNotFound); + } return this.getGroupedTasks().then((grouped) => { - if (!task) { - throw new TaskError(Severity.Info, nls.localize('TaskServer.noTask', 'Requested task {0} to execute not found.', task.configurationProperties.name), TaskErrors.TaskNotFound); - } else { - let resolver = this.createResolver(grouped); - if (options && options.attachProblemMatcher && this.shouldAttachProblemMatcher(task) && !InMemoryTask.is(task)) { - return this.attachProblemMatcher(task).then((toExecute) => { - if (toExecute) { - return this.executeTask(toExecute, resolver); - } else { - return Promise.resolve(undefined); - } - }); - } - return this.executeTask(task, resolver); + let resolver = this.createResolver(grouped); + if (options && options.attachProblemMatcher && this.shouldAttachProblemMatcher(task) && !InMemoryTask.is(task)) { + return this.attachProblemMatcher(task).then((toExecute) => { + if (toExecute) { + return this.executeTask(toExecute, resolver); + } else { + return Promise.resolve(undefined); + } + }); } + return this.executeTask(task, resolver); }).then(value => value, (error) => { this.handleError(error); return Promise.reject(error); @@ -898,7 +897,7 @@ class TaskService extends Disposable implements ITaskService { return false; } if (ContributedTask.is(task)) { - return !task.hasDefinedMatchers && task.configurationProperties.problemMatchers.length === 0; + return !task.hasDefinedMatchers && !!task.configurationProperties.problemMatchers && (task.configurationProperties.problemMatchers.length === 0); } if (CustomTask.is(task)) { let configProperties: TaskConfig.ConfigurationProperties = task._source.config.element; @@ -907,9 +906,9 @@ class TaskService extends Disposable implements ITaskService { return false; } - private attachProblemMatcher(task: ContributedTask | CustomTask): Promise { + private attachProblemMatcher(task: ContributedTask | CustomTask): Promise { interface ProblemMatcherPickEntry extends IQuickPickItem { - matcher: NamedProblemMatcher; + matcher: NamedProblemMatcher | undefined; never?: boolean; learnMore?: boolean; } @@ -930,7 +929,13 @@ class TaskService extends Disposable implements ITaskService { } } if (entries.length > 0) { - entries = entries.sort((a, b) => a.label.localeCompare(b.label)); + entries = entries.sort((a, b) => { + if (a.label && b.label) { + return a.label.localeCompare(b.label); + } else { + return 0; + } + }); entries.unshift({ type: 'separator', label: nls.localize('TaskService.associate', 'associate') }); entries.unshift( { label: nls.localize('TaskService.attachProblemMatcher.continueWithout', 'Continue without scanning the task output'), matcher: undefined }, @@ -1002,7 +1007,7 @@ class TaskService extends Disposable implements ITaskService { } public customize(task: ContributedTask | CustomTask, properties?: CustomizationProperties, openConfig?: boolean): Promise { - let workspaceFolder = task.getWorkspaceFolder(); + const workspaceFolder = task.getWorkspaceFolder(); if (!workspaceFolder) { return Promise.resolve(undefined); } @@ -1024,7 +1029,7 @@ class TaskService extends Disposable implements ITaskService { }; let identifier: TaskConfig.TaskIdentifier = Objects.assign(Object.create(null), task.defines); delete identifier['_key']; - Object.keys(identifier).forEach(key => toCustomize[key] = identifier[key]); + Object.keys(identifier).forEach(key => toCustomize![key] = identifier[key]); if (task.configurationProperties.problemMatchers && task.configurationProperties.problemMatchers.length > 0 && Types.isStringArray(task.configurationProperties.problemMatchers)) { toCustomize.problemMatcher = task.configurationProperties.problemMatchers; } @@ -1040,7 +1045,7 @@ class TaskService extends Disposable implements ITaskService { } } } else { - if (toCustomize.problemMatcher === undefined && task.configurationProperties.problemMatchers === undefined || task.configurationProperties.problemMatchers.length === 0) { + if (toCustomize.problemMatcher === undefined && task.configurationProperties.problemMatchers === undefined || (task.configurationProperties.problemMatchers && task.configurationProperties.problemMatchers.length === 0)) { toCustomize.problemMatcher = []; } } @@ -1062,7 +1067,7 @@ class TaskService extends Disposable implements ITaskService { promise = this.fileService.createFile(workspaceFolder.toResource('.vscode/tasks.json'), content).then(() => { }); } else { // We have a global task configuration - if (index === -1) { + if ((index === -1) && properties) { if (properties.problemMatcher !== undefined) { fileConfig.problemMatcher = properties.problemMatcher; promise = this.writeConfiguration(workspaceFolder, 'tasks.problemMatchers', fileConfig.problemMatcher); @@ -1108,7 +1113,7 @@ class TaskService extends Disposable implements ITaskService { }); } - private writeConfiguration(workspaceFolder: IWorkspaceFolder, key: string, value: any): Promise { + private writeConfiguration(workspaceFolder: IWorkspaceFolder, key: string, value: any): Promise | undefined { if (this.contextService.getWorkbenchState() === WorkbenchState.FOLDER) { return this.configurationService.updateValue(key, value, { resource: workspaceFolder.uri }, ConfigurationTarget.WORKSPACE); } else if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) { @@ -1119,7 +1124,7 @@ class TaskService extends Disposable implements ITaskService { } public openConfig(task: CustomTask | undefined): Promise { - let resource: URI; + let resource: URI | undefined; if (task) { resource = task.getWorkspaceFolder().toResource(task._source.config.file); } else { @@ -1133,7 +1138,7 @@ class TaskService extends Disposable implements ITaskService { }).then(() => undefined); } - private createRunnableTask(tasks: TaskMap, group: TaskGroup): { task: Task; resolver: ITaskResolver } { + private createRunnableTask(tasks: TaskMap, group: TaskGroup): { task: Task; resolver: ITaskResolver } | undefined { interface ResolverData { id: Map; label: Map; @@ -1156,7 +1161,9 @@ class TaskService extends Disposable implements ITaskService { for (let task of tasks) { data.id.set(task._id, task); data.label.set(task._label, task); - data.identifier.set(task.configurationProperties.identifier, task); + if (task.configurationProperties.identifier) { + data.identifier.set(task.configurationProperties.identifier, task); + } if (group && task.configurationProperties.group === group) { if (task._source.kind === TaskSourceKind.Workspace) { workspaceTasks.push(task); @@ -1199,7 +1206,7 @@ class TaskService extends Disposable implements ITaskService { { reevaluateOnRerun: true }, { identifier: id, - dependsOn: extensionTasks.map((task) => { return { workspaceFolder: task.getWorkspaceFolder(), task: task._id }; }), + dependsOn: extensionTasks.map((extensionTask) => { return { workspaceFolder: extensionTask.getWorkspaceFolder()!, task: extensionTask._id }; }), name: id, } ); @@ -1223,7 +1230,9 @@ class TaskService extends Disposable implements ITaskService { } for (let task of tasks) { data.label.set(task._label, task); - data.identifier.set(task.configurationProperties.identifier, task); + if (task.configurationProperties.identifier) { + data.identifier.set(task.configurationProperties.identifier, task); + } let keyedIdentifier = task.getDefinition(true); if (keyedIdentifier !== undefined) { data.taskIdentifier.set(keyedIdentifier._key, task); @@ -1231,9 +1240,9 @@ class TaskService extends Disposable implements ITaskService { } }); return { - resolve: (workspaceFolder: IWorkspaceFolder, identifier: string | TaskIdentifier) => { + resolve: (workspaceFolder: IWorkspaceFolder, identifier: string | TaskIdentifier | undefined) => { let data = resolverData.get(workspaceFolder.uri.toString()); - if (!data) { + if (!data || !identifier) { return undefined; } if (Types.isString(identifier)) { @@ -1269,7 +1278,7 @@ class TaskService extends Disposable implements ITaskService { } if (executeResult.kind === TaskExecuteKind.Active) { let active = executeResult.active; - if (active.same) { + if (active && active.same) { let message; if (active.background) { message = nls.localize('TaskSystem.activeSame.background', 'The task \'{0}\' is already active and in background mode.', executeResult.task.getQualifiedLabel()); @@ -1349,13 +1358,13 @@ class TaskService extends Disposable implements ITaskService { system.hasErrors(this._configHasErrors); this._taskSystem = system; } - this._taskSystemListener = this._taskSystem.onDidStateChange((event) => { + this._taskSystemListener = this._taskSystem!.onDidStateChange((event) => { if (this._taskSystem) { this._taskRunningState.set(this._taskSystem.isActiveSync()); } this._onDidStateChange.fire(event); }); - return this._taskSystem; + return this._taskSystem!; } private getGroupedTasks(): Promise { @@ -1470,7 +1479,7 @@ class TaskService extends Disposable implements ITaskService { result.add(key, ...folderTasks.set.tasks); } unUsedConfigurations.forEach((value) => { - let configuringTask = configurations.byIdentifier[value]; + let configuringTask = configurations!.byIdentifier[value]; this._outputChannel.append(nls.localize( 'TaskService.noConfiguration', 'Error: The {0} task detection didn\'t contribute a task for the following configuration:\n{1}\nThe task will be ignored.\n', @@ -1491,7 +1500,10 @@ class TaskService extends Disposable implements ITaskService { let result: TaskMap = new TaskMap(); for (let set of contributedTaskSets) { for (let task of set.tasks) { - result.add(task.getWorkspaceFolder(), task); + const folder = task.getWorkspaceFolder(); + if (folder) { + result.add(folder, task); + } } } return result; @@ -1499,14 +1511,14 @@ class TaskService extends Disposable implements ITaskService { }); } - private getLegacyTaskConfigurations(workspaceTasks: TaskSet): IStringDictionary { + private getLegacyTaskConfigurations(workspaceTasks: TaskSet): IStringDictionary | undefined { let result: IStringDictionary | undefined; - function getResult() { + function getResult(): IStringDictionary { if (result) { return result; } result = Object.create(null); - return result; + return result!; } for (let task of workspaceTasks.tasks) { if (CustomTask.is(task)) { @@ -1531,11 +1543,11 @@ class TaskService extends Disposable implements ITaskService { } this.updateWorkspaceTasks(runSource); if (runSource === TaskRunSource.User) { - this._workspaceTasksPromise.then(workspaceFolderTasks => { + this._workspaceTasksPromise!.then(workspaceFolderTasks => { RunAutomaticTasks.promptForPermission(this, this.storageService, this.notificationService, workspaceFolderTasks); }); } - return this._workspaceTasksPromise; + return this._workspaceTasksPromise!; } private updateWorkspaceTasks(runSource: TaskRunSource = TaskRunSource.User): void { @@ -1555,7 +1567,7 @@ class TaskService extends Disposable implements ITaskService { if (this.workspaceFolders.length === 0) { return Promise.resolve(new Map()); } else { - let promises: Promise[] = []; + let promises: Promise[] = []; for (let folder of this.workspaceFolders) { promises.push(this.computeWorkspaceFolderTasks(folder, runSource).then((value) => value, () => undefined)); } @@ -1580,9 +1592,9 @@ class TaskService extends Disposable implements ITaskService { return Promise.resolve({ workspaceFolder, set: undefined, configurations: undefined, hasErrors: workspaceFolderConfiguration ? workspaceFolderConfiguration.hasErrors : false }); } return ProblemMatcherRegistry.onReady().then((): WorkspaceFolderTaskResult => { - let taskSystemInfo: TaskSystemInfo = this._taskSystemInfos.get(workspaceFolder.uri.scheme); + let taskSystemInfo: TaskSystemInfo | undefined = this._taskSystemInfos.get(workspaceFolder.uri.scheme); let problemReporter = new ProblemReporter(this._outputChannel); - let parseResult = TaskConfig.parse(workspaceFolder, taskSystemInfo ? taskSystemInfo.platform : Platform.platform, workspaceFolderConfiguration.config, problemReporter); + let parseResult = TaskConfig.parse(workspaceFolder, taskSystemInfo ? taskSystemInfo.platform : Platform.platform, workspaceFolderConfiguration.config!, problemReporter); let hasErrors = false; if (!parseResult.validationStatus.isOK()) { hasErrors = true; @@ -1624,19 +1636,26 @@ class TaskService extends Disposable implements ITaskService { if (!detectedConfig) { return { workspaceFolder, config, hasErrors }; } - let result: TaskConfig.ExternalTaskRunnerConfiguration = Objects.deepClone(config); + let result: TaskConfig.ExternalTaskRunnerConfiguration = Objects.deepClone(config)!; let configuredTasks: IStringDictionary = Object.create(null); - if (!result.tasks) { + const resultTasks = result.tasks; + if (!resultTasks) { if (detectedConfig.tasks) { result.tasks = detectedConfig.tasks; } } else { - result.tasks.forEach(task => configuredTasks[task.taskName] = task); - detectedConfig.tasks.forEach((task) => { - if (!configuredTasks[task.taskName]) { - result.tasks.push(task); + resultTasks.forEach(task => { + if (task.taskName) { + configuredTasks[task.taskName] = task; } }); + if (detectedConfig.tasks) { + detectedConfig.tasks.forEach((task) => { + if (task.taskName && !configuredTasks[task.taskName]) { + resultTasks.push(task); + } + }); + } } return { workspaceFolder, config: result, hasErrors }; }); @@ -1646,7 +1665,7 @@ class TaskService extends Disposable implements ITaskService { } else { return new ProcessRunnerDetector(workspaceFolder, this.fileService, this.contextService, this.configurationResolverService).detect(true).then((value) => { let hasErrors = this.printStderr(value.stderr); - return { workspaceFolder, config: value.config, hasErrors }; + return { workspaceFolder, config: value.config!, hasErrors }; }); } } @@ -1694,7 +1713,7 @@ class TaskService extends Disposable implements ITaskService { return TaskConfig.JsonSchemaVersion.from(config); } - private getConfiguration(workspaceFolder: IWorkspaceFolder): { config: TaskConfig.ExternalTaskRunnerConfiguration; hasParseErrors: boolean } { + private getConfiguration(workspaceFolder: IWorkspaceFolder): { config: TaskConfig.ExternalTaskRunnerConfiguration | undefined; hasParseErrors: boolean } { let result = this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? Objects.deepClone(this.configurationService.getValue('tasks', { resource: workspaceFolder.uri })) : undefined; @@ -1780,7 +1799,7 @@ class TaskService extends Disposable implements ITaskService { return terminatePromise.then(res => { if (res.confirmed) { - return this._taskSystem.terminateAll().then((responses) => { + return this._taskSystem!.terminateAll().then((responses) => { let success = true; let code: number | undefined = undefined; for (let response of responses) { @@ -1792,7 +1811,7 @@ class TaskService extends Disposable implements ITaskService { } } if (success) { - this._taskSystem = null; + this._taskSystem = undefined; this.disposeTaskSystemListeners(); return false; // no veto } else if (code && code === TerminateResponseCode.ProcessNotFound) { @@ -1932,7 +1951,7 @@ class TaskService extends Disposable implements ITaskService { return entries; } - private showQuickPick(tasks: Promise | Task[], placeHolder: string, defaultEntry?: TaskQuickPickEntry, group: boolean = false, sort: boolean = false, selectedEntry?: TaskQuickPickEntry): Promise { + private showQuickPick(tasks: Promise | Task[], placeHolder: string, defaultEntry?: TaskQuickPickEntry, group: boolean = false, sort: boolean = false, selectedEntry?: TaskQuickPickEntry): Promise { let _createEntries = (): Promise => { if (Array.isArray(tasks)) { return Promise.resolve(this.createTaskQuickPickEntries(tasks, group, sort, selectedEntry)); @@ -2044,7 +2063,7 @@ class TaskService extends Disposable implements ITaskService { return this.handleExecuteResult(executeResult); } else { this.doRunTaskCommand(); - return undefined; + return Promise.resolve(undefined); } }); }); @@ -2266,7 +2285,7 @@ class TaskService extends Disposable implements ITaskService { } private getTaskIdentifier(arg?: any): string | KeyedTaskIdentifier | undefined { - let result: string | KeyedTaskIdentifier = undefined; + let result: string | KeyedTaskIdentifier | undefined = undefined; if (Types.isString(arg)) { result = arg; } else if (arg && Types.isString((arg as TaskIdentifier).type)) { @@ -2295,7 +2314,7 @@ class TaskService extends Disposable implements ITaskService { } return this.quickInputService.pick(getTaskTemplates(), { placeHolder: nls.localize('TaskService.template', 'Select a Task Template') }).then((selection) => { if (!selection) { - return undefined; + return Promise.resolve(undefined); } let content = selection.content; let editorConfig = this.configurationService.getValue(); @@ -2345,7 +2364,7 @@ class TaskService extends Disposable implements ITaskService { return candidate && !!candidate.task; } - let stats = this.contextService.getWorkspace().folders.map>((folder) => { + let stats = this.contextService.getWorkspace().folders.map>((folder) => { return this.fileService.resolveFile(folder.toResource('.vscode/tasks.json')).then(stat => stat, () => undefined); }); @@ -2443,7 +2462,7 @@ class TaskService extends Disposable implements ITaskService { this.showQuickPick(tasks, nls.localize('TaskService.pickDefaultBuildTask', 'Select the task to be used as the default build task'), undefined, true, false, selectedEntry). then((task) => { - if (task === undefined) { + if ((task === undefined) || (task === null)) { return; } if (task === selectedTask && CustomTask.is(task)) { @@ -2529,7 +2548,7 @@ class TaskService extends Disposable implements ITaskService { if (task === undefined || task === null) { return; } - this._taskSystem.revealTask(task); + this._taskSystem!.revealTask(task); }); } } diff --git a/src/vs/workbench/contrib/tasks/electron-browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/electron-browser/terminalTaskSystem.ts index 14a2af17131..025c9479cab 100644 --- a/src/vs/workbench/contrib/tasks/electron-browser/terminalTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/electron-browser/terminalTaskSystem.ts @@ -9,7 +9,6 @@ import * as Objects from 'vs/base/common/objects'; import * as Types from 'vs/base/common/types'; import * as Platform from 'vs/base/common/platform'; import * as Async from 'vs/base/common/async'; -import * as os from 'os'; import { IStringDictionary, values } from 'vs/base/common/collections'; import { LinkedMap, Touch } from 'vs/base/common/map'; import Severity from 'vs/base/common/severity'; @@ -28,7 +27,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; import { ITerminalService, ITerminalInstance, IShellLaunchConfig } from 'vs/workbench/contrib/terminal/common/terminal'; -import { IOutputService, IOutputChannel } from 'vs/workbench/contrib/output/common/output'; +import { IOutputService } from 'vs/workbench/contrib/output/common/output'; import { StartStopProblemCollector, WatchingProblemCollector, ProblemCollectorEventKind } from 'vs/workbench/contrib/tasks/common/problemCollectors'; import { Task, CustomTask, ContributedTask, RevealKind, CommandOptions, ShellConfiguration, RuntimeType, PanelKind, @@ -42,6 +41,7 @@ import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; import { URI } from 'vs/base/common/uri'; import { IWindowService } from 'vs/platform/windows/common/windows'; import { Schemas } from 'vs/base/common/network'; +import { getWindowsBuildNumber } from 'vs/workbench/contrib/terminal/node/terminal'; interface TerminalData { terminal: ITerminalInstance; @@ -148,7 +148,6 @@ export class TerminalTaskSystem implements ITaskSystem { 'win32': TerminalTaskSystem.shellQuotes['powershell'] }; - private outputChannel: IOutputChannel; private activeTasks: IStringDictionary; private terminals: IStringDictionary; private idleTaskTerminals: LinkedMap; @@ -166,10 +165,9 @@ export class TerminalTaskSystem implements ITaskSystem { private telemetryService: ITelemetryService, private contextService: IWorkspaceContextService, private windowService: IWindowService, - outputChannelId: string, + private outputChannelId: string, taskSystemInfoResolver: TaskSystemInfoResovler) { - this.outputChannel = this.outputService.getChannel(outputChannelId); this.activeTasks = Object.create(null); this.terminals = Object.create(null); this.idleTaskTerminals = new LinkedMap(); @@ -184,11 +182,11 @@ export class TerminalTaskSystem implements ITaskSystem { } public log(value: string): void { - this.outputChannel.append(value + '\n'); + this.appendOutput(value + '\n'); } protected showOutput(): void { - this.outputService.showChannel(this.outputChannel.id, true); + this.outputService.showChannel(this.outputChannelId, true); } public run(task: Task, resolver: ITaskResolver, trigger: string = Triggers.command): ITaskExecuteResult { @@ -747,7 +745,7 @@ export class TerminalTaskSystem implements ITaskSystem { if (!shellSpecified) { toAdd.push('-Command'); } - } else if ((basename === 'bash.exe') || (basename === 'zsh.exe') || ((basename === 'wsl.exe') && (this.getWindowsBuildNumber() < 17763))) { // See https://github.com/Microsoft/vscode/issues/67855 + } else if ((basename === 'bash.exe') || (basename === 'zsh.exe') || ((basename === 'wsl.exe') && (getWindowsBuildNumber() < 17763))) { // See https://github.com/Microsoft/vscode/issues/67855 windowsShellArgs = false; if (!shellSpecified) { toAdd.push('-c'); @@ -1156,7 +1154,7 @@ export class TerminalTaskSystem implements ITaskSystem { matcher = value; } if (!matcher) { - this.outputChannel.append(nls.localize('unkownProblemMatcher', 'Problem matcher {0} can\'t be resolved. The matcher will be ignored')); + this.appendOutput(nls.localize('unkownProblemMatcher', 'Problem matcher {0} can\'t be resolved. The matcher will be ignored')); return; } let taskSystemInfo: TaskSystemInfo | undefined = resolver.taskSystemInfo; @@ -1215,15 +1213,6 @@ export class TerminalTaskSystem implements ITaskSystem { return result; } - private getWindowsBuildNumber(): number { - const osVersion = (/(\d+)\.(\d+)\.(\d+)/g).exec(os.release()); - let buildNumber: number = 0; - if (osVersion && osVersion.length === 4) { - buildNumber = parseInt(osVersion[3]); - } - return buildNumber; - } - private registerLinkMatchers(terminal: ITerminalInstance, problemMatchers: ProblemMatcher[]): number[] { let result: number[] = []; /* @@ -1285,4 +1274,11 @@ export class TerminalTaskSystem implements ITaskSystem { } return 'other'; } + + private appendOutput(output: string): void { + const outputChannel = this.outputService.getChannel(this.outputChannelId); + if (outputChannel) { + outputChannel.append(output); + } + } } diff --git a/src/vs/workbench/contrib/tasks/node/processTaskSystem.ts b/src/vs/workbench/contrib/tasks/node/processTaskSystem.ts index 6098accc28c..4e128d5ded5 100644 --- a/src/vs/workbench/contrib/tasks/node/processTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/node/processTaskSystem.ts @@ -15,7 +15,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { SuccessData, ErrorData } from 'vs/base/common/processes'; import { LineProcess, LineData } from 'vs/base/node/processes'; -import { IOutputService, IOutputChannel } from 'vs/workbench/contrib/output/common/output'; +import { IOutputService } from 'vs/workbench/contrib/output/common/output'; import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; import { IMarkerService } from 'vs/platform/markers/common/markers'; @@ -48,8 +48,6 @@ export class ProcessTaskSystem implements ITaskSystem { private telemetryService: ITelemetryService; private configurationResolverService: IConfigurationResolverService; - private outputChannel: IOutputChannel; - private errorsShown: boolean; private childProcess: LineProcess | null; private activeTask: CustomTask | null; @@ -58,7 +56,7 @@ export class ProcessTaskSystem implements ITaskSystem { private readonly _onDidStateChange: Emitter; constructor(markerService: IMarkerService, modelService: IModelService, telemetryService: ITelemetryService, - outputService: IOutputService, configurationResolverService: IConfigurationResolverService, outputChannelId: string) { + outputService: IOutputService, configurationResolverService: IConfigurationResolverService, private outputChannelId: string) { this.markerService = markerService; this.modelService = modelService; this.outputService = outputService; @@ -68,7 +66,6 @@ export class ProcessTaskSystem implements ITaskSystem { this.childProcess = null; this.activeTask = null; this.activeTaskPromise = null; - this.outputChannel = this.outputService.getChannel(outputChannelId); this.errorsShown = true; this._onDidStateChange = new Emitter(); } @@ -188,10 +185,10 @@ export class ProcessTaskSystem implements ITaskSystem { throw err; } else if (err instanceof Error) { let error = err; - this.outputChannel.append(error.message); + this.appendOutput(error.message); throw new TaskError(Severity.Error, error.message, TaskErrors.UnknownError); } else { - this.outputChannel.append(err.toString()); + this.appendOutput(err.toString()); throw new TaskError(Severity.Error, nls.localize('TaskRunnerSystem.unknownError', 'A unknown error has occurred while executing a task. See task output log for details.'), TaskErrors.UnknownError); } } @@ -256,7 +253,7 @@ export class ProcessTaskSystem implements ITaskSystem { let processStartedSignaled: boolean = false; const onProgress = (progress: LineData) => { let line = Strings.removeAnsiEscapeCodes(progress.line); - this.outputChannel.append(line + '\n'); + this.appendOutput(line + '\n'); watchingProblemMatcher.processLine(line); if (delayer === null) { delayer = new Async.Delayer(3000); @@ -320,7 +317,7 @@ export class ProcessTaskSystem implements ITaskSystem { let processStartedSignaled: boolean = false; const onProgress = (progress) => { let line = Strings.removeAnsiEscapeCodes(progress.line); - this.outputChannel.append(line + '\n'); + this.appendOutput(line + '\n'); startStopProblemMatcher.processLine(line); }; const startPromise = this.childProcess.start(onProgress); @@ -367,16 +364,16 @@ export class ProcessTaskSystem implements ITaskSystem { if (errorData.error && !errorData.terminated) { let args: string = task.command.args ? task.command.args.join(' ') : ''; this.log(nls.localize('TaskRunnerSystem.childProcessError', 'Failed to launch external program {0} {1}.', JSON.stringify(task.command.name), args)); - this.outputChannel.append(errorData.error.message); + this.appendOutput(errorData.error.message); makeVisible = true; } if (errorData.stdout) { - this.outputChannel.append(errorData.stdout); + this.appendOutput(errorData.stdout); makeVisible = true; } if (errorData.stderr) { - this.outputChannel.append(errorData.stderr); + this.appendOutput(errorData.stderr); makeVisible = true; } makeVisible = this.checkTerminated(task, errorData) || makeVisible; @@ -436,7 +433,7 @@ export class ProcessTaskSystem implements ITaskSystem { matcher = value; } if (!matcher) { - this.outputChannel.append(nls.localize('unkownProblemMatcher', 'Problem matcher {0} can\'t be resolved. The matcher will be ignored')); + this.appendOutput(nls.localize('unkownProblemMatcher', 'Problem matcher {0} can\'t be resolved. The matcher will be ignored')); return; } if (!matcher.filePrefix) { @@ -455,14 +452,24 @@ export class ProcessTaskSystem implements ITaskSystem { } public log(value: string): void { - this.outputChannel.append(value + '\n'); + this.appendOutput(value + '\n'); } private showOutput(): void { - this.outputService.showChannel(this.outputChannel.id, true); + this.outputService.showChannel(this.outputChannelId, true); + } + + private appendOutput(output: string): void { + const outputChannel = this.outputService.getChannel(this.outputChannelId); + if (outputChannel) { + outputChannel.append(output); + } } private clearOutput(): void { - this.outputChannel.clear(); + const outputChannel = this.outputService.getChannel(this.outputChannelId); + if (outputChannel) { + outputChannel.clear(); + } } } diff --git a/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts b/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts new file mode 100644 index 00000000000..b2ca963f342 --- /dev/null +++ b/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts @@ -0,0 +1,90 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Registry } from 'vs/platform/registry/common/platform'; +import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry, IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { LifecyclePhase, ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; +import { IActivityService } from 'vs/workbench/services/activity/common/activity'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { IWindowService } from 'vs/platform/windows/common/windows'; +import { language } from 'vs/base/common/platform'; +import { Disposable } from 'vs/base/common/lifecycle'; +import ErrorTelemetry from 'vs/platform/telemetry/browser/errorTelemetry'; +import { configurationTelemetry } from 'vs/platform/telemetry/common/telemetryUtils'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; + +export class TelemetryContribution extends Disposable implements IWorkbenchContribution { + + constructor( + @ITelemetryService telemetryService: ITelemetryService, + @IWorkspaceContextService contextService: IWorkspaceContextService, + @IActivityService activityService: IActivityService, + @ILifecycleService lifecycleService: ILifecycleService, + @IEditorService editorService: IEditorService, + @IKeybindingService keybindingsService: IKeybindingService, + @IWorkbenchThemeService themeService: IWorkbenchThemeService, + @IWindowService windowService: IWindowService, + @IConfigurationService configurationService: IConfigurationService, + @IViewletService viewletService: IViewletService + ) { + super(); + + const { filesToOpen, filesToCreate, filesToDiff } = windowService.getConfiguration(); + const activeViewlet = viewletService.getActiveViewlet(); + + /* __GDPR__ + "workspaceLoad" : { + "userAgent" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "windowSize.innerHeight": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "windowSize.innerWidth": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "windowSize.outerHeight": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "windowSize.outerWidth": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "emptyWorkbench": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workbench.filesToOpen": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workbench.filesToCreate": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workbench.filesToDiff": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "customKeybindingsCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "theme": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "language": { "classification": "SystemMetaData", "purpose": "BusinessInsight" }, + "pinnedViewlets": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "restoredViewlet": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "restoredEditors": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "pinnedViewlets": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "startupKind": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } + } + */ + telemetryService.publicLog('workspaceLoad', { + userAgent: navigator.userAgent, + windowSize: { innerHeight: window.innerHeight, innerWidth: window.innerWidth, outerHeight: window.outerHeight, outerWidth: window.outerWidth }, + emptyWorkbench: contextService.getWorkbenchState() === WorkbenchState.EMPTY, + 'workbench.filesToOpen': filesToOpen && filesToOpen.length || 0, + 'workbench.filesToCreate': filesToCreate && filesToCreate.length || 0, + 'workbench.filesToDiff': filesToDiff && filesToDiff.length || 0, + customKeybindingsCount: keybindingsService.customKeybindingsCount(), + theme: themeService.getColorTheme().id, + language, + pinnedViewlets: activityService.getPinnedViewletIds(), + restoredViewlet: activeViewlet ? activeViewlet.getId() : undefined, + restoredEditors: editorService.visibleEditors.length, + startupKind: lifecycleService.startupKind + }); + + // Error Telemetry + this._register(new ErrorTelemetry(telemetryService)); + + // Configuration Telemetry + this._register(configurationTelemetry(telemetryService, configurationService)); + + // Lifecycle + this._register(lifecycleService.onShutdown(() => this.dispose())); + } +} + +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(TelemetryContribution, LifecyclePhase.Restored); \ No newline at end of file diff --git a/src/vs/workbench/contrib/terminal/electron-browser/media/configure-inverse.svg b/src/vs/workbench/contrib/terminal/browser/media/configure-inverse.svg similarity index 100% rename from src/vs/workbench/contrib/terminal/electron-browser/media/configure-inverse.svg rename to src/vs/workbench/contrib/terminal/browser/media/configure-inverse.svg diff --git a/src/vs/workbench/contrib/terminal/electron-browser/media/configure.svg b/src/vs/workbench/contrib/terminal/browser/media/configure.svg similarity index 100% rename from src/vs/workbench/contrib/terminal/electron-browser/media/configure.svg rename to src/vs/workbench/contrib/terminal/browser/media/configure.svg diff --git a/src/vs/workbench/contrib/terminal/electron-browser/media/kill-inverse.svg b/src/vs/workbench/contrib/terminal/browser/media/kill-inverse.svg similarity index 100% rename from src/vs/workbench/contrib/terminal/electron-browser/media/kill-inverse.svg rename to src/vs/workbench/contrib/terminal/browser/media/kill-inverse.svg diff --git a/src/vs/workbench/contrib/terminal/electron-browser/media/kill.svg b/src/vs/workbench/contrib/terminal/browser/media/kill.svg similarity index 100% rename from src/vs/workbench/contrib/terminal/electron-browser/media/kill.svg rename to src/vs/workbench/contrib/terminal/browser/media/kill.svg diff --git a/src/vs/workbench/contrib/terminal/electron-browser/media/new-inverse.svg b/src/vs/workbench/contrib/terminal/browser/media/new-inverse.svg similarity index 100% rename from src/vs/workbench/contrib/terminal/electron-browser/media/new-inverse.svg rename to src/vs/workbench/contrib/terminal/browser/media/new-inverse.svg diff --git a/src/vs/workbench/contrib/terminal/electron-browser/media/new.svg b/src/vs/workbench/contrib/terminal/browser/media/new.svg similarity index 100% rename from src/vs/workbench/contrib/terminal/electron-browser/media/new.svg rename to src/vs/workbench/contrib/terminal/browser/media/new.svg diff --git a/src/vs/workbench/contrib/terminal/electron-browser/media/scrollbar.css b/src/vs/workbench/contrib/terminal/browser/media/scrollbar.css similarity index 100% rename from src/vs/workbench/contrib/terminal/electron-browser/media/scrollbar.css rename to src/vs/workbench/contrib/terminal/browser/media/scrollbar.css diff --git a/src/vs/workbench/contrib/terminal/electron-browser/media/split-horizontal-inverse.svg b/src/vs/workbench/contrib/terminal/browser/media/split-horizontal-inverse.svg similarity index 100% rename from src/vs/workbench/contrib/terminal/electron-browser/media/split-horizontal-inverse.svg rename to src/vs/workbench/contrib/terminal/browser/media/split-horizontal-inverse.svg diff --git a/src/vs/workbench/contrib/terminal/electron-browser/media/split-horizontal.svg b/src/vs/workbench/contrib/terminal/browser/media/split-horizontal.svg similarity index 100% rename from src/vs/workbench/contrib/terminal/electron-browser/media/split-horizontal.svg rename to src/vs/workbench/contrib/terminal/browser/media/split-horizontal.svg diff --git a/src/vs/workbench/contrib/terminal/electron-browser/media/split-inverse.svg b/src/vs/workbench/contrib/terminal/browser/media/split-inverse.svg similarity index 100% rename from src/vs/workbench/contrib/terminal/electron-browser/media/split-inverse.svg rename to src/vs/workbench/contrib/terminal/browser/media/split-inverse.svg diff --git a/src/vs/workbench/contrib/terminal/electron-browser/media/split.svg b/src/vs/workbench/contrib/terminal/browser/media/split.svg similarity index 100% rename from src/vs/workbench/contrib/terminal/electron-browser/media/split.svg rename to src/vs/workbench/contrib/terminal/browser/media/split.svg diff --git a/src/vs/workbench/contrib/terminal/electron-browser/media/terminal.css b/src/vs/workbench/contrib/terminal/browser/media/terminal.css similarity index 100% rename from src/vs/workbench/contrib/terminal/electron-browser/media/terminal.css rename to src/vs/workbench/contrib/terminal/browser/media/terminal.css diff --git a/src/vs/workbench/contrib/terminal/electron-browser/media/widgets.css b/src/vs/workbench/contrib/terminal/browser/media/widgets.css similarity index 100% rename from src/vs/workbench/contrib/terminal/electron-browser/media/widgets.css rename to src/vs/workbench/contrib/terminal/browser/media/widgets.css diff --git a/src/vs/workbench/contrib/terminal/electron-browser/media/xterm.css b/src/vs/workbench/contrib/terminal/browser/media/xterm.css similarity index 100% rename from src/vs/workbench/contrib/terminal/electron-browser/media/xterm.css rename to src/vs/workbench/contrib/terminal/browser/media/xterm.css diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts new file mode 100644 index 00000000000..a420a5355b3 --- /dev/null +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -0,0 +1,519 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import * as platform from 'vs/base/common/platform'; +import 'vs/css!./media/scrollbar'; +import 'vs/css!./media/terminal'; +import 'vs/css!./media/widgets'; +import 'vs/css!./media/xterm'; +import * as nls from 'vs/nls'; +import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { Extensions as ActionBarExtensions, IActionBarRegistry, Scope } from 'vs/workbench/browser/actions'; +import * as panel from 'vs/workbench/browser/panel'; +import { getQuickNavigateHandler } from 'vs/workbench/browser/parts/quickopen/quickopen'; +import { Extensions as QuickOpenExtensions, IQuickOpenRegistry, QuickOpenHandlerDescriptor } from 'vs/workbench/browser/quickopen'; +import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; +import { AllowWorkspaceShellTerminalCommand, ClearSelectionTerminalAction, ClearTerminalAction, CopyTerminalSelectionAction, CreateNewInActiveWorkspaceTerminalAction, CreateNewTerminalAction, DeleteToLineStartTerminalAction, DeleteWordLeftTerminalAction, DeleteWordRightTerminalAction, DisallowWorkspaceShellTerminalCommand, FindNext, FindPrevious, FocusActiveTerminalAction, FocusNextPaneTerminalAction, FocusNextTerminalAction, FocusPreviousPaneTerminalAction, FocusPreviousTerminalAction, FocusTerminalFindWidgetAction, HideTerminalFindWidgetAction, KillTerminalAction, MoveToLineEndTerminalAction, MoveToLineStartTerminalAction, QuickOpenActionTermContributor, QuickOpenTermAction, RenameTerminalAction, ResizePaneDownTerminalAction, ResizePaneLeftTerminalAction, ResizePaneRightTerminalAction, ResizePaneUpTerminalAction, RunActiveFileInTerminalAction, RunSelectedTextInTerminalAction, ScrollDownPageTerminalAction, ScrollDownTerminalAction, ScrollToBottomTerminalAction, ScrollToNextCommandAction, ScrollToPreviousCommandAction, ScrollToTopTerminalAction, ScrollUpPageTerminalAction, ScrollUpTerminalAction, SelectAllTerminalAction, SelectDefaultShellWindowsTerminalAction, SelectToNextCommandAction, SelectToNextLineAction, SelectToPreviousCommandAction, SelectToPreviousLineAction, SendSequenceTerminalCommand, SplitInActiveWorkspaceTerminalAction, SplitTerminalAction, TerminalPasteAction, TERMINAL_PICKER_PREFIX, ToggleCaseSensitiveCommand, ToggleEscapeSequenceLoggingAction, ToggleRegexCommand, ToggleTerminalAction, ToggleWholeWordCommand } from 'vs/workbench/contrib/terminal/browser/terminalActions'; +import { TerminalPanel } from 'vs/workbench/contrib/terminal/browser/terminalPanel'; +import { TerminalPickerHandler } from 'vs/workbench/contrib/terminal/browser/terminalQuickOpen'; +import { KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_NOT_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, TERMINAL_PANEL_ID, DEFAULT_LETTER_SPACING, DEFAULT_LINE_HEIGHT, TerminalCursorStyle } from 'vs/workbench/contrib/terminal/common/terminal'; +import { registerColors } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; +import { setupTerminalCommands, TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminalCommands'; +import { setupTerminalMenu } from 'vs/workbench/contrib/terminal/common/terminalMenu'; +import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry'; +import { EDITOR_FONT_DEFAULTS } from 'vs/editor/common/config/editorOptions'; +import { DEFAULT_COMMANDS_TO_SKIP_SHELL } from 'vs/workbench/contrib/terminal/browser/terminalInstance'; + +const quickOpenRegistry = (Registry.as(QuickOpenExtensions.Quickopen)); + +const inTerminalsPicker = 'inTerminalPicker'; + +quickOpenRegistry.registerQuickOpenHandler( + new QuickOpenHandlerDescriptor( + TerminalPickerHandler, + TerminalPickerHandler.ID, + TERMINAL_PICKER_PREFIX, + inTerminalsPicker, + nls.localize('quickOpen.terminal', "Show All Opened Terminals") + ) +); + +const quickOpenNavigateNextInTerminalPickerId = 'workbench.action.quickOpenNavigateNextInTerminalPicker'; +CommandsRegistry.registerCommand( + { id: quickOpenNavigateNextInTerminalPickerId, handler: getQuickNavigateHandler(quickOpenNavigateNextInTerminalPickerId, true) }); + +const quickOpenNavigatePreviousInTerminalPickerId = 'workbench.action.quickOpenNavigatePreviousInTerminalPicker'; +CommandsRegistry.registerCommand( + { id: quickOpenNavigatePreviousInTerminalPickerId, handler: getQuickNavigateHandler(quickOpenNavigatePreviousInTerminalPickerId, false) }); + + +const configurationRegistry = Registry.as(Extensions.Configuration); +configurationRegistry.registerConfiguration({ + id: 'terminal', + order: 100, + title: nls.localize('terminalIntegratedConfigurationTitle', "Integrated Terminal"), + type: 'object', + properties: { + 'terminal.integrated.shellArgs.linux': { + markdownDescription: nls.localize('terminal.integrated.shellArgs.linux', "The command line arguments to use when on the Linux terminal. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration)."), + type: 'array', + items: { + type: 'string' + }, + default: [] + }, + 'terminal.integrated.shellArgs.osx': { + markdownDescription: nls.localize('terminal.integrated.shellArgs.osx', "The command line arguments to use when on the macOS terminal. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration)."), + type: 'array', + items: { + type: 'string' + }, + // Unlike on Linux, ~/.profile is not sourced when logging into a macOS session. This + // is the reason terminals on macOS typically run login shells by default which set up + // the environment. See http://unix.stackexchange.com/a/119675/115410 + default: ['-l'] + }, + 'terminal.integrated.shellArgs.windows': { + markdownDescription: nls.localize('terminal.integrated.shellArgs.windows', "The command line arguments to use when on the Windows terminal. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration)."), + 'anyOf': [ + { + type: 'array', + items: { + type: 'string', + markdownDescription: nls.localize('terminal.integrated.shellArgs.windows', "The command line arguments to use when on the Windows terminal. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration).") + }, + }, + { + type: 'string', + markdownDescription: nls.localize('terminal.integrated.shellArgs.windows.string', "The command line arguments in [command-line format](https://msdn.microsoft.com/en-au/08dfcab2-eb6e-49a4-80eb-87d4076c98c6) to use when on the Windows terminal. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration).") + } + ], + default: [] + }, + 'terminal.integrated.macOptionIsMeta': { + description: nls.localize('terminal.integrated.macOptionIsMeta', "Controls whether to treat the option key as the meta key in the terminal on macOS."), + type: 'boolean', + default: false + }, + 'terminal.integrated.macOptionClickForcesSelection': { + description: nls.localize('terminal.integrated.macOptionClickForcesSelection', "Controls whether to force selection when using Option+click on macOS. This will force a regular (line) selection and disallow the use of column selection mode. This enables copying and pasting using the regular terminal selection, for example, when mouse mode is enabled in tmux."), + type: 'boolean', + default: false + }, + 'terminal.integrated.copyOnSelection': { + description: nls.localize('terminal.integrated.copyOnSelection', "Controls whether text selected in the terminal will be copied to the clipboard."), + type: 'boolean', + default: false + }, + 'terminal.integrated.drawBoldTextInBrightColors': { + description: nls.localize('terminal.integrated.drawBoldTextInBrightColors', "Controls whether bold text in the terminal will always use the \"bright\" ANSI color variant."), + type: 'boolean', + default: true + }, + 'terminal.integrated.fontFamily': { + markdownDescription: nls.localize('terminal.integrated.fontFamily', "Controls the font family of the terminal, this defaults to `#editor.fontFamily#`'s value."), + type: 'string' + }, + // TODO: Support font ligatures + // 'terminal.integrated.fontLigatures': { + // 'description': nls.localize('terminal.integrated.fontLigatures', "Controls whether font ligatures are enabled in the terminal."), + // 'type': 'boolean', + // 'default': false + // }, + 'terminal.integrated.fontSize': { + description: nls.localize('terminal.integrated.fontSize', "Controls the font size in pixels of the terminal."), + type: 'number', + default: EDITOR_FONT_DEFAULTS.fontSize + }, + 'terminal.integrated.letterSpacing': { + description: nls.localize('terminal.integrated.letterSpacing', "Controls the letter spacing of the terminal, this is an integer value which represents the amount of additional pixels to add between characters."), + type: 'number', + default: DEFAULT_LETTER_SPACING + }, + 'terminal.integrated.lineHeight': { + description: nls.localize('terminal.integrated.lineHeight', "Controls the line height of the terminal, this number is multiplied by the terminal font size to get the actual line-height in pixels."), + type: 'number', + default: DEFAULT_LINE_HEIGHT + }, + 'terminal.integrated.fontWeight': { + type: 'string', + enum: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'], + description: nls.localize('terminal.integrated.fontWeight', "The font weight to use within the terminal for non-bold text."), + default: 'normal' + }, + 'terminal.integrated.fontWeightBold': { + type: 'string', + enum: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'], + description: nls.localize('terminal.integrated.fontWeightBold', "The font weight to use within the terminal for bold text."), + default: 'bold' + }, + 'terminal.integrated.cursorBlinking': { + description: nls.localize('terminal.integrated.cursorBlinking', "Controls whether the terminal cursor blinks."), + type: 'boolean', + default: false + }, + 'terminal.integrated.cursorStyle': { + description: nls.localize('terminal.integrated.cursorStyle', "Controls the style of terminal cursor."), + enum: [TerminalCursorStyle.BLOCK, TerminalCursorStyle.LINE, TerminalCursorStyle.UNDERLINE], + default: TerminalCursorStyle.BLOCK + }, + 'terminal.integrated.scrollback': { + description: nls.localize('terminal.integrated.scrollback', "Controls the maximum amount of lines the terminal keeps in its buffer."), + type: 'number', + default: 1000 + }, + 'terminal.integrated.setLocaleVariables': { + markdownDescription: nls.localize('terminal.integrated.setLocaleVariables', "Controls whether locale variables are set at startup of the terminal."), + type: 'boolean', + default: true + }, + 'terminal.integrated.rendererType': { + type: 'string', + enum: ['auto', 'canvas', 'dom'], + enumDescriptions: [ + nls.localize('terminal.integrated.rendererType.auto', "Let VS Code guess which renderer to use."), + nls.localize('terminal.integrated.rendererType.canvas', "Use the standard GPU/canvas-based renderer"), + nls.localize('terminal.integrated.rendererType.dom', "Use the fallback DOM-based renderer.") + ], + default: 'auto', + description: nls.localize('terminal.integrated.rendererType', "Controls how the terminal is rendered.") + }, + 'terminal.integrated.rightClickBehavior': { + type: 'string', + enum: ['default', 'copyPaste', 'selectWord'], + enumDescriptions: [ + nls.localize('terminal.integrated.rightClickBehavior.default', "Show the context menu."), + nls.localize('terminal.integrated.rightClickBehavior.copyPaste', "Copy when there is a selection, otherwise paste."), + nls.localize('terminal.integrated.rightClickBehavior.selectWord', "Select the word under the cursor and show the context menu.") + ], + default: platform.isMacintosh ? 'selectWord' : platform.isWindows ? 'copyPaste' : 'default', + description: nls.localize('terminal.integrated.rightClickBehavior', "Controls how terminal reacts to right click.") + }, + 'terminal.integrated.cwd': { + description: nls.localize('terminal.integrated.cwd', "An explicit start path where the terminal will be launched, this is used as the current working directory (cwd) for the shell process. This may be particularly useful in workspace settings if the root directory is not a convenient cwd."), + type: 'string', + default: undefined + }, + 'terminal.integrated.confirmOnExit': { + description: nls.localize('terminal.integrated.confirmOnExit', "Controls whether to confirm on exit if there are active terminal sessions."), + type: 'boolean', + default: false + }, + 'terminal.integrated.enableBell': { + description: nls.localize('terminal.integrated.enableBell', "Controls whether the terminal bell is enabled."), + type: 'boolean', + default: false + }, + 'terminal.integrated.commandsToSkipShell': { + description: nls.localize('terminal.integrated.commandsToSkipShell', "A set of command IDs whose keybindings will not be sent to the shell and instead always be handled by Code. This allows the use of keybindings that would normally be consumed by the shell to act the same as when the terminal is not focused, for example ctrl+p to launch Quick Open.\nDefault Skipped Commands:\n\n{0}", DEFAULT_COMMANDS_TO_SKIP_SHELL.sort().map(command => `- ${command}`).join('\n')), + type: 'array', + items: { + type: 'string' + }, + default: [] + }, + 'terminal.integrated.env.osx': { + markdownDescription: nls.localize('terminal.integrated.env.osx', "Object with environment variables that will be added to the VS Code process to be used by the terminal on macOS. Set to `null` to delete the environment variable."), + type: 'object', + additionalProperties: { + type: ['string', 'null'] + }, + default: {} + }, + 'terminal.integrated.env.linux': { + markdownDescription: nls.localize('terminal.integrated.env.linux', "Object with environment variables that will be added to the VS Code process to be used by the terminal on Linux. Set to `null` to delete the environment variable."), + type: 'object', + additionalProperties: { + type: ['string', 'null'] + }, + default: {} + }, + 'terminal.integrated.env.windows': { + markdownDescription: nls.localize('terminal.integrated.env.windows', "Object with environment variables that will be added to the VS Code process to be used by the terminal on Windows. Set to `null` to delete the environment variable."), + type: 'object', + additionalProperties: { + type: ['string', 'null'] + }, + default: {} + }, + 'terminal.integrated.showExitAlert': { + description: nls.localize('terminal.integrated.showExitAlert', "Controls whether to show the alert \"The terminal process terminated with exit code\" when exit code is non-zero."), + type: 'boolean', + default: true + }, + 'terminal.integrated.splitCwd': { + description: nls.localize('terminal.integrated.splitCwd', "Controls the working directory a split terminal starts with."), + type: 'string', + enum: ['workspaceRoot', 'initial', 'inherited'], + enumDescriptions: [ + nls.localize('terminal.integrated.splitCwd.workspaceRoot', "A new split terminal will use the workspace root as the working directory. In a multi-root workspace a choice for which root folder to use is offered."), + nls.localize('terminal.integrated.splitCwd.initial', "A new split terminal will use the working directory that the parent terminal started with."), + nls.localize('terminal.integrated.splitCwd.inherited', "On macOS and Linux, a new split terminal will use the working directory of the parent terminal. On Windows, this behaves the same as initial."), + ], + default: 'inherited' + }, + 'terminal.integrated.windowsEnableConpty': { + description: nls.localize('terminal.integrated.windowsEnableConpty', "Whether to use ConPTY for Windows terminal process communication (requires Windows 10 build number 18309+). Winpty will be used if this is false."), + type: 'boolean', + default: false + } + } +}); + +const registry = Registry.as(ActionExtensions.WorkbenchActions); +registry.registerWorkbenchAction(new SyncActionDescriptor(QuickOpenTermAction, QuickOpenTermAction.ID, QuickOpenTermAction.LABEL), 'Terminal: Switch Active Terminal', nls.localize('terminal', "Terminal")); +const actionBarRegistry = Registry.as(ActionBarExtensions.Actionbar); +actionBarRegistry.registerActionBarContributor(Scope.VIEWER, QuickOpenActionTermContributor); + +(Registry.as(panel.Extensions.Panels)).registerPanel(new panel.PanelDescriptor( + TerminalPanel, + TERMINAL_PANEL_ID, + nls.localize('terminal', "Terminal"), + 'terminal', + 40, + TERMINAL_COMMAND_ID.TOGGLE +)); + +// On mac cmd+` is reserved to cycle between windows, that's why the keybindings use WinCtrl +const category = nls.localize('terminalCategory', "Terminal"); +const actionRegistry = Registry.as(ActionExtensions.WorkbenchActions); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(KillTerminalAction, KillTerminalAction.ID, KillTerminalAction.LABEL), 'Terminal: Kill the Active Terminal Instance', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(CopyTerminalSelectionAction, CopyTerminalSelectionAction.ID, CopyTerminalSelectionAction.LABEL, { + primary: KeyMod.CtrlCmd | KeyCode.KEY_C, + linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_C } +}, ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, KEYBINDING_CONTEXT_TERMINAL_FOCUS)), 'Terminal: Copy Selection', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(CreateNewTerminalAction, CreateNewTerminalAction.ID, CreateNewTerminalAction.LABEL, { + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_BACKTICK, + mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.US_BACKTICK } +}), 'Terminal: Create New Integrated Terminal', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ClearSelectionTerminalAction, ClearSelectionTerminalAction.ID, ClearSelectionTerminalAction.LABEL, { + primary: KeyCode.Escape, + linux: { primary: KeyCode.Escape } +}, ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_NOT_VISIBLE)), 'Terminal: Escape selection', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(CreateNewInActiveWorkspaceTerminalAction, CreateNewInActiveWorkspaceTerminalAction.ID, CreateNewInActiveWorkspaceTerminalAction.LABEL), 'Terminal: Create New Integrated Terminal (In Active Workspace)', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusActiveTerminalAction, FocusActiveTerminalAction.ID, FocusActiveTerminalAction.LABEL), 'Terminal: Focus Terminal', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusNextTerminalAction, FocusNextTerminalAction.ID, FocusNextTerminalAction.LABEL), 'Terminal: Focus Next Terminal', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusPreviousTerminalAction, FocusPreviousTerminalAction.ID, FocusPreviousTerminalAction.LABEL), 'Terminal: Focus Previous Terminal', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(TerminalPasteAction, TerminalPasteAction.ID, TerminalPasteAction.LABEL, { + primary: KeyMod.CtrlCmd | KeyCode.KEY_V, + linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_V }, + // Don't apply to Mac since cmd+v works + mac: { primary: 0 } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Paste into Active Terminal', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SelectAllTerminalAction, SelectAllTerminalAction.ID, SelectAllTerminalAction.LABEL, { + // Don't use ctrl+a by default as that would override the common go to start + // of prompt shell binding + primary: 0, + // Technically this doesn't need to be here as it will fall back to this + // behavior anyway when handed to xterm.js, having this handled by VS Code + // makes it easier for users to see how it works though. + mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_A } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Select All', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(RunSelectedTextInTerminalAction, RunSelectedTextInTerminalAction.ID, RunSelectedTextInTerminalAction.LABEL), 'Terminal: Run Selected Text In Active Terminal', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(RunActiveFileInTerminalAction, RunActiveFileInTerminalAction.ID, RunActiveFileInTerminalAction.LABEL), 'Terminal: Run Active File In Active Terminal', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleTerminalAction, ToggleTerminalAction.ID, ToggleTerminalAction.LABEL, { + primary: KeyMod.CtrlCmd | KeyCode.US_BACKTICK, + mac: { primary: KeyMod.WinCtrl | KeyCode.US_BACKTICK } +}), 'View: Toggle Integrated Terminal', nls.localize('viewCategory', "View")); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ScrollDownTerminalAction, ScrollDownTerminalAction.ID, ScrollDownTerminalAction.LABEL, { + primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.PageDown, + linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.DownArrow } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Scroll Down (Line)', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ScrollDownPageTerminalAction, ScrollDownPageTerminalAction.ID, ScrollDownPageTerminalAction.LABEL, { + primary: KeyMod.Shift | KeyCode.PageDown, + mac: { primary: KeyCode.PageDown } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Scroll Down (Page)', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ScrollToBottomTerminalAction, ScrollToBottomTerminalAction.ID, ScrollToBottomTerminalAction.LABEL, { + primary: KeyMod.CtrlCmd | KeyCode.End, + linux: { primary: KeyMod.Shift | KeyCode.End } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Scroll to Bottom', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ScrollUpTerminalAction, ScrollUpTerminalAction.ID, ScrollUpTerminalAction.LABEL, { + primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.PageUp, + linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.UpArrow }, +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Scroll Up (Line)', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ScrollUpPageTerminalAction, ScrollUpPageTerminalAction.ID, ScrollUpPageTerminalAction.LABEL, { + primary: KeyMod.Shift | KeyCode.PageUp, + mac: { primary: KeyCode.PageUp } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Scroll Up (Page)', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ScrollToTopTerminalAction, ScrollToTopTerminalAction.ID, ScrollToTopTerminalAction.LABEL, { + primary: KeyMod.CtrlCmd | KeyCode.Home, + linux: { primary: KeyMod.Shift | KeyCode.Home } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Scroll to Top', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ClearTerminalAction, ClearTerminalAction.ID, ClearTerminalAction.LABEL, { + primary: 0, + mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_K } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KeybindingWeight.WorkbenchContrib + 1), 'Terminal: Clear', category); +if (platform.isWindows) { + actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SelectDefaultShellWindowsTerminalAction, SelectDefaultShellWindowsTerminalAction.ID, SelectDefaultShellWindowsTerminalAction.LABEL), 'Terminal: Select Default Shell', category); +} +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(AllowWorkspaceShellTerminalCommand, AllowWorkspaceShellTerminalCommand.ID, AllowWorkspaceShellTerminalCommand.LABEL), 'Terminal: Allow Workspace Shell Configuration', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(DisallowWorkspaceShellTerminalCommand, DisallowWorkspaceShellTerminalCommand.ID, DisallowWorkspaceShellTerminalCommand.LABEL), 'Terminal: Disallow Workspace Shell Configuration', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(RenameTerminalAction, RenameTerminalAction.ID, RenameTerminalAction.LABEL), 'Terminal: Rename', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusTerminalFindWidgetAction, FocusTerminalFindWidgetAction.ID, FocusTerminalFindWidgetAction.LABEL, { + primary: KeyMod.CtrlCmd | KeyCode.KEY_F +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Focus Find Widget', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusTerminalFindWidgetAction, FocusTerminalFindWidgetAction.ID, FocusTerminalFindWidgetAction.LABEL, { + primary: KeyMod.CtrlCmd | KeyCode.KEY_F +}, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Focus Find Widget', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(HideTerminalFindWidgetAction, HideTerminalFindWidgetAction.ID, HideTerminalFindWidgetAction.LABEL, { + primary: KeyCode.Escape, + secondary: [KeyMod.Shift | KeyCode.Escape] +}, ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE)), 'Terminal: Hide Find Widget', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(DeleteWordLeftTerminalAction, DeleteWordLeftTerminalAction.ID, DeleteWordLeftTerminalAction.LABEL, { + primary: KeyMod.CtrlCmd | KeyCode.Backspace, + mac: { primary: KeyMod.Alt | KeyCode.Backspace } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Delete Word Left', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(DeleteWordRightTerminalAction, DeleteWordRightTerminalAction.ID, DeleteWordRightTerminalAction.LABEL, { + primary: KeyMod.CtrlCmd | KeyCode.Delete, + mac: { primary: KeyMod.Alt | KeyCode.Delete } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Delete Word Right', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(DeleteToLineStartTerminalAction, DeleteToLineStartTerminalAction.ID, DeleteToLineStartTerminalAction.LABEL, { + primary: 0, + mac: { primary: KeyMod.CtrlCmd | KeyCode.Backspace } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Delete To Line Start', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(MoveToLineStartTerminalAction, MoveToLineStartTerminalAction.ID, MoveToLineStartTerminalAction.LABEL, { + primary: 0, + mac: { primary: KeyMod.CtrlCmd | KeyCode.LeftArrow } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Move To Line Start', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(MoveToLineEndTerminalAction, MoveToLineEndTerminalAction.ID, MoveToLineEndTerminalAction.LABEL, { + primary: 0, + mac: { primary: KeyMod.CtrlCmd | KeyCode.RightArrow } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Move To Line End', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SplitTerminalAction, SplitTerminalAction.ID, SplitTerminalAction.LABEL, { + primary: KeyMod.CtrlCmd | KeyCode.US_BACKSLASH, + secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_5], + mac: { + primary: KeyMod.CtrlCmd | KeyCode.US_BACKSLASH, + secondary: [KeyMod.WinCtrl | KeyMod.Shift | KeyCode.KEY_5] + } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Split', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SplitInActiveWorkspaceTerminalAction, SplitInActiveWorkspaceTerminalAction.ID, SplitInActiveWorkspaceTerminalAction.LABEL), 'Terminal: Split Terminal (In Active Workspace)', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusPreviousPaneTerminalAction, FocusPreviousPaneTerminalAction.ID, FocusPreviousPaneTerminalAction.LABEL, { + primary: KeyMod.Alt | KeyCode.LeftArrow, + secondary: [KeyMod.Alt | KeyCode.UpArrow], + mac: { + primary: KeyMod.Alt | KeyMod.CtrlCmd | KeyCode.LeftArrow, + secondary: [KeyMod.Alt | KeyMod.CtrlCmd | KeyCode.UpArrow] + } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Focus Previous Pane', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusNextPaneTerminalAction, FocusNextPaneTerminalAction.ID, FocusNextPaneTerminalAction.LABEL, { + primary: KeyMod.Alt | KeyCode.RightArrow, + secondary: [KeyMod.Alt | KeyCode.DownArrow], + mac: { + primary: KeyMod.Alt | KeyMod.CtrlCmd | KeyCode.RightArrow, + secondary: [KeyMod.Alt | KeyMod.CtrlCmd | KeyCode.DownArrow] + } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Focus Next Pane', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ResizePaneLeftTerminalAction, ResizePaneLeftTerminalAction.ID, ResizePaneLeftTerminalAction.LABEL, { + primary: 0, + linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.LeftArrow }, + mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.LeftArrow } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Resize Pane Left', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ResizePaneRightTerminalAction, ResizePaneRightTerminalAction.ID, ResizePaneRightTerminalAction.LABEL, { + primary: 0, + linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.RightArrow }, + mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.RightArrow } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Resize Pane Right', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ResizePaneUpTerminalAction, ResizePaneUpTerminalAction.ID, ResizePaneUpTerminalAction.LABEL, { + primary: 0, + linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.UpArrow }, + mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.UpArrow } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Resize Pane Up', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ResizePaneDownTerminalAction, ResizePaneDownTerminalAction.ID, ResizePaneDownTerminalAction.LABEL, { + primary: 0, + linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.DownArrow }, + mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.DownArrow } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Resize Pane Down', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ScrollToPreviousCommandAction, ScrollToPreviousCommandAction.ID, ScrollToPreviousCommandAction.LABEL, { + primary: 0, + mac: { primary: KeyMod.CtrlCmd | KeyCode.UpArrow } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Scroll To Previous Command', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ScrollToNextCommandAction, ScrollToNextCommandAction.ID, ScrollToNextCommandAction.LABEL, { + primary: 0, + mac: { primary: KeyMod.CtrlCmd | KeyCode.DownArrow } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Scroll To Next Command', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SelectToPreviousCommandAction, SelectToPreviousCommandAction.ID, SelectToPreviousCommandAction.LABEL, { + primary: 0, + mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.UpArrow } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Select To Previous Command', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SelectToNextCommandAction, SelectToNextCommandAction.ID, SelectToNextCommandAction.LABEL, { + primary: 0, + mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.DownArrow } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Select To Next Command', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SelectToPreviousLineAction, SelectToPreviousLineAction.ID, SelectToPreviousLineAction.LABEL), 'Terminal: Select To Previous Line', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SelectToNextLineAction, SelectToNextLineAction.ID, SelectToNextLineAction.LABEL), 'Terminal: Select To Next Line', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleEscapeSequenceLoggingAction, ToggleEscapeSequenceLoggingAction.ID, ToggleEscapeSequenceLoggingAction.LABEL), 'Terminal: Toggle Escape Sequence Logging', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleRegexCommand, ToggleRegexCommand.ID, ToggleRegexCommand.LABEL, { + primary: KeyMod.Alt | KeyCode.KEY_R, + mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_R } +}, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Toggle find by regex'); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleRegexCommand, ToggleRegexCommand.ID_TERMINAL_FOCUS, ToggleRegexCommand.LABEL, { + primary: KeyMod.Alt | KeyCode.KEY_R, + mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_R } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Toggle find by regex', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleWholeWordCommand, ToggleWholeWordCommand.ID, ToggleWholeWordCommand.LABEL, { + primary: KeyMod.Alt | KeyCode.KEY_W, + mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_W } +}, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Toggle find whole word'); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleWholeWordCommand, ToggleWholeWordCommand.ID_TERMINAL_FOCUS, ToggleWholeWordCommand.LABEL, { + primary: KeyMod.Alt | KeyCode.KEY_W, + mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_W } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Toggle find whole word', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleCaseSensitiveCommand, ToggleCaseSensitiveCommand.ID, ToggleCaseSensitiveCommand.LABEL, { + primary: KeyMod.Alt | KeyCode.KEY_C, + mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_C } +}, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Toggle find match case'); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleCaseSensitiveCommand, ToggleCaseSensitiveCommand.ID_TERMINAL_FOCUS, ToggleCaseSensitiveCommand.LABEL, { + primary: KeyMod.Alt | KeyCode.KEY_C, + mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_C } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Toggle find match case', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindNext, FindNext.ID_TERMINAL_FOCUS, FindNext.LABEL, { + primary: KeyCode.F3, + mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_G, secondary: [KeyCode.F3] } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Find next', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindNext, FindNext.ID, FindNext.LABEL, { + primary: KeyCode.F3, + mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_G, secondary: [KeyCode.F3] } +}, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Find next'); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindPrevious, FindPrevious.ID_TERMINAL_FOCUS, FindPrevious.LABEL, { + primary: KeyMod.Shift | KeyCode.F3, + mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_G, secondary: [KeyMod.Shift | KeyCode.F3] }, +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Find previous', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindPrevious, FindPrevious.ID, FindPrevious.LABEL, { + primary: KeyMod.Shift | KeyCode.F3, + mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_G, secondary: [KeyMod.Shift | KeyCode.F3] }, +}, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Find previous'); + + +const sendSequenceTerminalCommand = new SendSequenceTerminalCommand({ + id: SendSequenceTerminalCommand.ID, + precondition: null, + description: { + description: `Send Custom Sequence To Terminal`, + args: [{ + name: 'args', + schema: { + 'type': 'object', + 'required': ['text'], + 'properties': { + 'text': { + 'type': 'string' + } + }, + } + }] + } +}); +sendSequenceTerminalCommand.register(); + +setupTerminalCommands(); +setupTerminalMenu(); + +registerColors(); diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts new file mode 100644 index 00000000000..ff7f6b95f5b --- /dev/null +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Terminal as XTermTerminal } from 'vscode-xterm'; +import { ITerminalInstance, IWindowsShellHelper, ITerminalProcessManager, ITerminalConfigHelper, ITerminalChildProcess, IShellLaunchConfig } from 'vs/workbench/contrib/terminal/common/terminal'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IProcessEnvironment } from 'vs/base/common/platform'; + +export const ITerminalInstanceService = createDecorator('terminalInstanceService'); + +export interface ITerminalInstanceService { + _serviceBrand: any; + + getXtermConstructor(): Promise; + createWindowsShellHelper(shellProcessId: number, instance: ITerminalInstance, xterm: XTermTerminal): IWindowsShellHelper; + createTerminalProcessManager(id: number, configHelper: ITerminalConfigHelper): ITerminalProcessManager; + createTerminalProcess(shellLaunchConfig: IShellLaunchConfig, cwd: string, cols: number, rows: number, env: IProcessEnvironment, windowsEnableConpty: boolean): ITerminalChildProcess; +} + +export interface IBrowserTerminalConfigHelper extends ITerminalConfigHelper { + panelContainer: HTMLElement; +} diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts similarity index 99% rename from src/vs/workbench/contrib/terminal/electron-browser/terminalActions.ts rename to src/vs/workbench/contrib/terminal/browser/terminalActions.ts index a202076042b..663829a2683 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import * as os from 'os'; import { Action, IAction } from 'vs/base/common/actions'; import { EndOfLinePreference } from 'vs/editor/common/model'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; @@ -34,6 +33,7 @@ import { IConfigurationResolverService } from 'vs/workbench/services/configurati import { IHistoryService } from 'vs/workbench/services/history/common/history'; import { Schemas } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; +import { isWindows } from 'vs/base/common/platform'; export const TERMINAL_PICKER_PREFIX = 'term '; @@ -659,7 +659,7 @@ export class RunSelectedTextInTerminalAction extends Action { if (selection.isEmpty()) { text = editor.getModel().getLineContent(selection.selectionStartLineNumber).trim(); } else { - const endOfLinePreference = os.EOL === '\n' ? EndOfLinePreference.LF : EndOfLinePreference.CRLF; + const endOfLinePreference = isWindows ? EndOfLinePreference.LF : EndOfLinePreference.CRLF; text = editor.getModel().getValueInRange(selection, endOfLinePreference); } instance.sendText(text, true); @@ -696,7 +696,7 @@ export class RunActiveFileInTerminalAction extends Action { return Promise.resolve(undefined); } - return instance.preparePathForTerminalAsync(uri.fsPath).then(path => { + return this.terminalService.preparePathForTerminalAsync(uri.fsPath, instance.shellLaunchConfig.executable, instance.title).then(path => { instance.sendText(path, true); return this.terminalService.showPanel(); }); diff --git a/src/vs/workbench/contrib/terminal/node/terminalCommandTracker.ts b/src/vs/workbench/contrib/terminal/browser/terminalCommandTracker.ts similarity index 100% rename from src/vs/workbench/contrib/terminal/node/terminalCommandTracker.ts rename to src/vs/workbench/contrib/terminal/browser/terminalCommandTracker.ts diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalConfigHelper.ts b/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts similarity index 94% rename from src/vs/workbench/contrib/terminal/electron-browser/terminalConfigHelper.ts rename to src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts index fd82c1a61fb..506ceef0bf0 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalConfigHelper.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts @@ -8,13 +8,12 @@ import * as path from 'vs/base/common/path'; import * as platform from 'vs/base/common/platform'; import { EDITOR_FONT_DEFAULTS, IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { ITerminalConfiguration, ITerminalConfigHelper, ITerminalFont, IShellLaunchConfig, IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY, TERMINAL_CONFIG_SECTION, DEFAULT_LETTER_SPACING, DEFAULT_LINE_HEIGHT, MINIMUM_LETTER_SPACING } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalConfiguration, ITerminalFont, IShellLaunchConfig, IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY, TERMINAL_CONFIG_SECTION, DEFAULT_LETTER_SPACING, DEFAULT_LINE_HEIGHT, MINIMUM_LETTER_SPACING, LinuxDistro } from 'vs/workbench/contrib/terminal/common/terminal'; import Severity from 'vs/base/common/severity'; -import { isFedora, isUbuntu } from 'vs/workbench/contrib/terminal/node/terminal'; import { Terminal as XTermTerminal } from 'vscode-xterm'; import { INotificationService } from 'vs/platform/notification/common/notification'; +import { IBrowserTerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminal'; const MINIMUM_FONT_SIZE = 6; const MAXIMUM_FONT_SIZE = 25; @@ -23,7 +22,7 @@ const MAXIMUM_FONT_SIZE = 25; * Encapsulates terminal configuration logic, the primary purpose of this file is so that platform * specific test cases can be written. */ -export class TerminalConfigHelper implements ITerminalConfigHelper { +export class TerminalConfigHelper implements IBrowserTerminalConfigHelper { public panelContainer: HTMLElement; private _charMeasureElement: HTMLElement; @@ -31,8 +30,9 @@ export class TerminalConfigHelper implements ITerminalConfigHelper { public config: ITerminalConfiguration; public constructor( + private readonly _linuxDistro: LinuxDistro, @IConfigurationService private readonly _configurationService: IConfigurationService, - @IWorkspaceConfigurationService private readonly _workspaceConfigurationService: IWorkspaceConfigurationService, + @IConfigurationService private readonly _workspaceConfigurationService: IConfigurationService, @INotificationService private readonly _notificationService: INotificationService, @IStorageService private readonly _storageService: IStorageService ) { @@ -118,10 +118,10 @@ export class TerminalConfigHelper implements ITerminalConfigHelper { // Work around bad font on Fedora/Ubuntu if (!this.config.fontFamily) { - if (isFedora) { + if (this._linuxDistro === LinuxDistro.Fedora) { fontFamily = '\'DejaVu Sans Mono\', monospace'; } - if (isUbuntu) { + if (this._linuxDistro === LinuxDistro.Ubuntu) { fontFamily = '\'Ubuntu Mono\', monospace'; // Ubuntu mono is somehow smaller, so set fontSize a bit larger to get the same perceived size. diff --git a/src/vs/workbench/contrib/terminal/browser/terminalFindWidget.css b/src/vs/workbench/contrib/terminal/browser/terminalFindWidget.css deleted file mode 100644 index a4a092d8349..00000000000 --- a/src/vs/workbench/contrib/terminal/browser/terminalFindWidget.css +++ /dev/null @@ -1,4 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ diff --git a/src/vs/workbench/contrib/terminal/browser/terminalFindWidget.ts b/src/vs/workbench/contrib/terminal/browser/terminalFindWidget.ts index e5a54d0ba74..db30d122c39 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalFindWidget.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalFindWidget.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!./terminalFindWidget'; import { SimpleFindWidget } from 'vs/editor/contrib/find/simpleFindWidget'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { ITerminalService, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_INPUT_FOCUSED, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED } from 'vs/workbench/contrib/terminal/common/terminal'; diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts similarity index 93% rename from src/vs/workbench/contrib/terminal/electron-browser/terminalInstance.ts rename to src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index eff13a0af18..08845402186 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -3,8 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { execFile } from 'child_process'; -import * as os from 'os'; import * as path from 'vs/base/common/path'; import * as dom from 'vs/base/browser/dom'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; @@ -27,17 +25,16 @@ import { activeContrastBorder, scrollbarSliderActiveBackground, scrollbarSliderB import { ICssStyleCollector, ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { PANEL_BACKGROUND } from 'vs/workbench/common/theme'; import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/terminalWidgetManager'; -import { IShellLaunchConfig, ITerminalDimensions, ITerminalInstance, ITerminalProcessManager, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, NEVER_MEASURE_RENDER_TIME_STORAGE_KEY, ProcessState, TERMINAL_PANEL_ID } from 'vs/workbench/contrib/terminal/common/terminal'; +import { IShellLaunchConfig, ITerminalDimensions, ITerminalInstance, ITerminalProcessManager, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, NEVER_MEASURE_RENDER_TIME_STORAGE_KEY, ProcessState, TERMINAL_PANEL_ID, IWindowsShellHelper } from 'vs/workbench/contrib/terminal/common/terminal'; import { ansiColorIdentifiers, TERMINAL_BACKGROUND_COLOR, TERMINAL_CURSOR_BACKGROUND_COLOR, TERMINAL_CURSOR_FOREGROUND_COLOR, TERMINAL_FOREGROUND_COLOR, TERMINAL_SELECTION_BACKGROUND_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; import { TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminalCommands'; -import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/electron-browser/terminalConfigHelper'; -import { TerminalLinkHandler } from 'vs/workbench/contrib/terminal/electron-browser/terminalLinkHandler'; -import { TerminalProcessManager } from 'vs/workbench/contrib/terminal/electron-browser/terminalProcessManager'; -import { TerminalCommandTracker } from 'vs/workbench/contrib/terminal/node/terminalCommandTracker'; -import { WindowsShellHelper } from 'vs/workbench/contrib/terminal/node/windowsShellHelper'; +import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; +import { TerminalLinkHandler } from 'vs/workbench/contrib/terminal/browser/terminalLinkHandler'; +import { TerminalCommandTracker } from 'vs/workbench/contrib/terminal/browser/terminalCommandTracker'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { ISearchOptions, Terminal as XTermTerminal } from 'vscode-xterm'; import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; +import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; // How long in milliseconds should an average frame take to render for a notification to appear // which suggests the fallback DOM-based renderer @@ -151,8 +148,6 @@ export const DEFAULT_COMMANDS_TO_SKIP_SHELL: string[] = [ 'workbench.action.toggleMaximizedPanel' ]; -let Terminal: typeof XTermTerminal; - export class TerminalInstance implements ITerminalInstance { private static readonly EOL_REGEX = /\r?\n/g; @@ -175,13 +170,13 @@ export class TerminalInstance implements ITerminalInstance { private _cols: number; private _rows: number; private _dimensionsOverride: ITerminalDimensions; - private _windowsShellHelper: WindowsShellHelper; + private _windowsShellHelper: IWindowsShellHelper | undefined; private _xtermReadyPromise: Promise; private _titleReadyPromise: Promise; private _titleReadyComplete: (title: string) => any; private _disposables: lifecycle.IDisposable[]; - private _messageTitleDisposable: lifecycle.IDisposable; + private _messageTitleDisposable: lifecycle.IDisposable | undefined; private _widgetManager: TerminalWidgetManager; private _linkHandler: TerminalLinkHandler; @@ -240,6 +235,7 @@ export class TerminalInstance implements ITerminalInstance { private readonly _configHelper: TerminalConfigHelper, private _container: HTMLElement, private _shellLaunchConfig: IShellLaunchConfig, + @ITerminalInstanceService private readonly _terminalInstanceService: ITerminalInstanceService, @IContextKeyService private readonly _contextKeyService: IContextKeyService, @IKeybindingService private readonly _keybindingService: IKeybindingService, @INotificationService private readonly _notificationService: INotificationService, @@ -398,17 +394,7 @@ export class TerminalInstance implements ITerminalInstance { * Create xterm.js instance and attach data listeners. */ protected async _createXterm(): Promise { - if (!Terminal) { - Terminal = (await import('vscode-xterm')).Terminal; - // Enable xterm.js addons - Terminal.applyAddon(require.__$__nodeRequire('vscode-xterm/lib/addons/search/search')); - Terminal.applyAddon(require.__$__nodeRequire('vscode-xterm/lib/addons/webLinks/webLinks')); - Terminal.applyAddon(require.__$__nodeRequire('vscode-xterm/lib/addons/winptyCompat/winptyCompat')); - // Localize strings - Terminal.strings.blankLine = nls.localize('terminal.integrated.a11yBlankLine', 'Blank line'); - Terminal.strings.promptLabel = nls.localize('terminal.integrated.a11yPromptLabel', 'Terminal input'); - Terminal.strings.tooMuchOutput = nls.localize('terminal.integrated.a11yTooMuchOutput', 'Too much output to announce, navigate to rows manually to read'); - } + const Terminal = await this._terminalInstanceService.getXtermConstructor(); const font = this._configHelper.getFont(undefined, true); const config = this._configHelper.config; this._xterm = new Terminal({ @@ -717,7 +703,8 @@ export class TerminalInstance implements ITerminalInstance { public dispose(immediate?: boolean): void { this._logService.trace(`terminalInstance#dispose (id: ${this.id})`); - this._windowsShellHelper = lifecycle.dispose(this._windowsShellHelper); + lifecycle.dispose(this._windowsShellHelper); + this._windowsShellHelper = undefined; this._linkHandler = lifecycle.dispose(this._linkHandler); this._commandTracker = lifecycle.dispose(this._commandTracker); this._widgetManager = lifecycle.dispose(this._widgetManager); @@ -804,68 +791,6 @@ export class TerminalInstance implements ITerminalInstance { } } - public preparePathForTerminalAsync(originalPath: string): Promise { - return new Promise(c => { - const exe = this.shellLaunchConfig.executable; - if (!exe) { - c(originalPath); - return; - } - - const hasSpace = originalPath.indexOf(' ') !== -1; - - const pathBasename = path.basename(exe, '.exe'); - const isPowerShell = pathBasename === 'pwsh' || - this.title === 'pwsh' || - pathBasename === 'powershell' || - this.title === 'powershell'; - - if (isPowerShell && (hasSpace || originalPath.indexOf('\'') !== -1)) { - c(`& '${originalPath.replace(/'/g, '\'\'')}'`); - return; - } - - if (platform.isWindows) { - // 17063 is the build number where wsl path was introduced. - // Update Windows uriPath to be executed in WSL. - if (((exe.indexOf('wsl') !== -1) || ((exe.indexOf('bash.exe') !== -1) && (exe.indexOf('git') === -1))) && (TerminalInstance.getWindowsBuildNumber() >= 17063)) { - execFile('bash.exe', ['-c', 'echo $(wslpath ' + this._escapeNonWindowsPath(originalPath) + ')'], {}, (error, stdout, stderr) => { - c(this._escapeNonWindowsPath(stdout.trim())); - }); - return; - } else if (hasSpace) { - c('"' + originalPath + '"'); - } else { - c(originalPath); - } - return; - } - c(this._escapeNonWindowsPath(originalPath)); - }); - } - - private _escapeNonWindowsPath(path: string): string { - let newPath = path; - if (newPath.indexOf('\\') !== 0) { - newPath = newPath.replace(/\\/g, '\\\\'); - } - if (!newPath && (newPath.indexOf('"') !== -1)) { - newPath = '\'' + newPath + '\''; - } else if (newPath.indexOf(' ') !== -1) { - newPath = newPath.replace(/ /g, '\\ '); - } - return newPath; - } - - public static getWindowsBuildNumber(): number { - const osVersion = (/(\d+)\.(\d+)\.(\d+)/g).exec(os.release()); - let buildNumber: number = 0; - if (osVersion && osVersion.length === 4) { - buildNumber = parseInt(osVersion[3]); - } - return buildNumber; - } - public setVisible(visible: boolean): void { this._isVisible = visible; if (this._wrapperElement) { @@ -930,7 +855,7 @@ export class TerminalInstance implements ITerminalInstance { } protected _createProcess(): void { - this._processManager = this._instantiationService.createInstance(TerminalProcessManager, this._id, this._configHelper); + this._processManager = this._terminalInstanceService.createTerminalProcessManager(this._id, this._configHelper); this._processManager.onProcessReady(() => this._onProcessIdReady.fire(this)); this._processManager.onProcessExit(exitCode => this._onProcessExit(exitCode)); this._processManager.onProcessData(data => this._onData.fire(data)); @@ -947,7 +872,7 @@ export class TerminalInstance implements ITerminalInstance { this._processManager.ptyProcessReady.then(() => { this._xtermReadyPromise.then(() => { if (!this._isDisposed) { - this._windowsShellHelper = new WindowsShellHelper(this._processManager!.shellProcessId, this, this._xterm); + this._terminalInstanceService.createWindowsShellHelper(this._processManager!.shellProcessId, this, this._xterm); } }); }); @@ -1255,6 +1180,9 @@ export class TerminalInstance implements ITerminalInstance { // automatically updates the terminal name if (this._messageTitleDisposable) { lifecycle.dispose(this._messageTitleDisposable); + lifecycle.dispose(this._windowsShellHelper); + this._messageTitleDisposable = undefined; + this._windowsShellHelper = undefined; } } const didTitleChange = title !== this._title; diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalLinkHandler.ts b/src/vs/workbench/contrib/terminal/browser/terminalLinkHandler.ts similarity index 97% rename from src/vs/workbench/contrib/terminal/electron-browser/terminalLinkHandler.ts rename to src/vs/workbench/contrib/terminal/browser/terminalLinkHandler.ts index 734d13fd45a..0c7246a7038 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalLinkHandler.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalLinkHandler.ts @@ -6,8 +6,7 @@ import * as nls from 'vs/nls'; import * as path from 'vs/base/common/path'; import * as platform from 'vs/base/common/platform'; -import * as pfs from 'vs/base/node/pfs'; -import { URI as Uri } from 'vs/base/common/uri'; +import { URI } from 'vs/base/common/uri'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/terminalWidgetManager'; @@ -15,6 +14,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { ITerminalService } from 'vs/workbench/contrib/terminal/common/terminal'; import { ITextEditorSelection } from 'vs/platform/editor/common/editor'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IFileService } from 'vs/platform/files/common/files'; import { ILinkMatcherOptions } from 'vscode-xterm'; const pathPrefix = '(\\.\\.?|\\~)'; @@ -74,6 +74,7 @@ export class TerminalLinkHandler { @IEditorService private readonly _editorService: IEditorService, @IConfigurationService private readonly _configurationService: IConfigurationService, @ITerminalService private readonly _terminalService: ITerminalService, + @IFileService private readonly _fileService: IFileService ) { const baseLocalLinkClause = _platform === platform.Platform.Windows ? winLocalLinkClause : unixLocalLinkClause; // Append line and column number regex @@ -203,7 +204,7 @@ export class TerminalLinkHandler { if (!normalizedUrl) { return Promise.resolve(null); } - const resource = Uri.file(normalizedUrl); + const resource = URI.file(normalizedUrl); const lineColumnInfo: LineColumnInfo = this.extractLineColumnInfo(link); const selection: ITextEditorSelection = { startLineNumber: lineColumnInfo.lineNumber, @@ -223,7 +224,7 @@ export class TerminalLinkHandler { } private _handleHypertextLink(url: string): void { - const uri = Uri.parse(url); + const uri = URI.parse(url); this._openerService.open(uri); } @@ -288,7 +289,7 @@ export class TerminalLinkHandler { } // Ensure the file exists on disk, so an editor can be opened after clicking it - return pfs.fileExists(linkUrl).then(isFile => { + return this._fileService.existsFile(URI.file(linkUrl)).then(isFile => { if (!isFile) { return null; } diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalPanel.ts b/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts similarity index 92% rename from src/vs/workbench/contrib/terminal/electron-browser/terminalPanel.ts rename to src/vs/workbench/contrib/terminal/browser/terminalPanel.ts index bfde146007b..b248acd1af3 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalPanel.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts @@ -6,7 +6,6 @@ import * as dom from 'vs/base/browser/dom'; import * as nls from 'vs/nls'; import * as platform from 'vs/base/common/platform'; -import * as terminalEnvironment from 'vs/workbench/contrib/terminal/node/terminalEnvironment'; import { Action, IAction } from 'vs/base/common/actions'; import { IActionItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -17,14 +16,13 @@ import { ITerminalService, TERMINAL_PANEL_ID } from 'vs/workbench/contrib/termin import { IThemeService, ITheme, registerThemingParticipant, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; import { TerminalFindWidget } from 'vs/workbench/contrib/terminal/browser/terminalFindWidget'; import { editorHoverBackground, editorHoverBorder, editorForeground } from 'vs/platform/theme/common/colorRegistry'; -import { KillTerminalAction, SwitchTerminalAction, SwitchTerminalActionItem, CopyTerminalSelectionAction, TerminalPasteAction, ClearTerminalAction, SelectAllTerminalAction, CreateNewTerminalAction, SplitTerminalAction } from 'vs/workbench/contrib/terminal/electron-browser/terminalActions'; +import { KillTerminalAction, SwitchTerminalAction, SwitchTerminalActionItem, CopyTerminalSelectionAction, TerminalPasteAction, ClearTerminalAction, SelectAllTerminalAction, CreateNewTerminalAction, SplitTerminalAction } from 'vs/workbench/contrib/terminal/browser/terminalActions'; import { Panel } from 'vs/workbench/browser/panel'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { URI } from 'vs/base/common/uri'; import { TERMINAL_BACKGROUND_COLOR, TERMINAL_BORDER_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; import { DataTransfers } from 'vs/base/browser/dnd'; import { INotificationService, IPromptChoice, Severity } from 'vs/platform/notification/common/notification'; -import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/electron-browser/terminalConfigHelper'; import { IStorageService } from 'vs/platform/storage/common/storage'; const FIND_FOCUS_CLASS = 'find-focused'; @@ -82,14 +80,12 @@ export class TerminalPanel extends Panel { if (e.affectsConfiguration('terminal.integrated.fontFamily') || e.affectsConfiguration('editor.fontFamily')) { const configHelper = this._terminalService.configHelper; - if (configHelper instanceof TerminalConfigHelper) { - if (!configHelper.configFontIsMonospace()) { - const choices: IPromptChoice[] = [{ - label: nls.localize('terminal.useMonospace', "Use 'monospace'"), - run: () => this._configurationService.updateValue('terminal.integrated.fontFamily', 'monospace'), - }]; - this._notificationService.prompt(Severity.Warning, nls.localize('terminal.monospaceOnly', "The terminal only supports monospace fonts."), choices); - } + if (!configHelper.configFontIsMonospace()) { + const choices: IPromptChoice[] = [{ + label: nls.localize('terminal.useMonospace', "Use 'monospace'"), + run: () => this._configurationService.updateValue('terminal.integrated.fontFamily', 'monospace'), + }]; + this._notificationService.prompt(Severity.Warning, nls.localize('terminal.monospaceOnly', "The terminal only supports monospace fonts."), choices); } } })); @@ -165,7 +161,7 @@ export class TerminalPanel extends Panel { return this._contextMenuActions; } - public getActionItem(action: Action): IActionItem { + public getActionItem(action: Action): IActionItem | null { if (action.id === SwitchTerminalAction.ID) { return this._instantiationService.createInstance(SwitchTerminalActionItem, action); } @@ -182,7 +178,7 @@ export class TerminalPanel extends Panel { public focusFindWidget() { const activeInstance = this._terminalService.getActiveInstance(); - if (activeInstance && activeInstance.hasSelection() && (activeInstance.selection.indexOf('\n') === -1)) { + if (activeInstance && activeInstance.hasSelection() && activeInstance.selection!.indexOf('\n') === -1) { this._findWidget.reveal(activeInstance.selection); } else { this._findWidget.reveal(); @@ -195,7 +191,7 @@ export class TerminalPanel extends Panel { public showFindWidget() { const activeInstance = this._terminalService.getActiveInstance(); - if (activeInstance && activeInstance.hasSelection() && (activeInstance.selection.indexOf('\n') === -1)) { + if (activeInstance && activeInstance.hasSelection() && activeInstance.selection!.indexOf('\n') === -1) { this._findWidget.show(activeInstance.selection); } else { this._findWidget.show(); @@ -215,10 +211,16 @@ export class TerminalPanel extends Panel { if (event.which === 2 && platform.isLinux) { // Drop selection and focus terminal on Linux to enable middle button paste when click // occurs on the selection itself. - this._terminalService.getActiveInstance().focus(); + const terminal = this._terminalService.getActiveInstance(); + if (terminal) { + terminal.focus(); + } } else if (event.which === 3) { if (this._terminalService.configHelper.config.rightClickBehavior === 'copyPaste') { const terminal = this._terminalService.getActiveInstance(); + if (!terminal) { + return; + } if (terminal.hasSelection()) { terminal.copySelection(); terminal.clearSelection(); @@ -246,7 +248,7 @@ export class TerminalPanel extends Panel { if (event.which === 1) { const terminal = this._terminalService.getActiveInstance(); - if (terminal.hasSelection()) { + if (terminal && terminal.hasSelection()) { terminal.copySelection(); } } @@ -278,7 +280,7 @@ export class TerminalPanel extends Panel { event.stopPropagation(); } })); - this._register(dom.addDisposableListener(this._parentDomElement, dom.EventType.DROP, (e: DragEvent) => { + this._register(dom.addDisposableListener(this._parentDomElement, dom.EventType.DROP, async (e: DragEvent) => { if (e.target === this._parentDomElement || dom.isAncestor(e.target as HTMLElement, this._parentDomElement)) { if (!e.dataTransfer) { return; @@ -299,7 +301,11 @@ export class TerminalPanel extends Panel { } const terminal = this._terminalService.getActiveInstance(); - terminal.sendText(terminalEnvironment.preparePathForTerminal(path), false); + if (terminal) { + return this._terminalService.preparePathForTerminalAsync(path, terminal.shellLaunchConfig.executable, terminal.title).then(preparedPath => { + terminal.sendText(preparedPath, false); + }); + } } })); } diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalProcessManager.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts similarity index 87% rename from src/vs/workbench/contrib/terminal/electron-browser/terminalProcessManager.ts rename to src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts index 330b7644171..432d886e99f 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalProcessManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts @@ -4,23 +4,24 @@ *--------------------------------------------------------------------------------------------*/ import * as platform from 'vs/base/common/platform'; -import * as terminalEnvironment from 'vs/workbench/contrib/terminal/node/terminalEnvironment'; +import * as terminalEnvironment from 'vs/workbench/contrib/terminal/common/terminalEnvironment'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { ProcessState, ITerminalProcessManager, IShellLaunchConfig, ITerminalConfigHelper } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ProcessState, ITerminalProcessManager, IShellLaunchConfig, ITerminalConfigHelper, ITerminalChildProcess } from 'vs/workbench/contrib/terminal/common/terminal'; import { ILogService } from 'vs/platform/log/common/log'; import { Emitter, Event } from 'vs/base/common/event'; import { IHistoryService } from 'vs/workbench/services/history/common/history'; -import { ITerminalChildProcess } from 'vs/workbench/contrib/terminal/node/terminal'; -import { TerminalProcessExtHostProxy } from 'vs/workbench/contrib/terminal/node/terminalProcessExtHostProxy'; +import { TerminalProcessExtHostProxy } from 'vs/workbench/contrib/terminal/common/terminalProcessExtHostProxy'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { TerminalProcess } from 'vs/workbench/contrib/terminal/node/terminalProcess'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; import { IWindowService } from 'vs/platform/windows/common/windows'; import { Schemas } from 'vs/base/common/network'; import { REMOTE_HOST_SCHEME, getRemoteAuthority } from 'vs/platform/remote/common/remoteHosts'; -import { sanitizeProcessEnvironment } from 'vs/base/node/processes'; -import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; +import { sanitizeProcessEnvironment } from 'vs/base/common/processes'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IProductService } from 'vs/platform/product/common/product'; +import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; /** The amount of time to consider terminal errors to be related to the launch */ const LAUNCHING_DURATION = 500; @@ -60,7 +61,10 @@ export class TerminalProcessManager implements ITerminalProcessManager { @IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService, @IConfigurationResolverService private readonly _configurationResolverService: IConfigurationResolverService, @IWindowService private readonly _windowService: IWindowService, - @IWorkspaceConfigurationService private readonly _workspaceConfigurationService: IWorkspaceConfigurationService, + @IConfigurationService private readonly _workspaceConfigurationService: IConfigurationService, + @IEnvironmentService private readonly _environmentService: IEnvironmentService, + @IProductService private readonly _productService: IProductService, + @ITerminalInstanceService private readonly _terminalInstanceService: ITerminalInstanceService ) { this.ptyProcessReady = new Promise(c => { this.onProcessReady(() => { @@ -110,7 +114,7 @@ export class TerminalProcessManager implements ITerminalProcessManager { } const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(Schemas.file); - const initialCwd = terminalEnvironment.getCwd(shellLaunchConfig, activeWorkspaceRootUri, this._configHelper.config.cwd); + const initialCwd = terminalEnvironment.getCwd(shellLaunchConfig, this._environmentService.userHome, activeWorkspaceRootUri, this._configHelper.config.cwd); // Compel type system as process.env should not have any undefined entries let env: platform.IProcessEnvironment = {}; @@ -138,14 +142,14 @@ export class TerminalProcessManager implements ITerminalProcessManager { // Sanitize the environment, removing any undesirable VS Code and Electron environment // variables - sanitizeProcessEnvironment(env); + sanitizeProcessEnvironment(env, 'VSCODE_IPC_HOOK_CLI'); // Adding other env keys necessary to create the process - terminalEnvironment.addTerminalEnvironmentKeys(env, platform.locale, this._configHelper.config.setLocaleVariables); + terminalEnvironment.addTerminalEnvironmentKeys(env, this._productService.version, platform.locale, this._configHelper.config.setLocaleVariables); } this._logService.debug(`Terminal process launching`, shellLaunchConfig, initialCwd, cols, rows, env); - this._process = new TerminalProcess(shellLaunchConfig, initialCwd, cols, rows, env, this._configHelper.config.windowsEnableConpty); + this._process = this._terminalInstanceService.createTerminalProcess(shellLaunchConfig, initialCwd, cols, rows, env, this._configHelper.config.windowsEnableConpty); } this.processState = ProcessState.LAUNCHING; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts new file mode 100644 index 00000000000..28a01868122 --- /dev/null +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -0,0 +1,175 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import * as platform from 'vs/base/common/platform'; +import { ITerminalService, TERMINAL_PANEL_ID, ITerminalInstance, IShellLaunchConfig, NEVER_SUGGEST_SELECT_WINDOWS_SHELL_STORAGE_KEY, ITerminalConfigHelper } from 'vs/workbench/contrib/terminal/common/terminal'; +import { TerminalService as CommonTerminalService } from 'vs/workbench/contrib/terminal/common/terminalService'; +import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; +import { IPartService } from 'vs/workbench/services/part/common/partService'; +import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { TerminalPanel } from 'vs/workbench/contrib/terminal/browser/terminalPanel'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; +import { TerminalTab } from 'vs/workbench/contrib/terminal/browser/terminalTab'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IWindowService } from 'vs/platform/windows/common/windows'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { IFileService } from 'vs/platform/files/common/files'; +import { TerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminalInstance'; +import { IBrowserTerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminal'; + +export abstract class TerminalService extends CommonTerminalService implements ITerminalService { + protected _configHelper: IBrowserTerminalConfigHelper; + + constructor( + @IContextKeyService contextKeyService: IContextKeyService, + @IPanelService panelService: IPanelService, + @IPartService partService: IPartService, + @ILifecycleService lifecycleService: ILifecycleService, + @IStorageService storageService: IStorageService, + @INotificationService notificationService: INotificationService, + @IDialogService dialogService: IDialogService, + @IInstantiationService protected readonly _instantiationService: IInstantiationService, + @IWindowService private _windowService: IWindowService, + @IExtensionService extensionService: IExtensionService, + @IFileService fileService: IFileService + ) { + super(contextKeyService, panelService, partService, lifecycleService, storageService, notificationService, dialogService, extensionService, fileService); + } + + protected abstract _getDefaultShell(p: platform.Platform): string; + + public createInstance(terminalFocusContextKey: IContextKey, configHelper: ITerminalConfigHelper, container: HTMLElement | undefined, shellLaunchConfig: IShellLaunchConfig, doCreateProcess: boolean): ITerminalInstance { + const instance = this._instantiationService.createInstance(TerminalInstance, terminalFocusContextKey, configHelper, container, shellLaunchConfig); + this._onInstanceCreated.fire(instance); + return instance; + } + + public createTerminal(shell: IShellLaunchConfig = {}, wasNewTerminalAction?: boolean): ITerminalInstance { + const terminalTab = this._instantiationService.createInstance(TerminalTab, + this._terminalFocusContextKey, + this.configHelper, + this._terminalContainer, + shell); + this._terminalTabs.push(terminalTab); + const instance = terminalTab.terminalInstances[0]; + terminalTab.addDisposable(terminalTab.onDisposed(this._onTabDisposed.fire, this._onTabDisposed)); + terminalTab.addDisposable(terminalTab.onInstancesChanged(this._onInstancesChanged.fire, this._onInstancesChanged)); + this._initInstanceListeners(instance); + if (this.terminalInstances.length === 1) { + // It's the first instance so it should be made active automatically + this.setActiveInstanceByIndex(0); + } + this._onInstancesChanged.fire(); + this._suggestShellChange(wasNewTerminalAction); + return instance; + } + + private _suggestShellChange(wasNewTerminalAction?: boolean): void { + // Only suggest on Windows since $SHELL works great for macOS/Linux + if (!platform.isWindows) { + return; + } + + if (this._windowService.getConfiguration().remoteAuthority) { + // Don't suggest if the opened workspace is remote + return; + } + + // Only suggest when the terminal instance is being created by an explicit user action to + // launch a terminal, as opposed to something like tasks, debug, panel restore, etc. + if (!wasNewTerminalAction) { + return; + } + + if (this._windowService.getConfiguration().remoteAuthority) { + // Don't suggest if the opened workspace is remote + return; + } + + // Don't suggest if the user has explicitly opted out + const neverSuggest = this._storageService.getBoolean(NEVER_SUGGEST_SELECT_WINDOWS_SHELL_STORAGE_KEY, StorageScope.GLOBAL, false); + if (neverSuggest) { + return; + } + + // Never suggest if the setting is non-default already (ie. they set the setting manually) + if (this.configHelper.config.shell.windows !== this._getDefaultShell(platform.Platform.Windows)) { + this._storageService.store(NEVER_SUGGEST_SELECT_WINDOWS_SHELL_STORAGE_KEY, true, StorageScope.GLOBAL); + return; + } + + this._notificationService.prompt( + Severity.Info, + nls.localize('terminal.integrated.chooseWindowsShellInfo', "You can change the default terminal shell by selecting the customize button."), + [{ + label: nls.localize('customize', "Customize"), + run: () => { + this.selectDefaultWindowsShell().then(shell => { + if (!shell) { + return Promise.resolve(null); + } + // Launch a new instance with the newly selected shell + const instance = this.createTerminal({ + executable: shell, + args: this.configHelper.config.shellArgs.windows + }); + if (instance) { + this.setActiveInstance(instance); + } + return Promise.resolve(null); + }); + } + }, + { + label: nls.localize('never again', "Don't Show Again"), + isSecondary: true, + run: () => this._storageService.store(NEVER_SUGGEST_SELECT_WINDOWS_SHELL_STORAGE_KEY, true, StorageScope.GLOBAL) + }] + ); + } + + public focusFindWidget(): Promise { + return this.showPanel(false).then(() => { + const panel = this._panelService.getActivePanel() as TerminalPanel; + panel.focusFindWidget(); + this._findWidgetVisible.set(true); + }); + } + + public hideFindWidget(): void { + const panel = this._panelService.getActivePanel() as TerminalPanel; + if (panel && panel.getId() === TERMINAL_PANEL_ID) { + panel.hideFindWidget(); + this._findWidgetVisible.reset(); + panel.focus(); + } + } + + public findNext(): void { + const panel = this._panelService.getActivePanel() as TerminalPanel; + if (panel && panel.getId() === TERMINAL_PANEL_ID) { + panel.showFindWidget(); + panel.getFindWidget().find(false); + } + } + + public findPrevious(): void { + const panel = this._panelService.getActivePanel() as TerminalPanel; + if (panel && panel.getId() === TERMINAL_PANEL_ID) { + panel.showFindWidget(); + panel.getFindWidget().find(true); + } + } + + public setContainers(panelContainer: HTMLElement, terminalContainer: HTMLElement): void { + this._configHelper.panelContainer = panelContainer; + this._terminalContainer = terminalContainer; + this._terminalTabs.forEach(tab => tab.attachToElement(this._terminalContainer)); + } +} \ No newline at end of file diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index d36b3cbeae3..ec0303a0ea3 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -13,8 +13,6 @@ import { FindReplaceState } from 'vs/editor/contrib/find/findState'; export const TERMINAL_PANEL_ID = 'workbench.panel.terminal'; -export const TERMINAL_SERVICE_ID = 'terminalService'; - /** A context key that is set when there is at least one opened integrated terminal. */ export const KEYBINDING_CONTEXT_TERMINAL_IS_OPEN = new RawContextKey('terminalIsOpen', false); /** A context key that is set when the integrated terminal has focus. */ @@ -47,7 +45,7 @@ export const NEVER_MEASURE_RENDER_TIME_STORAGE_KEY = 'terminal.integrated.neverM // trying to create the corressponding object on the ext host. export const EXT_HOST_CREATION_DELAY = 100; -export const ITerminalService = createDecorator(TERMINAL_SERVICE_ID); +export const ITerminalService = createDecorator('terminalService'); export const TerminalCursorStyle = { BLOCK: 'block', @@ -107,6 +105,7 @@ export interface ITerminalConfiguration { export interface ITerminalConfigHelper { config: ITerminalConfiguration; + configFontIsMonospace(): boolean; getFont(): ITerminalFont; /** * Merges the default shell path and args into the provided launch configuration @@ -251,9 +250,20 @@ export interface ITerminalService { findPrevious(): void; setContainers(panelContainer: HTMLElement, terminalContainer: HTMLElement): void; - selectDefaultWindowsShell(): Promise; + selectDefaultWindowsShell(): Promise; setWorkspaceShellAllowed(isAllowed: boolean): void; + /** + * Takes a path and returns the properly escaped path to send to the terminal. + * On Windows, this included trying to prepare the path for WSL if needed. + * + * @param executable The executable off the shellLaunchConfig + * @param title The terminal's title + * @param path The path to be escaped and formatted. + * @returns An escaped version of the path to be execuded in the terminal. + */ + preparePathForTerminalAsync(path: string, executable: string | undefined, title: string): Promise; + requestExtHostProcess(proxy: ITerminalProcessExtHostProxy, shellLaunchConfig: IShellLaunchConfig, activeWorkspaceRootUri: URI, cols: number, rows: number): void; } @@ -524,15 +534,6 @@ export interface ITerminalInstance { */ sendText(text: string, addNewLine: boolean): void; - /** - * Takes a path and returns the properly escaped path to send to the terminal. - * On Windows, this included trying to prepare the path for WSL if needed. - * - * @param path The path to be escaped and formatted. - * @returns An escaped version of the path to be execuded in the terminal. - */ - preparePathForTerminalAsync(path: string): Promise; - /** * Write text directly to the terminal, skipping the process if it exists. * @param text The text to write. @@ -686,4 +687,38 @@ export interface ITerminalProcessExtHostRequest { activeWorkspaceRootUri: URI; cols: number; rows: number; +} + +export enum LinuxDistro { + Fedora, + Ubuntu, + Unknown +} + +export interface IWindowsShellHelper extends IDisposable { + getShellName(): Promise; +} + +/** + * An interface representing a raw terminal child process, this contains a subset of the + * child_process.ChildProcess node.js interface. + */ +export interface ITerminalChildProcess { + onProcessData: Event; + onProcessExit: Event; + onProcessIdReady: Event; + onProcessTitleChanged: Event; + + /** + * Shutdown the terminal process. + * + * @param immediate When true the process will be killed immediately, otherwise the process will + * be given some time to make sure no additional data comes through. + */ + shutdown(immediate: boolean): void; + input(data: string): void; + resize(cols: number, rows: number): void; + + getInitialCwd(): Promise; + getCwd(): Promise; } \ No newline at end of file diff --git a/src/vs/workbench/contrib/terminal/node/terminalEnvironment.ts b/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts similarity index 80% rename from src/vs/workbench/contrib/terminal/node/terminalEnvironment.ts rename to src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts index 612ea746d21..5480374bba8 100644 --- a/src/vs/workbench/contrib/terminal/node/terminalEnvironment.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts @@ -3,10 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as os from 'os'; import * as path from 'vs/base/common/path'; import * as platform from 'vs/base/common/platform'; -import pkg from 'vs/platform/product/node/package'; import { URI as Uri } from 'vs/base/common/uri'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { IShellLaunchConfig, ITerminalEnvironment } from 'vs/workbench/contrib/terminal/common/terminal'; @@ -51,9 +49,9 @@ function _mergeEnvironmentValue(env: ITerminalEnvironment, key: string, value: s } } -export function addTerminalEnvironmentKeys(env: ITerminalEnvironment, locale: string | undefined, setLocaleVariables: boolean): void { +export function addTerminalEnvironmentKeys(env: ITerminalEnvironment, version: string | undefined, locale: string | undefined, setLocaleVariables: boolean): void { env['TERM_PROGRAM'] = 'vscode'; - env['TERM_PROGRAM_VERSION'] = pkg.version; + env['TERM_PROGRAM_VERSION'] = version ? version : null; if (setLocaleVariables) { env['LANG'] = _getLangEnvVariable(locale); } @@ -102,7 +100,7 @@ function _getLangEnvVariable(locale?: string) { return parts.join('_') + '.UTF-8'; } -export function getCwd(shell: IShellLaunchConfig, root?: Uri, customCwd?: string): string { +export function getCwd(shell: IShellLaunchConfig, userHome: string, root?: Uri, customCwd?: string): string { if (shell.cwd) { return (typeof shell.cwd === 'object') ? shell.cwd.fsPath : shell.cwd; } @@ -120,7 +118,7 @@ export function getCwd(shell: IShellLaunchConfig, root?: Uri, customCwd?: string // If there was no custom cwd or it was relative with no workspace if (!cwd) { - cwd = root ? root.fsPath : os.homedir(); + cwd = root ? root.fsPath : userHome; } return _sanitizeCwd(cwd); @@ -134,26 +132,15 @@ function _sanitizeCwd(cwd: string): string { return cwd; } -/** - * Adds quotes to a path if it contains whitespaces - */ -export function preparePathForTerminal(path: string): string { - if (platform.isWindows) { - if (/\s+/.test(path)) { - return `"${path}"`; - } - return path; +export function escapeNonWindowsPath(path: string): string { + let newPath = path; + if (newPath.indexOf('\\') !== 0) { + newPath = newPath.replace(/\\/g, '\\\\'); } - path = path.replace(/(%5C|\\)/g, '\\\\'); - const charsToEscape = [ - ' ', '\'', '"', '?', ':', ';', '!', '*', '(', ')', '{', '}', '[', ']' - ]; - for (let i = 0; i < path.length; i++) { - const indexOfChar = charsToEscape.indexOf(path.charAt(i)); - if (indexOfChar >= 0) { - path = `${path.substring(0, i)}\\${path.charAt(i)}${path.substring(i + 1)}`; - i++; // Skip char due to escape char being added - } + if (!newPath && (newPath.indexOf('"') !== -1)) { + newPath = '\'' + newPath + '\''; + } else if (newPath.indexOf(' ') !== -1) { + newPath = newPath.replace(/ /g, '\\ '); } - return path; + return newPath; } diff --git a/src/vs/workbench/contrib/terminal/node/terminalProcessExtHostProxy.ts b/src/vs/workbench/contrib/terminal/common/terminalProcessExtHostProxy.ts similarity index 96% rename from src/vs/workbench/contrib/terminal/node/terminalProcessExtHostProxy.ts rename to src/vs/workbench/contrib/terminal/common/terminalProcessExtHostProxy.ts index 5eb61629e48..84131aa9edd 100644 --- a/src/vs/workbench/contrib/terminal/node/terminalProcessExtHostProxy.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalProcessExtHostProxy.ts @@ -3,9 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ITerminalChildProcess } from 'vs/workbench/contrib/terminal/node/terminal'; import { Event, Emitter } from 'vs/base/common/event'; -import { ITerminalService, ITerminalProcessExtHostProxy, IShellLaunchConfig } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalService, ITerminalProcessExtHostProxy, IShellLaunchConfig, ITerminalChildProcess } from 'vs/workbench/contrib/terminal/common/terminal'; import { IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; diff --git a/src/vs/workbench/contrib/terminal/common/terminalService.ts b/src/vs/workbench/contrib/terminal/common/terminalService.ts index 2af5a375481..b2d81641a84 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalService.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as nls from 'vs/nls'; import { Event, Emitter } from 'vs/base/common/event'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; @@ -12,6 +13,13 @@ import { ITerminalService, ITerminalInstance, IShellLaunchConfig, ITerminalConfi import { IStorageService } from 'vs/platform/storage/common/storage'; import { URI } from 'vs/base/common/uri'; import { FindReplaceState } from 'vs/editor/contrib/find/findState'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { IFileService } from 'vs/platform/files/common/files'; +import { escapeNonWindowsPath } from 'vs/workbench/contrib/terminal/common/terminalEnvironment'; +import { isWindows } from 'vs/base/common/platform'; +import { basename } from 'vs/base/common/path'; export abstract class TerminalService implements ITerminalService { public _serviceBrand: any; @@ -20,8 +28,10 @@ export abstract class TerminalService implements ITerminalService { protected _terminalFocusContextKey: IContextKey; protected _findWidgetVisible: IContextKey; protected _terminalContainer: HTMLElement; - protected _terminalTabs: ITerminalTab[]; - protected abstract _terminalInstances: ITerminalInstance[]; + protected _terminalTabs: ITerminalTab[] = []; + protected get _terminalInstances(): ITerminalInstance[] { + return this._terminalTabs.reduce((p, c) => p.concat(c.terminalInstances), []); + } private _findState: FindReplaceState; private _activeTabIndex: number; @@ -58,7 +68,11 @@ export abstract class TerminalService implements ITerminalService { @IPanelService protected readonly _panelService: IPanelService, @IPartService private readonly _partService: IPartService, @ILifecycleService lifecycleService: ILifecycleService, - @IStorageService protected readonly _storageService: IStorageService + @IStorageService protected readonly _storageService: IStorageService, + @INotificationService protected readonly _notificationService: INotificationService, + @IDialogService private readonly _dialogService: IDialogService, + @IExtensionService private readonly _extensionService: IExtensionService, + @IFileService private readonly _fileService: IFileService ) { this._activeTabIndex = 0; this._isShuttingDown = false; @@ -86,15 +100,32 @@ export abstract class TerminalService implements ITerminalService { this.onInstancesChanged(() => updateTerminalContextKeys()); } - protected abstract _showTerminalCloseConfirmation(): Promise; - protected abstract _showNotEnoughSpaceToast(): void; + protected abstract _getWslPath(path: string): Promise; + protected abstract _getWindowsBuildNumber(): number; + public abstract createTerminal(shell?: IShellLaunchConfig, wasNewTerminalAction?: boolean): ITerminalInstance; - public abstract createTerminalRenderer(name: string): ITerminalInstance; public abstract createInstance(terminalFocusContextKey: IContextKey, configHelper: ITerminalConfigHelper, container: HTMLElement, shellLaunchConfig: IShellLaunchConfig, doCreateProcess: boolean): ITerminalInstance; - public abstract getActiveOrCreateInstance(wasNewTerminalAction?: boolean): ITerminalInstance; - public abstract selectDefaultWindowsShell(): Promise; + public abstract selectDefaultWindowsShell(): Promise; public abstract setContainers(panelContainer: HTMLElement, terminalContainer: HTMLElement): void; - public abstract requestExtHostProcess(proxy: ITerminalProcessExtHostProxy, shellLaunchConfig: IShellLaunchConfig, activeWorkspaceRootUri: URI, cols: number, rows: number): void; + + public createTerminalRenderer(name: string): ITerminalInstance { + return this.createTerminal({ name, isRendererOnly: true }); + } + + public getActiveOrCreateInstance(wasNewTerminalAction?: boolean): ITerminalInstance { + const activeInstance = this.getActiveInstance(); + return activeInstance ? activeInstance : this.createTerminal(undefined, wasNewTerminalAction); + } + + public requestExtHostProcess(proxy: ITerminalProcessExtHostProxy, shellLaunchConfig: IShellLaunchConfig, activeWorkspaceRootUri: URI, cols: number, rows: number): void { + // Ensure extension host is ready before requesting a process + this._extensionService.whenInstalledExtensionsRegistered().then(() => { + // TODO: MainThreadTerminalService is not ready at this point, fix this + setTimeout(() => { + this._onInstanceRequestExtHostProcess.fire({ proxy, shellLaunchConfig, activeWorkspaceRootUri, cols, rows }); + }, 500); + }); + } private _onBeforeShutdown(): boolean | Promise { if (this.terminalInstances.length === 0) { @@ -368,4 +399,73 @@ export abstract class TerminalService implements ITerminalService { public setWorkspaceShellAllowed(isAllowed: boolean): void { this.configHelper.setWorkspaceShellAllowed(isAllowed); } + + protected _showTerminalCloseConfirmation(): Promise { + let message; + if (this.terminalInstances.length === 1) { + message = nls.localize('terminalService.terminalCloseConfirmationSingular', "There is an active terminal session, do you want to kill it?"); + } else { + message = nls.localize('terminalService.terminalCloseConfirmationPlural', "There are {0} active terminal sessions, do you want to kill them?", this.terminalInstances.length); + } + + return this._dialogService.confirm({ + message, + type: 'warning', + }).then(res => !res.confirmed); + } + + protected _showNotEnoughSpaceToast(): void { + this._notificationService.info(nls.localize('terminal.minWidth', "Not enough space to split terminal.")); + } + + protected _validateShellPaths(label: string, potentialPaths: string[]): Promise<[string, string] | null> { + if (potentialPaths.length === 0) { + return Promise.resolve(null); + } + const current = potentialPaths.shift(); + return this._fileService.existsFile(URI.file(current!)).then(exists => { + if (!exists) { + return this._validateShellPaths(label, potentialPaths); + } + return [label, current] as [string, string]; + }); + } + + public preparePathForTerminalAsync(originalPath: string, executable: string, title: string): Promise { + return new Promise(c => { + const exe = executable; + if (!exe) { + c(originalPath); + return; + } + + const hasSpace = originalPath.indexOf(' ') !== -1; + + const pathBasename = basename(exe, '.exe'); + const isPowerShell = pathBasename === 'pwsh' || + title === 'pwsh' || + pathBasename === 'powershell' || + title === 'powershell'; + + if (isPowerShell && (hasSpace || originalPath.indexOf('\'') !== -1)) { + c(`& '${originalPath.replace(/'/g, '\'\'')}'`); + return; + } + + if (isWindows) { + // 17063 is the build number where wsl path was introduced. + // Update Windows uriPath to be executed in WSL. + if (((exe.indexOf('wsl') !== -1) || ((exe.indexOf('bash.exe') !== -1) && (exe.indexOf('git') === -1))) && (this._getWindowsBuildNumber() >= 17063)) { + c(this._getWslPath(originalPath)); + return; + } else if (hasSpace) { + c('"' + originalPath + '"'); + } else { + c(originalPath); + } + return; + } + c(escapeNonWindowsPath(originalPath)); + }); + } } diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminal.contribution.ts index b15ee85b95b..a55b93caa40 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminal.contribution.ts @@ -3,64 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!./media/scrollbar'; -import 'vs/css!./media/terminal'; -import 'vs/css!./media/xterm'; -import 'vs/css!./media/widgets'; -import * as nls from 'vs/nls'; -import * as panel from 'vs/workbench/browser/panel'; import * as platform from 'vs/base/common/platform'; +import * as nls from 'vs/nls'; import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; -import { ITerminalService, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, TERMINAL_PANEL_ID, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE, TerminalCursorStyle, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_NOT_VISIBLE, DEFAULT_LINE_HEIGHT, DEFAULT_LETTER_SPACING, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED } from 'vs/workbench/contrib/terminal/common/terminal'; -import { getDefaultShell } from 'vs/workbench/contrib/terminal/node/terminal'; -import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; -import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { KillTerminalAction, ClearSelectionTerminalAction, CopyTerminalSelectionAction, CreateNewTerminalAction, CreateNewInActiveWorkspaceTerminalAction, FocusActiveTerminalAction, FocusNextTerminalAction, FocusPreviousTerminalAction, SelectDefaultShellWindowsTerminalAction, RunSelectedTextInTerminalAction, RunActiveFileInTerminalAction, ScrollDownTerminalAction, ScrollDownPageTerminalAction, ScrollToBottomTerminalAction, ScrollUpTerminalAction, ScrollUpPageTerminalAction, ScrollToTopTerminalAction, TerminalPasteAction, ToggleTerminalAction, ClearTerminalAction, AllowWorkspaceShellTerminalCommand, DisallowWorkspaceShellTerminalCommand, RenameTerminalAction, SelectAllTerminalAction, FocusTerminalFindWidgetAction, HideTerminalFindWidgetAction, DeleteWordLeftTerminalAction, DeleteWordRightTerminalAction, QuickOpenActionTermContributor, QuickOpenTermAction, TERMINAL_PICKER_PREFIX, MoveToLineStartTerminalAction, MoveToLineEndTerminalAction, SplitTerminalAction, SplitInActiveWorkspaceTerminalAction, FocusPreviousPaneTerminalAction, FocusNextPaneTerminalAction, ResizePaneLeftTerminalAction, ResizePaneRightTerminalAction, ResizePaneUpTerminalAction, ResizePaneDownTerminalAction, ScrollToPreviousCommandAction, ScrollToNextCommandAction, SelectToPreviousCommandAction, SelectToNextCommandAction, SelectToPreviousLineAction, SelectToNextLineAction, ToggleEscapeSequenceLoggingAction, SendSequenceTerminalCommand, ToggleRegexCommand, ToggleWholeWordCommand, ToggleCaseSensitiveCommand, FindNext, FindPrevious, DeleteToLineStartTerminalAction } from 'vs/workbench/contrib/terminal/electron-browser/terminalActions'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; -import { TerminalService } from 'vs/workbench/contrib/terminal/electron-browser/terminalService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { EDITOR_FONT_DEFAULTS } from 'vs/editor/common/config/editorOptions'; -import { registerColors } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; -import { getQuickNavigateHandler } from 'vs/workbench/browser/parts/quickopen/quickopen'; -import { IQuickOpenRegistry, Extensions as QuickOpenExtensions, QuickOpenHandlerDescriptor } from 'vs/workbench/browser/quickopen'; -import { Scope, IActionBarRegistry, Extensions as ActionBarExtensions } from 'vs/workbench/browser/actions'; -import { CommandsRegistry } from 'vs/platform/commands/common/commands'; -import { TerminalPanel } from 'vs/workbench/contrib/terminal/electron-browser/terminalPanel'; -import { TerminalPickerHandler } from 'vs/workbench/contrib/terminal/browser/terminalQuickOpen'; -import { setupTerminalCommands, TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminalCommands'; -import { setupTerminalMenu } from 'vs/workbench/contrib/terminal/common/terminalMenu'; -import { DEFAULT_COMMANDS_TO_SKIP_SHELL } from 'vs/workbench/contrib/terminal/electron-browser/terminalInstance'; - -const quickOpenRegistry = (Registry.as(QuickOpenExtensions.Quickopen)); - -const inTerminalsPicker = 'inTerminalPicker'; - -quickOpenRegistry.registerQuickOpenHandler( - new QuickOpenHandlerDescriptor( - TerminalPickerHandler, - TerminalPickerHandler.ID, - TERMINAL_PICKER_PREFIX, - inTerminalsPicker, - nls.localize('quickOpen.terminal', "Show All Opened Terminals") - ) -); - -const quickOpenNavigateNextInTerminalPickerId = 'workbench.action.quickOpenNavigateNextInTerminalPicker'; -CommandsRegistry.registerCommand( - { id: quickOpenNavigateNextInTerminalPickerId, handler: getQuickNavigateHandler(quickOpenNavigateNextInTerminalPickerId, true) }); - -const quickOpenNavigatePreviousInTerminalPickerId = 'workbench.action.quickOpenNavigatePreviousInTerminalPicker'; -CommandsRegistry.registerCommand( - { id: quickOpenNavigatePreviousInTerminalPickerId, handler: getQuickNavigateHandler(quickOpenNavigatePreviousInTerminalPickerId, false) }); - - -const registry = Registry.as(ActionExtensions.WorkbenchActions); -registry.registerWorkbenchAction(new SyncActionDescriptor(QuickOpenTermAction, QuickOpenTermAction.ID, QuickOpenTermAction.LABEL), 'Terminal: Switch Active Terminal', nls.localize('terminal', "Terminal")); -const actionBarRegistry = Registry.as(ActionBarExtensions.Actionbar); -actionBarRegistry.registerActionBarContributor(Scope.VIEWER, QuickOpenActionTermContributor); +import { Registry } from 'vs/platform/registry/common/platform'; +import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { ITerminalService } from 'vs/workbench/contrib/terminal/common/terminal'; +import { TerminalInstanceService } from 'vs/workbench/contrib/terminal/electron-browser/terminalInstanceService'; +import { TerminalService } from 'vs/workbench/contrib/terminal/electron-browser/terminalService'; +import { getDefaultShell } from 'vs/workbench/contrib/terminal/node/terminal'; const configurationRegistry = Registry.as(Extensions.Configuration); configurationRegistry.registerConfiguration({ @@ -74,466 +26,18 @@ configurationRegistry.registerConfiguration({ type: 'string', default: getDefaultShell(platform.Platform.Linux) }, - 'terminal.integrated.shellArgs.linux': { - markdownDescription: nls.localize('terminal.integrated.shellArgs.linux', "The command line arguments to use when on the Linux terminal. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration)."), - type: 'array', - items: { - type: 'string' - }, - default: [] - }, 'terminal.integrated.shell.osx': { markdownDescription: nls.localize('terminal.integrated.shell.osx', "The path of the shell that the terminal uses on macOS. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration)."), type: 'string', default: getDefaultShell(platform.Platform.Mac) }, - 'terminal.integrated.shellArgs.osx': { - markdownDescription: nls.localize('terminal.integrated.shellArgs.osx', "The command line arguments to use when on the macOS terminal. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration)."), - type: 'array', - items: { - type: 'string' - }, - // Unlike on Linux, ~/.profile is not sourced when logging into a macOS session. This - // is the reason terminals on macOS typically run login shells by default which set up - // the environment. See http://unix.stackexchange.com/a/119675/115410 - default: ['-l'] - }, 'terminal.integrated.shell.windows': { markdownDescription: nls.localize('terminal.integrated.shell.windows', "The path of the shell that the terminal uses on Windows. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration)."), type: 'string', default: getDefaultShell(platform.Platform.Windows) - }, - 'terminal.integrated.shellArgs.windows': { - markdownDescription: nls.localize('terminal.integrated.shellArgs.windows', "The command line arguments to use when on the Windows terminal. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration)."), - 'anyOf': [ - { - type: 'array', - items: { - type: 'string', - markdownDescription: nls.localize('terminal.integrated.shellArgs.windows', "The command line arguments to use when on the Windows terminal. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration).") - }, - }, - { - type: 'string', - markdownDescription: nls.localize('terminal.integrated.shellArgs.windows.string', "The command line arguments in [command-line format](https://msdn.microsoft.com/en-au/08dfcab2-eb6e-49a4-80eb-87d4076c98c6) to use when on the Windows terminal. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration).") - } - ], - default: [] - }, - 'terminal.integrated.macOptionIsMeta': { - description: nls.localize('terminal.integrated.macOptionIsMeta', "Controls whether to treat the option key as the meta key in the terminal on macOS."), - type: 'boolean', - default: false - }, - 'terminal.integrated.macOptionClickForcesSelection': { - description: nls.localize('terminal.integrated.macOptionClickForcesSelection', "Controls whether to force selection when using Option+click on macOS. This will force a regular (line) selection and disallow the use of column selection mode. This enables copying and pasting using the regular terminal selection, for example, when mouse mode is enabled in tmux."), - type: 'boolean', - default: false - }, - 'terminal.integrated.copyOnSelection': { - description: nls.localize('terminal.integrated.copyOnSelection', "Controls whether text selected in the terminal will be copied to the clipboard."), - type: 'boolean', - default: false - }, - 'terminal.integrated.drawBoldTextInBrightColors': { - description: nls.localize('terminal.integrated.drawBoldTextInBrightColors', "Controls whether bold text in the terminal will always use the \"bright\" ANSI color variant."), - type: 'boolean', - default: true - }, - 'terminal.integrated.fontFamily': { - markdownDescription: nls.localize('terminal.integrated.fontFamily', "Controls the font family of the terminal, this defaults to `#editor.fontFamily#`'s value."), - type: 'string' - }, - // TODO: Support font ligatures - // 'terminal.integrated.fontLigatures': { - // 'description': nls.localize('terminal.integrated.fontLigatures', "Controls whether font ligatures are enabled in the terminal."), - // 'type': 'boolean', - // 'default': false - // }, - 'terminal.integrated.fontSize': { - description: nls.localize('terminal.integrated.fontSize', "Controls the font size in pixels of the terminal."), - type: 'number', - default: EDITOR_FONT_DEFAULTS.fontSize - }, - 'terminal.integrated.letterSpacing': { - description: nls.localize('terminal.integrated.letterSpacing', "Controls the letter spacing of the terminal, this is an integer value which represents the amount of additional pixels to add between characters."), - type: 'number', - default: DEFAULT_LETTER_SPACING - }, - 'terminal.integrated.lineHeight': { - description: nls.localize('terminal.integrated.lineHeight', "Controls the line height of the terminal, this number is multiplied by the terminal font size to get the actual line-height in pixels."), - type: 'number', - default: DEFAULT_LINE_HEIGHT - }, - 'terminal.integrated.fontWeight': { - type: 'string', - enum: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'], - description: nls.localize('terminal.integrated.fontWeight', "The font weight to use within the terminal for non-bold text."), - default: 'normal' - }, - 'terminal.integrated.fontWeightBold': { - type: 'string', - enum: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'], - description: nls.localize('terminal.integrated.fontWeightBold', "The font weight to use within the terminal for bold text."), - default: 'bold' - }, - 'terminal.integrated.cursorBlinking': { - description: nls.localize('terminal.integrated.cursorBlinking', "Controls whether the terminal cursor blinks."), - type: 'boolean', - default: false - }, - 'terminal.integrated.cursorStyle': { - description: nls.localize('terminal.integrated.cursorStyle', "Controls the style of terminal cursor."), - enum: [TerminalCursorStyle.BLOCK, TerminalCursorStyle.LINE, TerminalCursorStyle.UNDERLINE], - default: TerminalCursorStyle.BLOCK - }, - 'terminal.integrated.scrollback': { - description: nls.localize('terminal.integrated.scrollback', "Controls the maximum amount of lines the terminal keeps in its buffer."), - type: 'number', - default: 1000 - }, - 'terminal.integrated.setLocaleVariables': { - markdownDescription: nls.localize('terminal.integrated.setLocaleVariables', "Controls whether locale variables are set at startup of the terminal."), - type: 'boolean', - default: true - }, - 'terminal.integrated.rendererType': { - type: 'string', - enum: ['auto', 'canvas', 'dom'], - enumDescriptions: [ - nls.localize('terminal.integrated.rendererType.auto', "Let VS Code guess which renderer to use."), - nls.localize('terminal.integrated.rendererType.canvas', "Use the standard GPU/canvas-based renderer"), - nls.localize('terminal.integrated.rendererType.dom', "Use the fallback DOM-based renderer.") - ], - default: 'auto', - description: nls.localize('terminal.integrated.rendererType', "Controls how the terminal is rendered.") - }, - 'terminal.integrated.rightClickBehavior': { - type: 'string', - enum: ['default', 'copyPaste', 'selectWord'], - enumDescriptions: [ - nls.localize('terminal.integrated.rightClickBehavior.default', "Show the context menu."), - nls.localize('terminal.integrated.rightClickBehavior.copyPaste', "Copy when there is a selection, otherwise paste."), - nls.localize('terminal.integrated.rightClickBehavior.selectWord', "Select the word under the cursor and show the context menu.") - ], - default: platform.isMacintosh ? 'selectWord' : platform.isWindows ? 'copyPaste' : 'default', - description: nls.localize('terminal.integrated.rightClickBehavior', "Controls how terminal reacts to right click.") - }, - 'terminal.integrated.cwd': { - description: nls.localize('terminal.integrated.cwd', "An explicit start path where the terminal will be launched, this is used as the current working directory (cwd) for the shell process. This may be particularly useful in workspace settings if the root directory is not a convenient cwd."), - type: 'string', - default: undefined - }, - 'terminal.integrated.confirmOnExit': { - description: nls.localize('terminal.integrated.confirmOnExit', "Controls whether to confirm on exit if there are active terminal sessions."), - type: 'boolean', - default: false - }, - 'terminal.integrated.enableBell': { - description: nls.localize('terminal.integrated.enableBell', "Controls whether the terminal bell is enabled."), - type: 'boolean', - default: false - }, - 'terminal.integrated.commandsToSkipShell': { - description: nls.localize('terminal.integrated.commandsToSkipShell', "A set of command IDs whose keybindings will not be sent to the shell and instead always be handled by Code. This allows the use of keybindings that would normally be consumed by the shell to act the same as when the terminal is not focused, for example ctrl+p to launch Quick Open.\nDefault Skipped Commands:\n\n{0}", DEFAULT_COMMANDS_TO_SKIP_SHELL.sort().map(command => `- ${command}`).join('\n')), - type: 'array', - items: { - type: 'string' - }, - default: [] - }, - 'terminal.integrated.env.osx': { - markdownDescription: nls.localize('terminal.integrated.env.osx', "Object with environment variables that will be added to the VS Code process to be used by the terminal on macOS. Set to `null` to delete the environment variable."), - type: 'object', - additionalProperties: { - type: ['string', 'null'] - }, - default: {} - }, - 'terminal.integrated.env.linux': { - markdownDescription: nls.localize('terminal.integrated.env.linux', "Object with environment variables that will be added to the VS Code process to be used by the terminal on Linux. Set to `null` to delete the environment variable."), - type: 'object', - additionalProperties: { - type: ['string', 'null'] - }, - default: {} - }, - 'terminal.integrated.env.windows': { - markdownDescription: nls.localize('terminal.integrated.env.windows', "Object with environment variables that will be added to the VS Code process to be used by the terminal on Windows. Set to `null` to delete the environment variable."), - type: 'object', - additionalProperties: { - type: ['string', 'null'] - }, - default: {} - }, - 'terminal.integrated.showExitAlert': { - description: nls.localize('terminal.integrated.showExitAlert', "Controls whether to show the alert \"The terminal process terminated with exit code\" when exit code is non-zero."), - type: 'boolean', - default: true - }, - 'terminal.integrated.splitCwd': { - description: nls.localize('terminal.integrated.splitCwd', "Controls the working directory a split terminal starts with."), - type: 'string', - enum: ['workspaceRoot', 'initial', 'inherited'], - enumDescriptions: [ - nls.localize('terminal.integrated.splitCwd.workspaceRoot', "A new split terminal will use the workspace root as the working directory. In a multi-root workspace a choice for which root folder to use is offered."), - nls.localize('terminal.integrated.splitCwd.initial', "A new split terminal will use the working directory that the parent terminal started with."), - nls.localize('terminal.integrated.splitCwd.inherited', "On macOS and Linux, a new split terminal will use the working directory of the parent terminal. On Windows, this behaves the same as initial."), - ], - default: 'inherited' - }, - 'terminal.integrated.windowsEnableConpty': { - description: nls.localize('terminal.integrated.windowsEnableConpty', "Whether to use ConPTY for Windows terminal process communication (requires Windows 10 build number 18309+). Winpty will be used if this is false."), - type: 'boolean', - default: false } } }); registerSingleton(ITerminalService, TerminalService, true); - -(Registry.as(panel.Extensions.Panels)).registerPanel(new panel.PanelDescriptor( - TerminalPanel, - TERMINAL_PANEL_ID, - nls.localize('terminal', "Terminal"), - 'terminal', - 40, - TERMINAL_COMMAND_ID.TOGGLE -)); - -// On mac cmd+` is reserved to cycle between windows, that's why the keybindings use WinCtrl -const category = nls.localize('terminalCategory', "Terminal"); -const actionRegistry = Registry.as(ActionExtensions.WorkbenchActions); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(KillTerminalAction, KillTerminalAction.ID, KillTerminalAction.LABEL), 'Terminal: Kill the Active Terminal Instance', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(CopyTerminalSelectionAction, CopyTerminalSelectionAction.ID, CopyTerminalSelectionAction.LABEL, { - primary: KeyMod.CtrlCmd | KeyCode.KEY_C, - linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_C } -}, ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, KEYBINDING_CONTEXT_TERMINAL_FOCUS)), 'Terminal: Copy Selection', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(CreateNewTerminalAction, CreateNewTerminalAction.ID, CreateNewTerminalAction.LABEL, { - primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_BACKTICK, - mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.US_BACKTICK } -}), 'Terminal: Create New Integrated Terminal', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ClearSelectionTerminalAction, ClearSelectionTerminalAction.ID, ClearSelectionTerminalAction.LABEL, { - primary: KeyCode.Escape, - linux: { primary: KeyCode.Escape } -}, ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_NOT_VISIBLE)), 'Terminal: Escape selection', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(CreateNewInActiveWorkspaceTerminalAction, CreateNewInActiveWorkspaceTerminalAction.ID, CreateNewInActiveWorkspaceTerminalAction.LABEL), 'Terminal: Create New Integrated Terminal (In Active Workspace)', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusActiveTerminalAction, FocusActiveTerminalAction.ID, FocusActiveTerminalAction.LABEL), 'Terminal: Focus Terminal', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusNextTerminalAction, FocusNextTerminalAction.ID, FocusNextTerminalAction.LABEL), 'Terminal: Focus Next Terminal', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusPreviousTerminalAction, FocusPreviousTerminalAction.ID, FocusPreviousTerminalAction.LABEL), 'Terminal: Focus Previous Terminal', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(TerminalPasteAction, TerminalPasteAction.ID, TerminalPasteAction.LABEL, { - primary: KeyMod.CtrlCmd | KeyCode.KEY_V, - linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_V }, - // Don't apply to Mac since cmd+v works - mac: { primary: 0 } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Paste into Active Terminal', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SelectAllTerminalAction, SelectAllTerminalAction.ID, SelectAllTerminalAction.LABEL, { - // Don't use ctrl+a by default as that would override the common go to start - // of prompt shell binding - primary: 0, - // Technically this doesn't need to be here as it will fall back to this - // behavior anyway when handed to xterm.js, having this handled by VS Code - // makes it easier for users to see how it works though. - mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_A } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Select All', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(RunSelectedTextInTerminalAction, RunSelectedTextInTerminalAction.ID, RunSelectedTextInTerminalAction.LABEL), 'Terminal: Run Selected Text In Active Terminal', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(RunActiveFileInTerminalAction, RunActiveFileInTerminalAction.ID, RunActiveFileInTerminalAction.LABEL), 'Terminal: Run Active File In Active Terminal', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleTerminalAction, ToggleTerminalAction.ID, ToggleTerminalAction.LABEL, { - primary: KeyMod.CtrlCmd | KeyCode.US_BACKTICK, - mac: { primary: KeyMod.WinCtrl | KeyCode.US_BACKTICK } -}), 'View: Toggle Integrated Terminal', nls.localize('viewCategory', "View")); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ScrollDownTerminalAction, ScrollDownTerminalAction.ID, ScrollDownTerminalAction.LABEL, { - primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.PageDown, - linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.DownArrow } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Scroll Down (Line)', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ScrollDownPageTerminalAction, ScrollDownPageTerminalAction.ID, ScrollDownPageTerminalAction.LABEL, { - primary: KeyMod.Shift | KeyCode.PageDown, - mac: { primary: KeyCode.PageDown } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Scroll Down (Page)', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ScrollToBottomTerminalAction, ScrollToBottomTerminalAction.ID, ScrollToBottomTerminalAction.LABEL, { - primary: KeyMod.CtrlCmd | KeyCode.End, - linux: { primary: KeyMod.Shift | KeyCode.End } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Scroll to Bottom', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ScrollUpTerminalAction, ScrollUpTerminalAction.ID, ScrollUpTerminalAction.LABEL, { - primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.PageUp, - linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.UpArrow }, -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Scroll Up (Line)', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ScrollUpPageTerminalAction, ScrollUpPageTerminalAction.ID, ScrollUpPageTerminalAction.LABEL, { - primary: KeyMod.Shift | KeyCode.PageUp, - mac: { primary: KeyCode.PageUp } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Scroll Up (Page)', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ScrollToTopTerminalAction, ScrollToTopTerminalAction.ID, ScrollToTopTerminalAction.LABEL, { - primary: KeyMod.CtrlCmd | KeyCode.Home, - linux: { primary: KeyMod.Shift | KeyCode.Home } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Scroll to Top', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ClearTerminalAction, ClearTerminalAction.ID, ClearTerminalAction.LABEL, { - primary: 0, - mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_K } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KeybindingWeight.WorkbenchContrib + 1), 'Terminal: Clear', category); -if (platform.isWindows) { - actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SelectDefaultShellWindowsTerminalAction, SelectDefaultShellWindowsTerminalAction.ID, SelectDefaultShellWindowsTerminalAction.LABEL), 'Terminal: Select Default Shell', category); -} -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(AllowWorkspaceShellTerminalCommand, AllowWorkspaceShellTerminalCommand.ID, AllowWorkspaceShellTerminalCommand.LABEL), 'Terminal: Allow Workspace Shell Configuration', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(DisallowWorkspaceShellTerminalCommand, DisallowWorkspaceShellTerminalCommand.ID, DisallowWorkspaceShellTerminalCommand.LABEL), 'Terminal: Disallow Workspace Shell Configuration', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(RenameTerminalAction, RenameTerminalAction.ID, RenameTerminalAction.LABEL), 'Terminal: Rename', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusTerminalFindWidgetAction, FocusTerminalFindWidgetAction.ID, FocusTerminalFindWidgetAction.LABEL, { - primary: KeyMod.CtrlCmd | KeyCode.KEY_F -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Focus Find Widget', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusTerminalFindWidgetAction, FocusTerminalFindWidgetAction.ID, FocusTerminalFindWidgetAction.LABEL, { - primary: KeyMod.CtrlCmd | KeyCode.KEY_F -}, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Focus Find Widget', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(HideTerminalFindWidgetAction, HideTerminalFindWidgetAction.ID, HideTerminalFindWidgetAction.LABEL, { - primary: KeyCode.Escape, - secondary: [KeyMod.Shift | KeyCode.Escape] -}, ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE)), 'Terminal: Hide Find Widget', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(DeleteWordLeftTerminalAction, DeleteWordLeftTerminalAction.ID, DeleteWordLeftTerminalAction.LABEL, { - primary: KeyMod.CtrlCmd | KeyCode.Backspace, - mac: { primary: KeyMod.Alt | KeyCode.Backspace } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Delete Word Left', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(DeleteWordRightTerminalAction, DeleteWordRightTerminalAction.ID, DeleteWordRightTerminalAction.LABEL, { - primary: KeyMod.CtrlCmd | KeyCode.Delete, - mac: { primary: KeyMod.Alt | KeyCode.Delete } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Delete Word Right', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(DeleteToLineStartTerminalAction, DeleteToLineStartTerminalAction.ID, DeleteToLineStartTerminalAction.LABEL, { - primary: 0, - mac: { primary: KeyMod.CtrlCmd | KeyCode.Backspace } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Delete To Line Start', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(MoveToLineStartTerminalAction, MoveToLineStartTerminalAction.ID, MoveToLineStartTerminalAction.LABEL, { - primary: 0, - mac: { primary: KeyMod.CtrlCmd | KeyCode.LeftArrow } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Move To Line Start', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(MoveToLineEndTerminalAction, MoveToLineEndTerminalAction.ID, MoveToLineEndTerminalAction.LABEL, { - primary: 0, - mac: { primary: KeyMod.CtrlCmd | KeyCode.RightArrow } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Move To Line End', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SplitTerminalAction, SplitTerminalAction.ID, SplitTerminalAction.LABEL, { - primary: KeyMod.CtrlCmd | KeyCode.US_BACKSLASH, - secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_5], - mac: { - primary: KeyMod.CtrlCmd | KeyCode.US_BACKSLASH, - secondary: [KeyMod.WinCtrl | KeyMod.Shift | KeyCode.KEY_5] - } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Split', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SplitInActiveWorkspaceTerminalAction, SplitInActiveWorkspaceTerminalAction.ID, SplitInActiveWorkspaceTerminalAction.LABEL), 'Terminal: Split Terminal (In Active Workspace)', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusPreviousPaneTerminalAction, FocusPreviousPaneTerminalAction.ID, FocusPreviousPaneTerminalAction.LABEL, { - primary: KeyMod.Alt | KeyCode.LeftArrow, - secondary: [KeyMod.Alt | KeyCode.UpArrow], - mac: { - primary: KeyMod.Alt | KeyMod.CtrlCmd | KeyCode.LeftArrow, - secondary: [KeyMod.Alt | KeyMod.CtrlCmd | KeyCode.UpArrow] - } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Focus Previous Pane', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusNextPaneTerminalAction, FocusNextPaneTerminalAction.ID, FocusNextPaneTerminalAction.LABEL, { - primary: KeyMod.Alt | KeyCode.RightArrow, - secondary: [KeyMod.Alt | KeyCode.DownArrow], - mac: { - primary: KeyMod.Alt | KeyMod.CtrlCmd | KeyCode.RightArrow, - secondary: [KeyMod.Alt | KeyMod.CtrlCmd | KeyCode.DownArrow] - } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Focus Next Pane', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ResizePaneLeftTerminalAction, ResizePaneLeftTerminalAction.ID, ResizePaneLeftTerminalAction.LABEL, { - primary: 0, - linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.LeftArrow }, - mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.LeftArrow } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Resize Pane Left', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ResizePaneRightTerminalAction, ResizePaneRightTerminalAction.ID, ResizePaneRightTerminalAction.LABEL, { - primary: 0, - linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.RightArrow }, - mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.RightArrow } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Resize Pane Right', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ResizePaneUpTerminalAction, ResizePaneUpTerminalAction.ID, ResizePaneUpTerminalAction.LABEL, { - primary: 0, - linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.UpArrow }, - mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.UpArrow } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Resize Pane Up', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ResizePaneDownTerminalAction, ResizePaneDownTerminalAction.ID, ResizePaneDownTerminalAction.LABEL, { - primary: 0, - linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.DownArrow }, - mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.DownArrow } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Resize Pane Down', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ScrollToPreviousCommandAction, ScrollToPreviousCommandAction.ID, ScrollToPreviousCommandAction.LABEL, { - primary: 0, - mac: { primary: KeyMod.CtrlCmd | KeyCode.UpArrow } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Scroll To Previous Command', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ScrollToNextCommandAction, ScrollToNextCommandAction.ID, ScrollToNextCommandAction.LABEL, { - primary: 0, - mac: { primary: KeyMod.CtrlCmd | KeyCode.DownArrow } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Scroll To Next Command', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SelectToPreviousCommandAction, SelectToPreviousCommandAction.ID, SelectToPreviousCommandAction.LABEL, { - primary: 0, - mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.UpArrow } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Select To Previous Command', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SelectToNextCommandAction, SelectToNextCommandAction.ID, SelectToNextCommandAction.LABEL, { - primary: 0, - mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.DownArrow } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Select To Next Command', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SelectToPreviousLineAction, SelectToPreviousLineAction.ID, SelectToPreviousLineAction.LABEL), 'Terminal: Select To Previous Line', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SelectToNextLineAction, SelectToNextLineAction.ID, SelectToNextLineAction.LABEL), 'Terminal: Select To Next Line', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleEscapeSequenceLoggingAction, ToggleEscapeSequenceLoggingAction.ID, ToggleEscapeSequenceLoggingAction.LABEL), 'Terminal: Toggle Escape Sequence Logging', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleRegexCommand, ToggleRegexCommand.ID, ToggleRegexCommand.LABEL, { - primary: KeyMod.Alt | KeyCode.KEY_R, - mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_R } -}, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Toggle find by regex'); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleRegexCommand, ToggleRegexCommand.ID_TERMINAL_FOCUS, ToggleRegexCommand.LABEL, { - primary: KeyMod.Alt | KeyCode.KEY_R, - mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_R } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Toggle find by regex', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleWholeWordCommand, ToggleWholeWordCommand.ID, ToggleWholeWordCommand.LABEL, { - primary: KeyMod.Alt | KeyCode.KEY_W, - mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_W } -}, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Toggle find whole word'); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleWholeWordCommand, ToggleWholeWordCommand.ID_TERMINAL_FOCUS, ToggleWholeWordCommand.LABEL, { - primary: KeyMod.Alt | KeyCode.KEY_W, - mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_W } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Toggle find whole word', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleCaseSensitiveCommand, ToggleCaseSensitiveCommand.ID, ToggleCaseSensitiveCommand.LABEL, { - primary: KeyMod.Alt | KeyCode.KEY_C, - mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_C } -}, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Toggle find match case'); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleCaseSensitiveCommand, ToggleCaseSensitiveCommand.ID_TERMINAL_FOCUS, ToggleCaseSensitiveCommand.LABEL, { - primary: KeyMod.Alt | KeyCode.KEY_C, - mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_C } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Toggle find match case', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindNext, FindNext.ID_TERMINAL_FOCUS, FindNext.LABEL, { - primary: KeyCode.F3, - mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_G, secondary: [KeyCode.F3] } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Find next', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindNext, FindNext.ID, FindNext.LABEL, { - primary: KeyCode.F3, - mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_G, secondary: [KeyCode.F3] } -}, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Find next'); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindPrevious, FindPrevious.ID_TERMINAL_FOCUS, FindPrevious.LABEL, { - primary: KeyMod.Shift | KeyCode.F3, - mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_G, secondary: [KeyMod.Shift | KeyCode.F3] }, -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Find previous', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindPrevious, FindPrevious.ID, FindPrevious.LABEL, { - primary: KeyMod.Shift | KeyCode.F3, - mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_G, secondary: [KeyMod.Shift | KeyCode.F3] }, -}, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Find previous'); - - -const sendSequenceTerminalCommand = new SendSequenceTerminalCommand({ - id: SendSequenceTerminalCommand.ID, - precondition: null, - description: { - description: `Send Custom Sequence To Terminal`, - args: [{ - name: 'args', - schema: { - 'type': 'object', - 'required': ['text'], - 'properties': { - 'text': { - 'type': 'string' - } - }, - } - }] - } -}); -sendSequenceTerminalCommand.register(); - -setupTerminalCommands(); -setupTerminalMenu(); - -registerColors(); +registerSingleton(ITerminalInstanceService, TerminalInstanceService, true); diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts new file mode 100644 index 00000000000..d9d128e5f92 --- /dev/null +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts @@ -0,0 +1,57 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { Terminal as XTermTerminal } from 'vscode-xterm'; +import { ITerminalInstance, IWindowsShellHelper, ITerminalConfigHelper, ITerminalProcessManager, IShellLaunchConfig, ITerminalChildProcess } from 'vs/workbench/contrib/terminal/common/terminal'; +import { WindowsShellHelper } from 'vs/workbench/contrib/terminal/node/windowsShellHelper'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { TerminalProcessManager } from 'vs/workbench/contrib/terminal/browser/terminalProcessManager'; +import { IProcessEnvironment } from 'vs/base/common/platform'; +import { TerminalProcess } from 'vs/workbench/contrib/terminal/node/terminalProcess'; + +let Terminal: typeof XTermTerminal; + +/** + * A service used by TerminalInstance (and components owned by it) that allows it to break its + * dependency on electron-browser and node layers, while at the same time avoiding a cyclic + * dependency on ITerminalService. + */ +export class TerminalInstanceService implements ITerminalInstanceService { + public _serviceBrand: any; + + constructor( + @IInstantiationService private readonly _instantiationService: IInstantiationService, + ) { + } + + public async getXtermConstructor(): Promise { + if (!Terminal) { + Terminal = (await import('vscode-xterm')).Terminal; + // Enable xterm.js addons + Terminal.applyAddon(require.__$__nodeRequire('vscode-xterm/lib/addons/search/search')); + Terminal.applyAddon(require.__$__nodeRequire('vscode-xterm/lib/addons/webLinks/webLinks')); + Terminal.applyAddon(require.__$__nodeRequire('vscode-xterm/lib/addons/winptyCompat/winptyCompat')); + // Localize strings + Terminal.strings.blankLine = nls.localize('terminal.integrated.a11yBlankLine', 'Blank line'); + Terminal.strings.promptLabel = nls.localize('terminal.integrated.a11yPromptLabel', 'Terminal input'); + Terminal.strings.tooMuchOutput = nls.localize('terminal.integrated.a11yTooMuchOutput', 'Too much output to announce, navigate to rows manually to read'); + } + return Terminal; + } + + public createWindowsShellHelper(shellProcessId: number, instance: ITerminalInstance, xterm: XTermTerminal): IWindowsShellHelper { + return new WindowsShellHelper(shellProcessId, instance, xterm); + } + + public createTerminalProcessManager(id: number, configHelper: ITerminalConfigHelper): ITerminalProcessManager { + return this._instantiationService.createInstance(TerminalProcessManager, id, configHelper); + } + + public createTerminalProcess(shellLaunchConfig: IShellLaunchConfig, cwd: string, cols: number, rows: number, env: IProcessEnvironment, windowsEnableConpty: boolean): ITerminalChildProcess { + return new TerminalProcess(shellLaunchConfig, cwd, cols, rows, env, windowsEnableConpty); + } +} \ No newline at end of file diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalService.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminalService.ts index fbace8ec6fb..34dc08eec10 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminalService.ts @@ -6,39 +6,31 @@ import * as nls from 'vs/nls'; import * as pfs from 'vs/base/node/pfs'; import * as platform from 'vs/base/common/platform'; -import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IPartService } from 'vs/workbench/services/part/common/partService'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; -import { ITerminalInstance, ITerminalService, IShellLaunchConfig, ITerminalConfigHelper, NEVER_SUGGEST_SELECT_WINDOWS_SHELL_STORAGE_KEY, TERMINAL_PANEL_ID, ITerminalProcessExtHostProxy } from 'vs/workbench/contrib/terminal/common/terminal'; -import { TerminalService as AbstractTerminalService } from 'vs/workbench/contrib/terminal/common/terminalService'; -import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/electron-browser/terminalConfigHelper'; -import Severity from 'vs/base/common/severity'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { getDefaultShell } from 'vs/workbench/contrib/terminal/node/terminal'; -import { TerminalPanel } from 'vs/workbench/contrib/terminal/electron-browser/terminalPanel'; -import { TerminalTab } from 'vs/workbench/contrib/terminal/browser/terminalTab'; +import { ITerminalService, ITerminalConfigHelper } from 'vs/workbench/contrib/terminal/common/terminal'; +import { TerminalService as BrowserTerminalService } from 'vs/workbench/contrib/terminal/browser/terminalService'; +import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { getDefaultShell, linuxDistro, getWindowsBuildNumber } from 'vs/workbench/contrib/terminal/node/terminal'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { ipcRenderer as ipc } from 'electron'; import { IOpenFileRequest, IWindowService } from 'vs/platform/windows/common/windows'; -import { TerminalInstance } from 'vs/workbench/contrib/terminal/electron-browser/terminalInstance'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { URI } from 'vs/base/common/uri'; import { IQuickInputService, IQuickPickItem, IPickOptions } from 'vs/platform/quickinput/common/quickInput'; import { coalesce } from 'vs/base/common/arrays'; +import { IFileService } from 'vs/platform/files/common/files'; +import { escapeNonWindowsPath } from 'vs/workbench/contrib/terminal/common/terminalEnvironment'; +import { execFile } from 'child_process'; -export class TerminalService extends AbstractTerminalService implements ITerminalService { - private _configHelper: TerminalConfigHelper; +export class TerminalService extends BrowserTerminalService implements ITerminalService { public get configHelper(): ITerminalConfigHelper { return this._configHelper; } - protected _terminalTabs: TerminalTab[]; - protected get _terminalInstances(): ITerminalInstance[] { - return this._terminalTabs.reduce((p, c) => p.concat(c.terminalInstances), []); - } - constructor( @IContextKeyService contextKeyService: IContextKeyService, @IPanelService panelService: IPanelService, @@ -46,17 +38,17 @@ export class TerminalService extends AbstractTerminalService implements ITermina @IStorageService storageService: IStorageService, @ILifecycleService lifecycleService: ILifecycleService, @IConfigurationService private readonly _configurationService: IConfigurationService, - @IInstantiationService private readonly _instantiationService: IInstantiationService, + @IInstantiationService instantiationService: IInstantiationService, @IQuickInputService private readonly _quickInputService: IQuickInputService, - @INotificationService private readonly _notificationService: INotificationService, - @IDialogService private readonly _dialogService: IDialogService, - @IExtensionService private readonly _extensionService: IExtensionService, - @IWindowService private readonly _windowService: IWindowService, + @INotificationService notificationService: INotificationService, + @IDialogService dialogService: IDialogService, + @IExtensionService extensionService: IExtensionService, + @IWindowService windowService: IWindowService, + @IFileService fileService: IFileService ) { - super(contextKeyService, panelService, partService, lifecycleService, storageService); + super(contextKeyService, panelService, partService, lifecycleService, storageService, notificationService, dialogService, instantiationService, windowService, extensionService, fileService); - this._terminalTabs = []; - this._configHelper = this._instantiationService.createInstance(TerminalConfigHelper); + this._configHelper = this._instantiationService.createInstance(TerminalConfigHelper, linuxDistro); ipc.on('vscode:openFiles', (_event: any, request: IOpenFileRequest) => { // if the request to open files is coming in from the integrated terminal (identified though // the termProgram variable) and we are instructed to wait for editors close, wait for the @@ -64,7 +56,10 @@ export class TerminalService extends AbstractTerminalService implements ITermina if (request.termProgram === 'vscode' && request.filesToWait) { pfs.whenDeleted(request.filesToWait.waitMarkerFilePath).then(() => { if (this.terminalInstances.length > 0) { - this.getActiveInstance().focus(); + const terminal = this.getActiveInstance(); + if (terminal) { + terminal.focus(); + } } }); } @@ -78,151 +73,18 @@ export class TerminalService extends AbstractTerminalService implements ITermina }); } - public createTerminal(shell: IShellLaunchConfig = {}, wasNewTerminalAction?: boolean): ITerminalInstance { - const terminalTab = this._instantiationService.createInstance(TerminalTab, - this._terminalFocusContextKey, - this._configHelper, - this._terminalContainer, - shell); - this._terminalTabs.push(terminalTab); - const instance = terminalTab.terminalInstances[0]; - terminalTab.addDisposable(terminalTab.onDisposed(this._onTabDisposed.fire, this._onTabDisposed)); - terminalTab.addDisposable(terminalTab.onInstancesChanged(this._onInstancesChanged.fire, this._onInstancesChanged)); - this._initInstanceListeners(instance); - if (this.terminalInstances.length === 1) { - // It's the first instance so it should be made active automatically - this.setActiveInstanceByIndex(0); - } - this._onInstancesChanged.fire(); - this._suggestShellChange(wasNewTerminalAction); - return instance; + protected _getDefaultShell(p: platform.Platform): string { + return getDefaultShell(p); } - public createTerminalRenderer(name: string): ITerminalInstance { - return this.createTerminal({ name, isRendererOnly: true }); - } - - public createInstance(terminalFocusContextKey: IContextKey, configHelper: ITerminalConfigHelper, container: HTMLElement | undefined, shellLaunchConfig: IShellLaunchConfig, doCreateProcess: boolean): ITerminalInstance { - const instance = this._instantiationService.createInstance(TerminalInstance, terminalFocusContextKey, configHelper, container, shellLaunchConfig); - this._onInstanceCreated.fire(instance); - return instance; - } - - public requestExtHostProcess(proxy: ITerminalProcessExtHostProxy, shellLaunchConfig: IShellLaunchConfig, activeWorkspaceRootUri: URI, cols: number, rows: number): void { - // Ensure extension host is ready before requesting a process - this._extensionService.whenInstalledExtensionsRegistered().then(() => { - // TODO: MainThreadTerminalService is not ready at this point, fix this - setTimeout(() => { - this._onInstanceRequestExtHostProcess.fire({ proxy, shellLaunchConfig, activeWorkspaceRootUri, cols, rows }); - }, 500); - }); - } - - public focusFindWidget(): Promise { - return this.showPanel(false).then(() => { - const panel = this._panelService.getActivePanel() as TerminalPanel; - panel.focusFindWidget(); - this._findWidgetVisible.set(true); - }); - } - - public hideFindWidget(): void { - const panel = this._panelService.getActivePanel() as TerminalPanel; - if (panel && panel.getId() === TERMINAL_PANEL_ID) { - panel.hideFindWidget(); - this._findWidgetVisible.reset(); - panel.focus(); - } - } - - public findNext(): void { - const panel = this._panelService.getActivePanel() as TerminalPanel; - if (panel && panel.getId() === TERMINAL_PANEL_ID) { - panel.showFindWidget(); - panel.getFindWidget().find(false); - } - } - - public findPrevious(): void { - const panel = this._panelService.getActivePanel() as TerminalPanel; - if (panel && panel.getId() === TERMINAL_PANEL_ID) { - panel.showFindWidget(); - panel.getFindWidget().find(true); - } - } - - private _suggestShellChange(wasNewTerminalAction?: boolean): void { - // Only suggest on Windows since $SHELL works great for macOS/Linux - if (!platform.isWindows) { - return; - } - - if (this._windowService.getConfiguration().remoteAuthority) { - // Don't suggest if the opened workspace is remote - return; - } - - // Only suggest when the terminal instance is being created by an explicit user action to - // launch a terminal, as opposed to something like tasks, debug, panel restore, etc. - if (!wasNewTerminalAction) { - return; - } - - if (this._windowService.getConfiguration().remoteAuthority) { - // Don't suggest if the opened workspace is remote - return; - } - - // Don't suggest if the user has explicitly opted out - const neverSuggest = this._storageService.getBoolean(NEVER_SUGGEST_SELECT_WINDOWS_SHELL_STORAGE_KEY, StorageScope.GLOBAL, false); - if (neverSuggest) { - return; - } - - // Never suggest if the setting is non-default already (ie. they set the setting manually) - if (this._configHelper.config.shell.windows !== getDefaultShell(platform.Platform.Windows)) { - this._storageService.store(NEVER_SUGGEST_SELECT_WINDOWS_SHELL_STORAGE_KEY, true, StorageScope.GLOBAL); - return; - } - - this._notificationService.prompt( - Severity.Info, - nls.localize('terminal.integrated.chooseWindowsShellInfo', "You can change the default terminal shell by selecting the customize button."), - [{ - label: nls.localize('customize', "Customize"), - run: () => { - this.selectDefaultWindowsShell().then(shell => { - if (!shell) { - return Promise.resolve(null); - } - // Launch a new instance with the newly selected shell - const instance = this.createTerminal({ - executable: shell, - args: this._configHelper.config.shellArgs.windows - }); - if (instance) { - this.setActiveInstance(instance); - } - return Promise.resolve(null); - }); - } - }, - { - label: nls.localize('never again', "Don't Show Again"), - isSecondary: true, - run: () => this._storageService.store(NEVER_SUGGEST_SELECT_WINDOWS_SHELL_STORAGE_KEY, true, StorageScope.GLOBAL) - }] - ); - } - - public selectDefaultWindowsShell(): Promise { + public selectDefaultWindowsShell(): Promise { return this._detectWindowsShells().then(shells => { const options: IPickOptions = { placeHolder: nls.localize('terminal.integrated.chooseWindowsShell', "Select your preferred terminal shell, you can change this later in your settings") }; return this._quickInputService.pick(shells, options).then(value => { if (!value) { - return null; + return undefined; } const shell = value.description; return this._configurationService.updateValue('terminal.integrated.shell.windows', shell, ConfigurationTarget.USER).then(() => shell); @@ -240,7 +102,7 @@ export class TerminalService extends AbstractTerminalService implements ITermina let useWSLexe = false; - if (TerminalInstance.getWindowsBuildNumber() >= 16299) { + if (getWindowsBuildNumber() >= 16299) { useWSLexe = true; } @@ -270,45 +132,22 @@ export class TerminalService extends AbstractTerminalService implements ITermina }); } - private _validateShellPaths(label: string, potentialPaths: string[]): Promise<[string, string]> { - const current = potentialPaths.shift(); - return pfs.fileExists(current).then(exists => { - if (!exists) { - if (potentialPaths.length === 0) { - return null; - } - return this._validateShellPaths(label, potentialPaths); - } - return [label, current] as [string, string]; + protected _getWindowsBuildNumber(): number { + return getWindowsBuildNumber(); + } + + /** + * Converts a path to a path on WSL using the wslpath utility. + * @param path The original path. + */ + protected _getWslPath(path: string): Promise { + if (getWindowsBuildNumber() < 17063) { + throw new Error('wslpath does not exist on Windows build < 17063'); + } + return new Promise(c => { + execFile('bash.exe', ['-c', 'echo $(wslpath ' + escapeNonWindowsPath(path) + ')'], {}, (error, stdout, stderr) => { + c(escapeNonWindowsPath(stdout.trim())); + }); }); } - - public getActiveOrCreateInstance(wasNewTerminalAction?: boolean): ITerminalInstance { - const activeInstance = this.getActiveInstance(); - return activeInstance ? activeInstance : this.createTerminal(undefined, wasNewTerminalAction); - } - - protected _showTerminalCloseConfirmation(): Promise { - let message; - if (this.terminalInstances.length === 1) { - message = nls.localize('terminalService.terminalCloseConfirmationSingular', "There is an active terminal session, do you want to kill it?"); - } else { - message = nls.localize('terminalService.terminalCloseConfirmationPlural', "There are {0} active terminal sessions, do you want to kill them?", this.terminalInstances.length); - } - - return this._dialogService.confirm({ - message, - type: 'warning', - }).then(res => !res.confirmed); - } - - protected _showNotEnoughSpaceToast(): void { - this._notificationService.info(nls.localize('terminal.minWidth', "Not enough space to split terminal.")); - } - - public setContainers(panelContainer: HTMLElement, terminalContainer: HTMLElement): void { - this._configHelper.panelContainer = panelContainer; - this._terminalContainer = terminalContainer; - this._terminalTabs.forEach(tab => tab.attachToElement(this._terminalContainer)); - } } diff --git a/src/vs/workbench/contrib/terminal/node/terminal.ts b/src/vs/workbench/contrib/terminal/node/terminal.ts index 73b41c24e5d..493fad0b833 100644 --- a/src/vs/workbench/contrib/terminal/node/terminal.ts +++ b/src/vs/workbench/contrib/terminal/node/terminal.ts @@ -7,31 +7,7 @@ import * as os from 'os'; import * as platform from 'vs/base/common/platform'; import * as processes from 'vs/base/node/processes'; import { readFile, fileExists } from 'vs/base/node/pfs'; -import { Event } from 'vs/base/common/event'; - -/** - * An interface representing a raw terminal child process, this contains a subset of the - * child_process.ChildProcess node.js interface. - */ -export interface ITerminalChildProcess { - onProcessData: Event; - onProcessExit: Event; - onProcessIdReady: Event; - onProcessTitleChanged: Event; - - /** - * Shutdown the terminal process. - * - * @param immediate When true the process will be killed immediately, otherwise the process will - * be given some time to make sure no additional data comes through. - */ - shutdown(immediate: boolean): void; - input(data: string): void; - resize(cols: number, rows: number): void; - - getInitialCwd(): Promise; - getCwd(): Promise; -} +import { LinuxDistro } from 'vs/workbench/contrib/terminal/common/terminal'; export function getDefaultShell(p: platform.Platform): string { if (p === platform.Platform.Windows) { @@ -78,6 +54,7 @@ function getTerminalDefaultShellWindows(): string { return _TERMINAL_DEFAULT_SHELL_WINDOWS; } +let detectedDistro = LinuxDistro.Unknown; if (platform.isLinux) { const file = '/etc/os-release'; fileExists(file).then(exists => { @@ -87,13 +64,21 @@ if (platform.isLinux) { readFile(file).then(b => { const contents = b.toString(); if (/NAME="?Fedora"?/.test(contents)) { - isFedora = true; + detectedDistro = LinuxDistro.Fedora; } else if (/NAME="?Ubuntu"?/.test(contents)) { - isUbuntu = true; + detectedDistro = LinuxDistro.Ubuntu; } }); }); } -export let isFedora = false; -export let isUbuntu = false; \ No newline at end of file +export const linuxDistro = detectedDistro; + +export function getWindowsBuildNumber(): number { + const osVersion = (/(\d+)\.(\d+)\.(\d+)/g).exec(os.release()); + let buildNumber: number = 0; + if (osVersion && osVersion.length === 4) { + buildNumber = parseInt(osVersion[3]); + } + return buildNumber; +} diff --git a/src/vs/workbench/contrib/terminal/node/terminalProcess.ts b/src/vs/workbench/contrib/terminal/node/terminalProcess.ts index fdadec7eda5..3d40e7ed804 100644 --- a/src/vs/workbench/contrib/terminal/node/terminalProcess.ts +++ b/src/vs/workbench/contrib/terminal/node/terminalProcess.ts @@ -7,10 +7,11 @@ import * as os from 'os'; import * as path from 'vs/base/common/path'; import * as platform from 'vs/base/common/platform'; import * as pty from 'node-pty'; +import * as fs from 'fs'; import { Event, Emitter } from 'vs/base/common/event'; -import { ITerminalChildProcess } from 'vs/workbench/contrib/terminal/node/terminal'; +import { getWindowsBuildNumber } from 'vs/workbench/contrib/terminal/node/terminal'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { IShellLaunchConfig } from 'vs/workbench/contrib/terminal/common/terminal'; +import { IShellLaunchConfig, ITerminalChildProcess } from 'vs/workbench/contrib/terminal/common/terminal'; import { exec } from 'child_process'; export class TerminalProcess implements ITerminalChildProcess, IDisposable { @@ -50,7 +51,7 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable { } this._initialCwd = cwd; - const useConpty = windowsEnableConpty && process.platform === 'win32' && this._getWindowsBuildNumber() >= 18309; + const useConpty = windowsEnableConpty && process.platform === 'win32' && getWindowsBuildNumber() >= 18309; const options: pty.IPtyForkOptions = { name: shellName, cwd, @@ -105,15 +106,6 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable { this._onProcessTitleChanged.dispose(); } - private _getWindowsBuildNumber(): number { - const osVersion = (/(\d+)\.(\d+)\.(\d+)/g).exec(os.release()); - let buildNumber: number = 0; - if (osVersion && osVersion.length === 4) { - buildNumber = parseInt(osVersion[3]); - } - return buildNumber; - } - private _setupTitlePolling() { // Send initial timeout async to give event listeners a chance to init setTimeout(() => { @@ -196,18 +188,29 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable { } public getCwd(): Promise { - if (platform.isWindows) { + if (platform.isMacintosh) { return new Promise(resolve => { - resolve(this._initialCwd); + exec('lsof -p ' + this._ptyProcess.pid + ' | grep cwd', (error, stdout, stderr) => { + if (stdout !== '') { + resolve(stdout.substring(stdout.indexOf('/'), stdout.length - 1)); + } + }); + }); + } + + if (platform.isLinux) { + return new Promise(resolve => { + fs.readlink('/proc/' + this._ptyProcess.pid + '/cwd', (err, linkedstr) => { + if (err) { + resolve(this._initialCwd); + } + resolve(linkedstr); + }); }); } return new Promise(resolve => { - exec('lsof -p ' + this._ptyProcess.pid + ' | grep cwd', (error, stdout, stderr) => { - if (stdout !== '') { - resolve(stdout.substring(stdout.indexOf('/'), stdout.length - 1)); - } - }); + resolve(this._initialCwd); }); } } diff --git a/src/vs/workbench/contrib/terminal/node/windowsShellHelper.ts b/src/vs/workbench/contrib/terminal/node/windowsShellHelper.ts index 80233f87d4d..d841d023695 100644 --- a/src/vs/workbench/contrib/terminal/node/windowsShellHelper.ts +++ b/src/vs/workbench/contrib/terminal/node/windowsShellHelper.ts @@ -5,7 +5,7 @@ import * as platform from 'vs/base/common/platform'; import { Emitter, Event } from 'vs/base/common/event'; -import { ITerminalInstance } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalInstance, IWindowsShellHelper } from 'vs/workbench/contrib/terminal/common/terminal'; import { Terminal as XTermTerminal } from 'vscode-xterm'; import WindowsProcessTreeType = require('windows-process-tree'); @@ -24,7 +24,7 @@ const SHELL_EXECUTABLES = [ let windowsProcessTree: typeof WindowsProcessTreeType; -export class WindowsShellHelper { +export class WindowsShellHelper implements IWindowsShellHelper { private _onCheckShell: Emitter | undefined>; private _isDisposed: boolean; private _currentRequest: Promise | null; diff --git a/src/vs/workbench/contrib/terminal/test/node/terminalCommandTracker.test.ts b/src/vs/workbench/contrib/terminal/test/electron-browser/terminalCommandTracker.test.ts similarity index 99% rename from src/vs/workbench/contrib/terminal/test/node/terminalCommandTracker.test.ts rename to src/vs/workbench/contrib/terminal/test/electron-browser/terminalCommandTracker.test.ts index 324781f164c..ec94ff8a518 100644 --- a/src/vs/workbench/contrib/terminal/test/node/terminalCommandTracker.test.ts +++ b/src/vs/workbench/contrib/terminal/test/electron-browser/terminalCommandTracker.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import { Terminal, TerminalCore } from 'vscode-xterm'; -import { TerminalCommandTracker } from 'vs/workbench/contrib/terminal/node/terminalCommandTracker'; +import { TerminalCommandTracker } from 'vs/workbench/contrib/terminal/browser/terminalCommandTracker'; import { isWindows } from 'vs/base/common/platform'; interface TestTerminalCore extends TerminalCore { diff --git a/src/vs/workbench/contrib/terminal/test/electron-browser/terminalConfigHelper.test.ts b/src/vs/workbench/contrib/terminal/test/electron-browser/terminalConfigHelper.test.ts index 4ee63fee360..e0110601c82 100644 --- a/src/vs/workbench/contrib/terminal/test/electron-browser/terminalConfigHelper.test.ts +++ b/src/vs/workbench/contrib/terminal/test/electron-browser/terminalConfigHelper.test.ts @@ -4,10 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/electron-browser/terminalConfigHelper'; +import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; import { EDITOR_FONT_DEFAULTS } from 'vs/editor/common/config/editorOptions'; -import { isFedora, isUbuntu } from 'vs/workbench/contrib/terminal/node/terminal'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; +import { LinuxDistro } from 'vs/workbench/contrib/terminal/common/terminal'; suite('Workbench - TerminalConfigHelper', () => { let fixture: HTMLElement; @@ -21,22 +21,24 @@ suite('Workbench - TerminalConfigHelper', () => { configurationService.setUserConfiguration('editor', { fontFamily: 'foo' }); configurationService.setUserConfiguration('terminal', { integrated: { fontFamily: 'bar' } }); - let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!); + let configHelper = new TerminalConfigHelper(LinuxDistro.Unknown, configurationService, null!, null!, null!); configHelper.panelContainer = fixture; assert.equal(configHelper.getFont().fontFamily, 'bar', 'terminal.integrated.fontFamily should be selected over editor.fontFamily'); configurationService.setUserConfiguration('terminal', { integrated: { fontFamily: null } }); // Recreate config helper as onDidChangeConfiguration isn't implemented in TestConfigurationService - configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!); + configHelper = new TerminalConfigHelper(LinuxDistro.Fedora, configurationService, null!, null!, null!); configHelper.panelContainer = fixture; - if (isFedora) { - assert.equal(configHelper.getFont().fontFamily, '\'DejaVu Sans Mono\', monospace', 'Fedora should have its font overridden when terminal.integrated.fontFamily not set'); - } else if (isUbuntu) { - assert.equal(configHelper.getFont().fontFamily, '\'Ubuntu Mono\', monospace', 'Ubuntu should have its font overridden when terminal.integrated.fontFamily not set'); - } else { - assert.equal(configHelper.getFont().fontFamily, 'foo', 'editor.fontFamily should be the fallback when terminal.integrated.fontFamily not set'); - } + assert.equal(configHelper.getFont().fontFamily, '\'DejaVu Sans Mono\', monospace', 'Fedora should have its font overridden when terminal.integrated.fontFamily not set'); + + configHelper = new TerminalConfigHelper(LinuxDistro.Ubuntu, configurationService, null!, null!, null!); + configHelper.panelContainer = fixture; + assert.equal(configHelper.getFont().fontFamily, '\'Ubuntu Mono\', monospace', 'Ubuntu should have its font overridden when terminal.integrated.fontFamily not set'); + + configHelper = new TerminalConfigHelper(LinuxDistro.Unknown, configurationService, null!, null!, null!); + configHelper.panelContainer = fixture; + assert.equal(configHelper.getFont().fontFamily, 'foo', 'editor.fontFamily should be the fallback when terminal.integrated.fontFamily not set'); }); test('TerminalConfigHelper - getFont fontSize', function () { @@ -52,7 +54,7 @@ suite('Workbench - TerminalConfigHelper', () => { fontSize: 10 } }); - let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!); + let configHelper = new TerminalConfigHelper(LinuxDistro.Unknown, configurationService, null!, null!, null!); configHelper.panelContainer = fixture; assert.equal(configHelper.getFont().fontSize, 10, 'terminal.integrated.fontSize should be selected over editor.fontSize'); @@ -65,13 +67,14 @@ suite('Workbench - TerminalConfigHelper', () => { fontSize: 0 } }); - configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!); + configHelper = new TerminalConfigHelper(LinuxDistro.Ubuntu, configurationService, null!, null!, null!); configHelper.panelContainer = fixture; - if (isUbuntu) { - assert.equal(configHelper.getFont().fontSize, 8, 'The minimum terminal font size (with adjustment) should be used when terminal.integrated.fontSize less than it'); - } else { - assert.equal(configHelper.getFont().fontSize, 6, 'The minimum terminal font size should be used when terminal.integrated.fontSize less than it'); - } + assert.equal(configHelper.getFont().fontSize, 8, 'The minimum terminal font size (with adjustment) should be used when terminal.integrated.fontSize less than it'); + + configHelper = new TerminalConfigHelper(LinuxDistro.Unknown, configurationService, null!, null!, null!); + configHelper.panelContainer = fixture; + assert.equal(configHelper.getFont().fontSize, 6, 'The minimum terminal font size should be used when terminal.integrated.fontSize less than it'); + configurationService.setUserConfiguration('editor', { fontFamily: 'foo' }); @@ -81,7 +84,7 @@ suite('Workbench - TerminalConfigHelper', () => { fontSize: 1500 } }); - configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!); + configHelper = new TerminalConfigHelper(LinuxDistro.Unknown, configurationService, null!, null!, null!); configHelper.panelContainer = fixture; assert.equal(configHelper.getFont().fontSize, 25, 'The maximum terminal font size should be used when terminal.integrated.fontSize more than it'); @@ -94,13 +97,13 @@ suite('Workbench - TerminalConfigHelper', () => { fontSize: null } }); - configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!); + configHelper = new TerminalConfigHelper(LinuxDistro.Ubuntu, configurationService, null!, null!, null!); configHelper.panelContainer = fixture; - if (isUbuntu) { - assert.equal(configHelper.getFont().fontSize, EDITOR_FONT_DEFAULTS.fontSize + 2, 'The default editor font size (with adjustment) should be used when terminal.integrated.fontSize is not set'); - } else { - assert.equal(configHelper.getFont().fontSize, EDITOR_FONT_DEFAULTS.fontSize, 'The default editor font size should be used when terminal.integrated.fontSize is not set'); - } + assert.equal(configHelper.getFont().fontSize, EDITOR_FONT_DEFAULTS.fontSize + 2, 'The default editor font size (with adjustment) should be used when terminal.integrated.fontSize is not set'); + + configHelper = new TerminalConfigHelper(LinuxDistro.Unknown, configurationService, null!, null!, null!); + configHelper.panelContainer = fixture; + assert.equal(configHelper.getFont().fontSize, EDITOR_FONT_DEFAULTS.fontSize, 'The default editor font size should be used when terminal.integrated.fontSize is not set'); }); test('TerminalConfigHelper - getFont lineHeight', function () { @@ -116,7 +119,7 @@ suite('Workbench - TerminalConfigHelper', () => { lineHeight: 2 } }); - let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!); + let configHelper = new TerminalConfigHelper(LinuxDistro.Unknown, configurationService, null!, null!, null!); configHelper.panelContainer = fixture; assert.equal(configHelper.getFont().lineHeight, 2, 'terminal.integrated.lineHeight should be selected over editor.lineHeight'); @@ -130,7 +133,7 @@ suite('Workbench - TerminalConfigHelper', () => { lineHeight: 0 } }); - configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!); + configHelper = new TerminalConfigHelper(LinuxDistro.Unknown, configurationService, null!, null!, null!); configHelper.panelContainer = fixture; assert.equal(configHelper.getFont().lineHeight, 1, 'editor.lineHeight should be 1 when terminal.integrated.lineHeight not set'); }); @@ -143,7 +146,7 @@ suite('Workbench - TerminalConfigHelper', () => { } }); - let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!); + let configHelper = new TerminalConfigHelper(LinuxDistro.Unknown, configurationService, null!, null!, null!); configHelper.panelContainer = fixture; assert.equal(configHelper.configFontIsMonospace(), true, 'monospace is monospaced'); }); @@ -155,7 +158,7 @@ suite('Workbench - TerminalConfigHelper', () => { fontFamily: 'sans-serif' } }); - let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!); + let configHelper = new TerminalConfigHelper(LinuxDistro.Unknown, configurationService, null!, null!, null!); configHelper.panelContainer = fixture; assert.equal(configHelper.configFontIsMonospace(), false, 'sans-serif is not monospaced'); }); @@ -167,7 +170,7 @@ suite('Workbench - TerminalConfigHelper', () => { fontFamily: 'serif' } }); - let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!); + let configHelper = new TerminalConfigHelper(LinuxDistro.Unknown, configurationService, null!, null!, null!); configHelper.panelContainer = fixture; assert.equal(configHelper.configFontIsMonospace(), false, 'serif is not monospaced'); }); @@ -183,7 +186,7 @@ suite('Workbench - TerminalConfigHelper', () => { } }); - let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!); + let configHelper = new TerminalConfigHelper(LinuxDistro.Unknown, configurationService, null!, null!, null!); configHelper.panelContainer = fixture; assert.equal(configHelper.configFontIsMonospace(), true, 'monospace is monospaced'); }); @@ -199,7 +202,7 @@ suite('Workbench - TerminalConfigHelper', () => { } }); - let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!); + let configHelper = new TerminalConfigHelper(LinuxDistro.Unknown, configurationService, null!, null!, null!); configHelper.panelContainer = fixture; assert.equal(configHelper.configFontIsMonospace(), false, 'sans-serif is not monospaced'); }); @@ -215,7 +218,7 @@ suite('Workbench - TerminalConfigHelper', () => { } }); - let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!); + let configHelper = new TerminalConfigHelper(LinuxDistro.Unknown, configurationService, null!, null!, null!); configHelper.panelContainer = fixture; assert.equal(configHelper.configFontIsMonospace(), false, 'serif is not monospaced'); }); diff --git a/src/vs/workbench/contrib/terminal/test/electron-browser/terminalLinkHandler.test.ts b/src/vs/workbench/contrib/terminal/test/electron-browser/terminalLinkHandler.test.ts index 99edaab4671..5b708d68123 100644 --- a/src/vs/workbench/contrib/terminal/test/electron-browser/terminalLinkHandler.test.ts +++ b/src/vs/workbench/contrib/terminal/test/electron-browser/terminalLinkHandler.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import { Platform } from 'vs/base/common/platform'; -import { TerminalLinkHandler, LineColumnInfo } from 'vs/workbench/contrib/terminal/electron-browser/terminalLinkHandler'; +import { TerminalLinkHandler, LineColumnInfo } from 'vs/workbench/contrib/terminal/browser/terminalLinkHandler'; import * as strings from 'vs/base/common/strings'; import * as path from 'vs/base/common/path'; import * as sinon from 'sinon'; @@ -39,7 +39,7 @@ interface LinkFormatInfo { suite('Workbench - TerminalLinkHandler', () => { suite('localLinkRegex', () => { test('Windows', () => { - const terminalLinkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Windows, null!, null!, null!, null!); + const terminalLinkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Windows, null!, null!, null!, null!, null!); function testLink(link: string, linkUrl: string, lineNo?: string, columnNo?: string) { assert.equal(terminalLinkHandler.extractLinkUrl(link), linkUrl); assert.equal(terminalLinkHandler.extractLinkUrl(`:${link}:`), linkUrl); @@ -111,7 +111,7 @@ suite('Workbench - TerminalLinkHandler', () => { }); test('Linux', () => { - const terminalLinkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Linux, null!, null!, null!, null!); + const terminalLinkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Linux, null!, null!, null!, null!, null!); function testLink(link: string, linkUrl: string, lineNo?: string, columnNo?: string) { assert.equal(terminalLinkHandler.extractLinkUrl(link), linkUrl); assert.equal(terminalLinkHandler.extractLinkUrl(`:${link}:`), linkUrl); @@ -175,7 +175,7 @@ suite('Workbench - TerminalLinkHandler', () => { suite('preprocessPath', () => { test('Windows', () => { - const linkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Windows, null!, null!, null!, null!); + const linkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Windows, null!, null!, null!, null!, null!); linkHandler.processCwd = 'C:\\base'; let stub = sinon.stub(path, 'join', function (arg1: string, arg2: string) { @@ -188,7 +188,7 @@ suite('Workbench - TerminalLinkHandler', () => { stub.restore(); }); test('Windows - spaces', () => { - const linkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Windows, null!, null!, null!, null!); + const linkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Windows, null!, null!, null!, null!, null!); linkHandler.processCwd = 'C:\\base dir'; let stub = sinon.stub(path, 'join', function (arg1: string, arg2: string) { @@ -202,7 +202,7 @@ suite('Workbench - TerminalLinkHandler', () => { }); test('Linux', () => { - const linkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Linux, null!, null!, null!, null!); + const linkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Linux, null!, null!, null!, null!, null!); linkHandler.processCwd = '/base'; let stub = sinon.stub(path, 'join', function (arg1: string, arg2: string) { @@ -216,7 +216,7 @@ suite('Workbench - TerminalLinkHandler', () => { }); test('No Workspace', () => { - const linkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Linux, null!, null!, null!, null!); + const linkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Linux, null!, null!, null!, null!, null!); assert.equal(linkHandler.preprocessPath('./src/file1'), null); assert.equal(linkHandler.preprocessPath('src/file2'), null); @@ -226,7 +226,7 @@ suite('Workbench - TerminalLinkHandler', () => { test('gitDiffLinkRegex', () => { // The platform is irrelevant because the links generated by Git are the same format regardless of platform - const linkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Linux, null!, null!, null!, null!); + const linkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Linux, null!, null!, null!, null!, null!); function assertAreGoodMatches(matches: RegExpMatchArray | null) { if (matches) { diff --git a/src/vs/workbench/contrib/terminal/test/node/terminalEnvironment.test.ts b/src/vs/workbench/contrib/terminal/test/node/terminalEnvironment.test.ts index 0f9ecc1299a..58a8af973ec 100644 --- a/src/vs/workbench/contrib/terminal/test/node/terminalEnvironment.test.ts +++ b/src/vs/workbench/contrib/terminal/test/node/terminalEnvironment.test.ts @@ -4,9 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import * as os from 'os'; import * as platform from 'vs/base/common/platform'; -import * as terminalEnvironment from 'vs/workbench/contrib/terminal/node/terminalEnvironment'; +import * as terminalEnvironment from 'vs/workbench/contrib/terminal/common/terminalEnvironment'; import { URI as Uri } from 'vs/base/common/uri'; import { IStringDictionary } from 'vs/base/common/collections'; @@ -14,21 +13,21 @@ suite('Workbench - TerminalEnvironment', () => { test('addTerminalEnvironmentKeys', () => { const env = { FOO: 'bar' }; const locale = 'en-au'; - terminalEnvironment.addTerminalEnvironmentKeys(env, locale, true); + terminalEnvironment.addTerminalEnvironmentKeys(env, '1.2.3', locale, true); assert.equal(env['TERM_PROGRAM'], 'vscode'); - assert.equal(env['TERM_PROGRAM_VERSION'].search(/^\d+\.\d+\.\d+$/), 0); + assert.equal(env['TERM_PROGRAM_VERSION'], '1.2.3'); assert.equal(env['LANG'], 'en_AU.UTF-8', 'LANG is equal to the requested locale with UTF-8'); const env2 = { FOO: 'bar' }; - terminalEnvironment.addTerminalEnvironmentKeys(env2, undefined, true); + terminalEnvironment.addTerminalEnvironmentKeys(env2, '1.2.3', undefined, true); assert.equal(env2['LANG'], 'en_US.UTF-8', 'LANG is equal to en_US.UTF-8 as fallback.'); // More info on issue #14586 const env3 = { LANG: 'replace' }; - terminalEnvironment.addTerminalEnvironmentKeys(env3, undefined, true); + terminalEnvironment.addTerminalEnvironmentKeys(env3, '1.2.3', undefined, true); assert.equal(env3['LANG'], 'en_US.UTF-8', 'LANG is set to the fallback LANG'); const env4 = { LANG: 'en_US.UTF-8' }; - terminalEnvironment.addTerminalEnvironmentKeys(env3, undefined, true); + terminalEnvironment.addTerminalEnvironmentKeys(env3, '1.2.3', undefined, true); assert.equal(env4['LANG'], 'en_US.UTF-8', 'LANG is equal to the parent environment\'s LANG'); }); @@ -101,42 +100,32 @@ suite('Workbench - TerminalEnvironment', () => { assert.equal(Uri.file(a).fsPath, Uri.file(b).fsPath); } - test('should default to os.homedir() for an empty workspace', () => { - assertPathsMatch(terminalEnvironment.getCwd({ executable: undefined, args: [] }, undefined, undefined), os.homedir()); + test('should default to userHome for an empty workspace', () => { + assertPathsMatch(terminalEnvironment.getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, undefined), '/userHome/'); }); test('should use to the workspace if it exists', () => { - assertPathsMatch(terminalEnvironment.getCwd({ executable: undefined, args: [] }, Uri.file('/foo'), undefined), '/foo'); + assertPathsMatch(terminalEnvironment.getCwd({ executable: undefined, args: [] }, '/userHome/', Uri.file('/foo'), undefined), '/foo'); }); test('should use an absolute custom cwd as is', () => { - assertPathsMatch(terminalEnvironment.getCwd({ executable: undefined, args: [] }, undefined, '/foo'), '/foo'); + assertPathsMatch(terminalEnvironment.getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, '/foo'), '/foo'); }); test('should normalize a relative custom cwd against the workspace path', () => { - assertPathsMatch(terminalEnvironment.getCwd({ executable: undefined, args: [] }, Uri.file('/bar'), 'foo'), '/bar/foo'); - assertPathsMatch(terminalEnvironment.getCwd({ executable: undefined, args: [] }, Uri.file('/bar'), './foo'), '/bar/foo'); - assertPathsMatch(terminalEnvironment.getCwd({ executable: undefined, args: [] }, Uri.file('/bar'), '../foo'), '/foo'); + assertPathsMatch(terminalEnvironment.getCwd({ executable: undefined, args: [] }, '/userHome/', Uri.file('/bar'), 'foo'), '/bar/foo'); + assertPathsMatch(terminalEnvironment.getCwd({ executable: undefined, args: [] }, '/userHome/', Uri.file('/bar'), './foo'), '/bar/foo'); + assertPathsMatch(terminalEnvironment.getCwd({ executable: undefined, args: [] }, '/userHome/', Uri.file('/bar'), '../foo'), '/foo'); }); test('should fall back for relative a custom cwd that doesn\'t have a workspace', () => { - assertPathsMatch(terminalEnvironment.getCwd({ executable: undefined, args: [] }, undefined, 'foo'), os.homedir()); - assertPathsMatch(terminalEnvironment.getCwd({ executable: undefined, args: [] }, undefined, './foo'), os.homedir()); - assertPathsMatch(terminalEnvironment.getCwd({ executable: undefined, args: [] }, undefined, '../foo'), os.homedir()); + assertPathsMatch(terminalEnvironment.getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, 'foo'), '/userHome/'); + assertPathsMatch(terminalEnvironment.getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, './foo'), '/userHome/'); + assertPathsMatch(terminalEnvironment.getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, '../foo'), '/userHome/'); }); test('should ignore custom cwd when told to ignore', () => { - assertPathsMatch(terminalEnvironment.getCwd({ executable: undefined, args: [], ignoreConfigurationCwd: true }, Uri.file('/bar'), '/foo'), '/bar'); + assertPathsMatch(terminalEnvironment.getCwd({ executable: undefined, args: [], ignoreConfigurationCwd: true }, '/userHome/', Uri.file('/bar'), '/foo'), '/bar'); }); }); - - test('preparePathForTerminal', () => { - if (platform.isWindows) { - assert.equal(terminalEnvironment.preparePathForTerminal('C:\\foo'), 'C:\\foo'); - assert.equal(terminalEnvironment.preparePathForTerminal('C:\\foo bar'), '"C:\\foo bar"'); - return; - } - assert.equal(terminalEnvironment.preparePathForTerminal('/a/\\foo bar"\'? ;\'?? :'), '/a/\\\\foo\\ bar\\"\\\'\\?\\ \\;\\\'\\?\\?\\ \\ \\:'); - assert.equal(terminalEnvironment.preparePathForTerminal('/\\\'"?:;!*(){}[]'), '/\\\\\\\'\\"\\?\\:\\;\\!\\*\\(\\)\\{\\}\\[\\]'); - }); }); diff --git a/src/vs/workbench/contrib/themes/browser/themes.contribution.ts b/src/vs/workbench/contrib/themes/browser/themes.contribution.ts index 47a9776b807..10167ea1196 100644 --- a/src/vs/workbench/contrib/themes/browser/themes.contribution.ts +++ b/src/vs/workbench/contrib/themes/browser/themes.contribution.ts @@ -15,11 +15,10 @@ import { VIEWLET_ID, IExtensionsViewlet } from 'vs/workbench/contrib/extensions/ import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { Delayer } from 'vs/base/common/async'; -import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { IColorRegistry, Extensions as ColorRegistryExtensions } from 'vs/platform/theme/common/colorRegistry'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { Color } from 'vs/base/common/color'; -import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; +import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { LIGHT, DARK, HIGH_CONTRAST } from 'vs/platform/theme/common/themeService'; import { colorThemeSchemaId } from 'vs/workbench/services/themes/common/colorThemeSchema'; import { onUnexpectedError } from 'vs/base/common/errors'; @@ -37,7 +36,7 @@ export class SelectColorThemeAction extends Action { @IWorkbenchThemeService private readonly themeService: IWorkbenchThemeService, @IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService, @IViewletService private readonly viewletService: IViewletService, - @IWorkspaceConfigurationService private readonly configurationService: IWorkspaceConfigurationService + @IConfigurationService private readonly configurationService: IConfigurationService ) { super(id, label); } @@ -98,7 +97,7 @@ class SelectIconThemeAction extends Action { @IWorkbenchThemeService private readonly themeService: IWorkbenchThemeService, @IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService, @IViewletService private readonly viewletService: IViewletService, - @IWorkspaceConfigurationService private readonly configurationService: IWorkspaceConfigurationService + @IConfigurationService private readonly configurationService: IConfigurationService ) { super(id, label); diff --git a/src/vs/workbench/contrib/update/electron-browser/update.ts b/src/vs/workbench/contrib/update/electron-browser/update.ts index 02eae23b8f0..8faae3e5068 100644 --- a/src/vs/workbench/contrib/update/electron-browser/update.ts +++ b/src/vs/workbench/contrib/update/electron-browser/update.ts @@ -512,7 +512,7 @@ export class UpdateContribution implements IGlobalActivity { this.storageService.store('update/updateNotificationTime', currentMillis, StorageScope.GLOBAL); } - const updateNotificationMillis = this.storageService.getInteger('update/updateNotificationTime', StorageScope.GLOBAL, currentMillis); + const updateNotificationMillis = this.storageService.getNumber('update/updateNotificationTime', StorageScope.GLOBAL, currentMillis); const diffDays = (currentMillis - updateNotificationMillis) / (1000 * 60 * 60 * 24); return diffDays > 5; diff --git a/src/vs/workbench/contrib/watermark/electron-browser/watermark.css b/src/vs/workbench/contrib/watermark/browser/watermark.css similarity index 100% rename from src/vs/workbench/contrib/watermark/electron-browser/watermark.css rename to src/vs/workbench/contrib/watermark/browser/watermark.css diff --git a/src/vs/workbench/contrib/watermark/electron-browser/watermark.ts b/src/vs/workbench/contrib/watermark/browser/watermark.ts similarity index 94% rename from src/vs/workbench/contrib/watermark/electron-browser/watermark.ts rename to src/vs/workbench/contrib/watermark/browser/watermark.ts index 150f230c22c..b82746b3d30 100644 --- a/src/vs/workbench/contrib/watermark/electron-browser/watermark.ts +++ b/src/vs/workbench/contrib/watermark/browser/watermark.ts @@ -15,7 +15,6 @@ import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/ import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { OpenRecentAction } from 'vs/workbench/electron-browser/actions/windowActions'; import { GlobalNewUntitledFileAction } from 'vs/workbench/contrib/files/browser/fileActions'; import { OpenFolderAction, OpenFileFolderAction, OpenFileAction } from 'vs/workbench/browser/actions/workspaceActions'; import { ShowAllCommandsAction } from 'vs/workbench/contrib/quickopen/browser/commandsHandler'; @@ -26,6 +25,8 @@ import { QUICKOPEN_ACTION_ID } from 'vs/workbench/browser/parts/quickopen/quicko import { TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminalCommands'; import * as dom from 'vs/base/browser/dom'; import { KeybindingLabel } from 'vs/base/browser/ui/keybindingLabel/keybindingLabel'; +import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; const $ = dom.$; @@ -60,7 +61,7 @@ const openFileOrFolderMacOnly: WatermarkEntry = { }; const openRecent: WatermarkEntry = { text: nls.localize('watermark.openRecent', "Open Recent"), - id: OpenRecentAction.ID + id: 'workbench.action.openRecent' }; const newUntitledFile: WatermarkEntry = { text: nls.localize('watermark.newUntitledFile', "New Untitled File"), @@ -112,7 +113,8 @@ export class WatermarkContribution implements IWorkbenchContribution { @IPartService private readonly partService: IPartService, @IKeybindingService private readonly keybindingService: IKeybindingService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, - @IConfigurationService private readonly configurationService: IConfigurationService + @IConfigurationService private readonly configurationService: IConfigurationService, + @IEditorGroupsService private readonly editorGroupsService: IEditorGroupsService ) { this.workbenchState = contextService.getWorkbenchState(); @@ -155,7 +157,8 @@ export class WatermarkContribution implements IWorkbenchContribution { const box = dom.append(this.watermark, $('.watermark-box')); const folder = this.workbenchState !== WorkbenchState.EMPTY; const selected = folder ? folderEntries : noFolderEntries - .filter(entry => !('mac' in entry) || entry.mac === isMacintosh); + .filter(entry => !('mac' in entry) || entry.mac === isMacintosh) + .filter(entry => !!CommandsRegistry.getCommand(entry.id)); const update = () => { dom.clearNode(box); selected.map(entry => { @@ -171,7 +174,7 @@ export class WatermarkContribution implements IWorkbenchContribution { update(); dom.prepend(container.firstElementChild as HTMLElement, this.watermark); this.toDispose.push(this.keybindingService.onDidUpdateKeybindings(update)); - this.toDispose.push(this.partService.onEditorLayout(({ height }: IDimension) => { + this.toDispose.push(this.editorGroupsService.onDidLayout(({ height }: IDimension) => { container.classList[height <= 478 ? 'add' : 'remove']('max-height-478px'); })); } diff --git a/src/vs/workbench/contrib/webview/electron-browser/baseWebviewEditor.ts b/src/vs/workbench/contrib/webview/electron-browser/baseWebviewEditor.ts deleted file mode 100644 index 0851ec06807..00000000000 --- a/src/vs/workbench/contrib/webview/electron-browser/baseWebviewEditor.ts +++ /dev/null @@ -1,98 +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 { Dimension } from 'vs/base/browser/dom'; -import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; -import { WebviewElement } from './webviewElement'; -import { IStorageService } from 'vs/platform/storage/common/storage'; - -/** A context key that is set when the find widget in a webview is visible. */ -export const KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE = new RawContextKey('webviewFindWidgetVisible', false); - - -/** - * This class is only intended to be subclassed and not instantiated. - */ -export abstract class BaseWebviewEditor extends BaseEditor { - - protected _webview: WebviewElement | undefined; - protected findWidgetVisible: IContextKey; - - constructor( - id: string, - telemetryService: ITelemetryService, - themeService: IThemeService, - contextKeyService: IContextKeyService, - storageService: IStorageService - ) { - super(id, telemetryService, themeService, storageService); - if (contextKeyService) { - this.findWidgetVisible = KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE.bindTo(contextKeyService); - } - } - - public showFind() { - if (this._webview) { - this._webview.showFind(); - this.findWidgetVisible.set(true); - } - } - - public hideFind() { - this.findWidgetVisible.reset(); - if (this._webview) { - this._webview.hideFind(); - } - } - - public get isWebviewEditor() { - return true; - } - - public reload() { - this.withWebviewElement(webview => webview.reload()); - } - - public layout(dimension: Dimension): void { - this.withWebviewElement(webview => webview.layout()); - } - - public focus(): void { - this.withWebviewElement(webview => webview.focus()); - } - - public selectAll(): void { - this.withWebviewElement(webview => webview.selectAll()); - } - - public copy(): void { - this.withWebviewElement(webview => webview.copy()); - } - - public paste(): void { - this.withWebviewElement(webview => webview.paste()); - } - - public cut(): void { - this.withWebviewElement(webview => webview.cut()); - } - - public undo(): void { - this.withWebviewElement(webview => webview.undo()); - } - - public redo(): void { - this.withWebviewElement(webview => webview.redo()); - } - - private withWebviewElement(f: (element: WebviewElement) => void): void { - if (this._webview) { - f(this._webview); - } - } -} diff --git a/src/vs/workbench/contrib/webview/electron-browser/webview.contribution.ts b/src/vs/workbench/contrib/webview/electron-browser/webview.contribution.ts index 8e2b9e03bc0..484ff86a0c3 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webview.contribution.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webview.contribution.ts @@ -15,9 +15,8 @@ import { EditorDescriptor, Extensions as EditorExtensions, IEditorRegistry } fro import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; import { Extensions as EditorInputExtensions, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor'; import { WebviewEditorInputFactory } from 'vs/workbench/contrib/webview/electron-browser/webviewEditorInputFactory'; -import { KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE } from './baseWebviewEditor'; import { HideWebViewEditorFindCommand, OpenWebviewDeveloperToolsAction, ReloadWebviewAction, ShowWebViewEditorFindWidgetCommand, SelectAllWebviewEditorCommand, CopyWebviewEditorCommand, PasteWebviewEditorCommand, CutWebviewEditorCommand, UndoWebviewEditorCommand, RedoWebviewEditorCommand } from './webviewCommands'; -import { WebviewEditor } from './webviewEditor'; +import { WebviewEditor, KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE } from './webviewEditor'; import { WebviewEditorInput } from './webviewEditorInput'; import { IWebviewEditorService, WebviewEditorService } from './webviewEditorService'; import { InputFocusedContextKey } from 'vs/platform/contextkey/common/contextkeys'; diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewCommands.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewCommands.ts index b91a3fa1224..ff62950a517 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewCommands.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewCommands.ts @@ -8,7 +8,7 @@ import { Command } from 'vs/editor/browser/editorExtensions'; import * as nls from 'vs/nls'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { BaseWebviewEditor } from './baseWebviewEditor'; +import { WebviewEditor } from 'vs/workbench/contrib/webview/electron-browser/webviewEditor'; export class ShowWebViewEditorFindWidgetCommand extends Command { public static readonly ID = 'editor.action.webvieweditor.showFind'; @@ -143,13 +143,13 @@ export class ReloadWebviewAction extends Action { private getVisibleWebviews() { return this.editorService.visibleControls - .filter(control => control && (control as BaseWebviewEditor).isWebviewEditor) - .map(control => control as BaseWebviewEditor); + .filter(control => control && (control as WebviewEditor).isWebviewEditor) + .map(control => control as WebviewEditor); } } -function getActiveWebviewEditor(accessor: ServicesAccessor): BaseWebviewEditor | null { +function getActiveWebviewEditor(accessor: ServicesAccessor): WebviewEditor | null { const editorService = accessor.get(IEditorService); - const activeControl = editorService.activeControl as BaseWebviewEditor; + const activeControl = editorService.activeControl as WebviewEditor; return activeControl.isWebviewEditor ? activeControl : null; } \ No newline at end of file diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewEditor.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewEditor.ts index b004ebbd6ea..344124376ef 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewEditor.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewEditor.ts @@ -6,24 +6,31 @@ import * as DOM from 'vs/base/browser/dom'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Emitter, Event } from 'vs/base/common/event'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; -import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IWindowService } from 'vs/platform/windows/common/windows'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; import { EditorOptions } from 'vs/workbench/common/editor'; import { WebviewEditorInput } from 'vs/workbench/contrib/webview/electron-browser/webviewEditorInput'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IPartService, Parts } from 'vs/workbench/services/part/common/partService'; -import { BaseWebviewEditor, KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE } from './baseWebviewEditor'; import { WebviewElement } from './webviewElement'; -import { IWindowService } from 'vs/platform/windows/common/windows'; -import { IStorageService } from 'vs/platform/storage/common/storage'; -export class WebviewEditor extends BaseWebviewEditor { +/** A context key that is set when the find widget in a webview is visible. */ +export const KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE = new RawContextKey('webviewFindWidgetVisible', false); + + +export class WebviewEditor extends BaseEditor { + + protected _webview: WebviewElement | undefined; + protected findWidgetVisible: IContextKey; public static readonly ID = 'WebviewEditor'; @@ -50,7 +57,10 @@ export class WebviewEditor extends BaseWebviewEditor { @IWindowService private readonly _windowService: IWindowService, @IStorageService storageService: IStorageService ) { - super(WebviewEditor.ID, telemetryService, themeService, _contextKeyService, storageService); + super(WebviewEditor.ID, telemetryService, themeService, storageService); + if (_contextKeyService) { + this.findWidgetVisible = KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE.bindTo(_contextKeyService); + } } protected createEditor(parent: HTMLElement): void { @@ -73,27 +83,6 @@ export class WebviewEditor extends BaseWebviewEditor { } } - public layout(dimension: DOM.Dimension): void { - if (this._webview) { - this.doUpdateContainer(); - } - super.layout(dimension); - } - - public focus() { - super.focus(); - if (this._onFocusWindowHandler) { - return; - } - - // Make sure we restore focus when switching back to a VS Code window - this._onFocusWindowHandler = this._windowService.onDidChangeFocus(focused => { - if (focused && this._editorService.activeControl === this) { - this.focus(); - } - }); - } - public dispose(): void { this.pendingMessages = []; @@ -122,6 +111,78 @@ export class WebviewEditor extends BaseWebviewEditor { this.pendingMessages.push(data); } } + public showFind() { + if (this._webview) { + this._webview.showFind(); + this.findWidgetVisible.set(true); + } + } + + public hideFind() { + this.findWidgetVisible.reset(); + if (this._webview) { + this._webview.hideFind(); + } + } + + public get isWebviewEditor() { + return true; + } + + public reload() { + this.withWebviewElement(webview => webview.reload()); + } + + public layout(_dimension: DOM.Dimension): void { + this.withWebviewElement(webview => { + this.doUpdateContainer(); + webview.layout(); + }); + } + + public focus(): void { + super.focus(); + if (!this._onFocusWindowHandler) { + + // Make sure we restore focus when switching back to a VS Code window + this._onFocusWindowHandler = this._windowService.onDidChangeFocus(focused => { + if (focused && this._editorService.activeControl === this) { + this.focus(); + } + }); + } + this.withWebviewElement(webview => webview.focus()); + } + + public selectAll(): void { + this.withWebviewElement(webview => webview.selectAll()); + } + + public copy(): void { + this.withWebviewElement(webview => webview.copy()); + } + + public paste(): void { + this.withWebviewElement(webview => webview.paste()); + } + + public cut(): void { + this.withWebviewElement(webview => webview.cut()); + } + + public undo(): void { + this.withWebviewElement(webview => webview.undo()); + } + + public redo(): void { + this.withWebviewElement(webview => webview.redo()); + } + + private withWebviewElement(f: (element: WebviewElement) => void): void { + if (this._webview) { + f(this._webview); + } + } protected setEditorVisible(visible: boolean, group: IEditorGroup): void { if (this.input && this.input instanceof WebviewEditorInput) { diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewEditorInput.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewEditorInput.ts index 5e9c6243e94..3173942bff3 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewEditorInput.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewEditorInput.ts @@ -10,7 +10,7 @@ import { IEditorModel } from 'vs/platform/editor/common/editor'; import { EditorInput, EditorModel, GroupIdentifier, IEditorInput } from 'vs/workbench/common/editor'; import { IPartService, Parts } from 'vs/workbench/services/part/common/partService'; import * as vscode from 'vscode'; -import { WebviewEvents, WebviewInputOptions, WebviewReviver } from './webviewEditorService'; +import { WebviewEvents, WebviewInputOptions } from './webviewEditorService'; import { WebviewElement } from './webviewElement'; export class WebviewEditorInput extends EditorInput { @@ -64,8 +64,6 @@ export class WebviewEditorInput extends EditorInput { private _scrollYPercentage: number = 0; private _state: any; - private _revived: boolean = false; - public readonly extensionLocation: URI | undefined; private readonly _id: number; @@ -77,7 +75,6 @@ export class WebviewEditorInput extends EditorInput { state: any, events: WebviewEvents, extensionLocation: URI | undefined, - public readonly reviver: WebviewReviver | undefined, @IPartService private readonly _partService: IPartService, ) { super(); @@ -213,10 +210,6 @@ export class WebviewEditorInput extends EditorInput { } public resolve(): Promise { - if (this.reviver && !this._revived) { - this._revived = true; - return this.reviver.reviveWebview(this).then(() => new EditorModel()); - } return Promise.resolve(new EditorModel()); } @@ -310,3 +303,30 @@ export class WebviewEditorInput extends EditorInput { this._group = group; } } + + +export class RevivedWebviewEditorInput extends WebviewEditorInput { + private _revived: boolean = false; + + constructor( + viewType: string, + id: number | undefined, + name: string, + options: WebviewInputOptions, + state: any, + events: WebviewEvents, + extensionLocation: URI | undefined, + public readonly reviver: (input: WebviewEditorInput) => Promise, + @IPartService partService: IPartService, + ) { + super(viewType, id, name, options, state, events, extensionLocation, partService); + } + + public async resolve(): Promise { + if (!this._revived) { + this._revived = true; + await this.reviver(this); + } + return super.resolve(); + } +} \ No newline at end of file diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewEditorInputFactory.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewEditorInputFactory.ts index e46bac47814..26247da337e 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewEditorInputFactory.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewEditorInputFactory.ts @@ -5,7 +5,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IEditorInputFactory } from 'vs/workbench/common/editor'; -import { WebviewEditorInput } from './webviewEditorInput'; +import { WebviewEditorInput, RevivedWebviewEditorInput } from './webviewEditorInput'; import { IWebviewEditorService, WebviewInputOptions } from './webviewEditorService'; import { URI, UriComponents } from 'vs/base/common/uri'; @@ -41,7 +41,7 @@ export class WebviewEditorInputFactory implements IEditorInputFactory { } // Only attempt revival if we may have a reviver - if (!this._webviewService.canRevive(input) && !input.reviver) { + if (!this._webviewService.canRevive(input) && !(input instanceof RevivedWebviewEditorInput)) { return null; } diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewEditorService.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewEditorService.ts index abbda87a79e..4fdb9469e84 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewEditorService.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewEditorService.ts @@ -9,9 +9,10 @@ import { IInstantiationService, createDecorator } from 'vs/platform/instantiatio import { IEditorService, ACTIVE_GROUP_TYPE, SIDE_GROUP_TYPE } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupsService, IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; import * as vscode from 'vscode'; -import { WebviewEditorInput } from './webviewEditorInput'; +import { WebviewEditorInput, RevivedWebviewEditorInput } from './webviewEditorInput'; import { GroupIdentifier } from 'vs/workbench/common/editor'; import { equals } from 'vs/base/common/arrays'; +import { values } from 'vs/base/common/map'; export const IWebviewEditorService = createDecorator('webviewEditorService'); @@ -49,7 +50,6 @@ export interface IWebviewEditorService { ): void; registerReviver( - viewType: string, reviver: WebviewReviver ): IDisposable; @@ -90,7 +90,7 @@ export function areWebviewInputOptionsEqual(a: WebviewInputOptions, b: WebviewIn export class WebviewEditorService implements IWebviewEditorService { _serviceBrand: any; - private readonly _revivers = new Map(); + private readonly _revivers = new Set(); private _awaitingRevival: { input: WebviewEditorInput, resolve: (x: any) => void }[] = []; constructor( @@ -133,36 +133,26 @@ export class WebviewEditorService implements IWebviewEditorService { options: WebviewInputOptions, extensionLocation: URI ): WebviewEditorInput { - const webviewInput = this._instantiationService.createInstance(WebviewEditorInput, viewType, id, title, options, state, {}, extensionLocation, { - canRevive: (_webview) => { - return true; - }, - reviveWebview: (webview: WebviewEditorInput): Promise => { - return this.tryRevive(webview).then(didRevive => { - if (didRevive) { - return Promise.resolve(undefined); - } - - // A reviver may not be registered yet. Put into queue and resolve promise when we can revive - let resolve: (value: void) => void; - const promise = new Promise(r => { resolve = r; }); - this._awaitingRevival.push({ input: webview, resolve: resolve! }); - return promise; - }); + const webviewInput = this._instantiationService.createInstance(RevivedWebviewEditorInput, viewType, id, title, options, state, {}, extensionLocation, async (webview: WebviewEditorInput): Promise => { + const didRevive = await this.tryRevive(webview); + if (didRevive) { + return Promise.resolve(undefined); } + + // A reviver may not be registered yet. Put into queue and resolve promise when we can revive + let resolve: () => void; + const promise = new Promise(r => { resolve = r; }); + this._awaitingRevival.push({ input: webview, resolve: resolve! }); + return promise; }); webviewInput.iconPath = iconPath; return webviewInput; } registerReviver( - viewType: string, reviver: WebviewReviver ): IDisposable { - if (this._revivers.has(viewType)) { - throw new Error(`Reviver for ${viewType} already registered`); - } - this._revivers.set(viewType, reviver); + this._revivers.add(reviver); // Resolve any pending views const toRevive = this._awaitingRevival.filter(x => reviver.canRevive(x.input)); @@ -173,27 +163,30 @@ export class WebviewEditorService implements IWebviewEditorService { } return toDisposable(() => { - this._revivers.delete(viewType); + this._revivers.delete(reviver); }); } canRevive( webview: WebviewEditorInput ): boolean { - const viewType = webview.viewType; - const reviver = this._revivers.get(viewType); - return !!reviver && reviver.canRevive(webview); + for (const reviver of values(this._revivers)) { + if (reviver.canRevive(webview)) { + return true; + } + } + return false; } private async tryRevive( webview: WebviewEditorInput ): Promise { - const reviver = this._revivers.get(webview.viewType); - if (!reviver || !reviver.canRevive(webview)) { - return false; + for (const reviver of values(this._revivers)) { + if (reviver.canRevive(webview)) { + await reviver.reviveWebview(webview); + return true; + } } - - await reviver.reviveWebview(webview); - return true; + return false; } } diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts index 5a287448892..70b14883b23 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts @@ -235,7 +235,7 @@ export class WebviewElement extends Disposable { private readonly _options: WebviewOptions, private _contentOptions: WebviewContentOptions, @IInstantiationService instantiationService: IInstantiationService, - @IThemeService private readonly _themeService: IThemeService, + @IThemeService themeService: IThemeService, @IEnvironmentService environmentService: IEnvironmentService, @IFileService fileService: IFileService ) { @@ -347,8 +347,8 @@ export class WebviewElement extends Disposable { this._webviewFindWidget = this._register(instantiationService.createInstance(WebviewFindWidget, this)); } - this.style(this._themeService.getTheme()); - this._register(this._themeService.onThemeChange(this.style, this)); + this.style(themeService.getTheme()); + themeService.onThemeChange(this.style, this, this._toDispose); } public mountTo(parent: HTMLElement) { diff --git a/src/vs/workbench/contrib/welcome/page/electron-browser/vs_code_welcome_page.ts b/src/vs/workbench/contrib/welcome/page/browser/vs_code_welcome_page.ts similarity index 100% rename from src/vs/workbench/contrib/welcome/page/electron-browser/vs_code_welcome_page.ts rename to src/vs/workbench/contrib/welcome/page/browser/vs_code_welcome_page.ts diff --git a/src/vs/workbench/contrib/welcome/page/electron-browser/welcomePage.contribution.ts b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.contribution.ts similarity index 97% rename from src/vs/workbench/contrib/welcome/page/electron-browser/welcomePage.contribution.ts rename to src/vs/workbench/contrib/welcome/page/browser/welcomePage.contribution.ts index 3346ebedbe3..0b8457849d8 100644 --- a/src/vs/workbench/contrib/welcome/page/electron-browser/welcomePage.contribution.ts +++ b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.contribution.ts @@ -6,7 +6,7 @@ import { localize } from 'vs/nls'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { Registry } from 'vs/platform/registry/common/platform'; -import { WelcomePageContribution, WelcomePageAction, WelcomeInputFactory } from 'vs/workbench/contrib/welcome/page/electron-browser/welcomePage'; +import { WelcomePageContribution, WelcomePageAction, WelcomeInputFactory } from 'vs/workbench/contrib/welcome/page/browser/welcomePage'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; diff --git a/src/vs/workbench/contrib/welcome/page/electron-browser/welcomePage.css b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.css similarity index 100% rename from src/vs/workbench/contrib/welcome/page/electron-browser/welcomePage.css rename to src/vs/workbench/contrib/welcome/page/browser/welcomePage.css diff --git a/src/vs/workbench/contrib/welcome/page/electron-browser/welcomePage.ts b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts similarity index 99% rename from src/vs/workbench/contrib/welcome/page/electron-browser/welcomePage.ts rename to src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts index cba26a0edd4..3564664a988 100644 --- a/src/vs/workbench/contrib/welcome/page/electron-browser/welcomePage.ts +++ b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts @@ -9,7 +9,7 @@ import * as strings from 'vs/base/common/strings'; import * as path from 'vs/base/common/path'; import { ICommandService } from 'vs/platform/commands/common/commands'; import * as arrays from 'vs/base/common/arrays'; -import { WalkThroughInput } from 'vs/workbench/contrib/welcome/walkThrough/node/walkThroughInput'; +import { WalkThroughInput } from 'vs/workbench/contrib/welcome/walkThrough/common/walkThroughInput'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -23,15 +23,15 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { Schemas } from 'vs/base/common/network'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; -import { getInstalledExtensions, IExtensionStatus, onExtensionChanged, isKeymapExtension } from 'vs/workbench/contrib/extensions/electron-browser/extensionsUtils'; +import { getInstalledExtensions, IExtensionStatus, onExtensionChanged, isKeymapExtension } from 'vs/workbench/contrib/extensions/common/extensionsUtils'; import { IExtensionEnablementService, IExtensionManagementService, IExtensionGalleryService, IExtensionTipsService, EnablementState, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { used } from 'vs/workbench/contrib/welcome/page/electron-browser/vs_code_welcome_page'; +import { used } from 'vs/workbench/contrib/welcome/page/browser/vs_code_welcome_page'; import { ILifecycleService, StartupKind } from 'vs/platform/lifecycle/common/lifecycle'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { tildify, getBaseLabel } from 'vs/base/common/labels'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { registerColor, focusBorder, textLinkForeground, textLinkActiveForeground, foreground, descriptionForeground, contrastBorder, activeContrastBorder } from 'vs/platform/theme/common/colorRegistry'; -import { getExtraColor } from 'vs/workbench/contrib/welcome/walkThrough/node/walkThroughUtils'; +import { getExtraColor } from 'vs/workbench/contrib/welcome/walkThrough/common/walkThroughUtils'; import { IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions'; import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { IEditorInputFactory, EditorInput } from 'vs/workbench/common/editor'; @@ -274,7 +274,7 @@ class WelcomePage { const resource = URI.parse(require.toUrl('./vs_code_welcome_page')) .with({ scheme: Schemas.walkThrough, - query: JSON.stringify({ moduleId: 'vs/workbench/contrib/welcome/page/electron-browser/vs_code_welcome_page' }) + query: JSON.stringify({ moduleId: 'vs/workbench/contrib/welcome/page/browser/vs_code_welcome_page' }) }); this.editorInput = this.instantiationService.createInstance(WalkThroughInput, { typeId: welcomeInputTypeId, diff --git a/src/vs/workbench/contrib/welcome/walkThrough/electron-browser/editor/editorWalkThrough.ts b/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/editorWalkThrough.ts similarity index 97% rename from src/vs/workbench/contrib/welcome/walkThrough/electron-browser/editor/editorWalkThrough.ts rename to src/vs/workbench/contrib/welcome/walkThrough/browser/editor/editorWalkThrough.ts index 3d69628cdbe..7a95ffda9e4 100644 --- a/src/vs/workbench/contrib/welcome/walkThrough/electron-browser/editor/editorWalkThrough.ts +++ b/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/editorWalkThrough.ts @@ -8,7 +8,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { Action } from 'vs/base/common/actions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { URI } from 'vs/base/common/uri'; -import { WalkThroughInput, WalkThroughInputOptions } from 'vs/workbench/contrib/welcome/walkThrough/node/walkThroughInput'; +import { WalkThroughInput, WalkThroughInputOptions } from 'vs/workbench/contrib/welcome/walkThrough/common/walkThroughInput'; import { Schemas } from 'vs/base/common/network'; import { IEditorInputFactory, EditorInput } from 'vs/workbench/common/editor'; diff --git a/src/vs/workbench/contrib/welcome/walkThrough/electron-browser/editor/vs_code_editor_walkthrough.md b/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/vs_code_editor_walkthrough.md similarity index 100% rename from src/vs/workbench/contrib/welcome/walkThrough/electron-browser/editor/vs_code_editor_walkthrough.md rename to src/vs/workbench/contrib/welcome/walkThrough/browser/editor/vs_code_editor_walkthrough.md diff --git a/src/vs/workbench/contrib/welcome/walkThrough/electron-browser/walkThrough.contribution.ts b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThrough.contribution.ts similarity index 91% rename from src/vs/workbench/contrib/welcome/walkThrough/electron-browser/walkThrough.contribution.ts rename to src/vs/workbench/contrib/welcome/walkThrough/browser/walkThrough.contribution.ts index 8d508626bdb..9eed29a774f 100644 --- a/src/vs/workbench/contrib/welcome/walkThrough/electron-browser/walkThrough.contribution.ts +++ b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThrough.contribution.ts @@ -4,11 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; -import { WalkThroughInput } from 'vs/workbench/contrib/welcome/walkThrough/node/walkThroughInput'; -import { WalkThroughPart } from 'vs/workbench/contrib/welcome/walkThrough/electron-browser/walkThroughPart'; -import { WalkThroughArrowUp, WalkThroughArrowDown, WalkThroughPageUp, WalkThroughPageDown } from 'vs/workbench/contrib/welcome/walkThrough/electron-browser/walkThroughActions'; -import { WalkThroughContentProvider, WalkThroughSnippetContentProvider } from 'vs/workbench/contrib/welcome/walkThrough/node/walkThroughContentProvider'; -import { EditorWalkThroughAction, EditorWalkThroughInputFactory } from 'vs/workbench/contrib/welcome/walkThrough/electron-browser/editor/editorWalkThrough'; +import { WalkThroughInput } from 'vs/workbench/contrib/welcome/walkThrough/common/walkThroughInput'; +import { WalkThroughPart } from 'vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart'; +import { WalkThroughArrowUp, WalkThroughArrowDown, WalkThroughPageUp, WalkThroughPageDown } from 'vs/workbench/contrib/welcome/walkThrough/browser/walkThroughActions'; +import { WalkThroughContentProvider, WalkThroughSnippetContentProvider } from 'vs/workbench/contrib/welcome/walkThrough/common/walkThroughContentProvider'; +import { EditorWalkThroughAction, EditorWalkThroughInputFactory } from 'vs/workbench/contrib/welcome/walkThrough/browser/editor/editorWalkThrough'; import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions as EditorInputExtensions, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; diff --git a/src/vs/workbench/contrib/welcome/walkThrough/electron-browser/walkThroughActions.ts b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughActions.ts similarity index 97% rename from src/vs/workbench/contrib/welcome/walkThrough/electron-browser/walkThroughActions.ts rename to src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughActions.ts index 95ba85eb935..cd4caf83cb7 100644 --- a/src/vs/workbench/contrib/welcome/walkThrough/electron-browser/walkThroughActions.ts +++ b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughActions.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { WalkThroughPart, WALK_THROUGH_FOCUS } from 'vs/workbench/contrib/welcome/walkThrough/electron-browser/walkThroughPart'; +import { WalkThroughPart, WALK_THROUGH_FOCUS } from 'vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart'; import { ICommandAndKeybindingRule, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; diff --git a/src/vs/workbench/contrib/welcome/walkThrough/electron-browser/walkThroughPart.css b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.css similarity index 100% rename from src/vs/workbench/contrib/welcome/walkThrough/electron-browser/walkThroughPart.css rename to src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.css diff --git a/src/vs/workbench/contrib/welcome/walkThrough/electron-browser/walkThroughPart.ts b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts similarity index 99% rename from src/vs/workbench/contrib/welcome/walkThrough/electron-browser/walkThroughPart.ts rename to src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts index 4840870a992..a51b73abb56 100644 --- a/src/vs/workbench/contrib/welcome/walkThrough/electron-browser/walkThroughPart.ts +++ b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts @@ -12,7 +12,7 @@ import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; import { EditorOptions, IEditorMemento } from 'vs/workbench/common/editor'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { WalkThroughInput } from 'vs/workbench/contrib/welcome/walkThrough/node/walkThroughInput'; +import { WalkThroughInput } from 'vs/workbench/contrib/welcome/walkThrough/common/walkThroughInput'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import * as marked from 'vs/base/common/marked/marked'; import { IModelService } from 'vs/editor/common/services/modelService'; @@ -29,7 +29,7 @@ import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { registerColor, focusBorder, textLinkForeground, textLinkActiveForeground, textPreformatForeground, contrastBorder, textBlockQuoteBackground, textBlockQuoteBorder } from 'vs/platform/theme/common/colorRegistry'; -import { getExtraColor } from 'vs/workbench/contrib/welcome/walkThrough/node/walkThroughUtils'; +import { getExtraColor } from 'vs/workbench/contrib/welcome/walkThrough/common/walkThroughUtils'; import { UILabelProvider } from 'vs/base/common/keybindingLabels'; import { OS, OperatingSystem } from 'vs/base/common/platform'; import { deepClone } from 'vs/base/common/objects'; diff --git a/src/vs/workbench/contrib/welcome/walkThrough/node/walkThroughContentProvider.ts b/src/vs/workbench/contrib/welcome/walkThrough/common/walkThroughContentProvider.ts similarity index 100% rename from src/vs/workbench/contrib/welcome/walkThrough/node/walkThroughContentProvider.ts rename to src/vs/workbench/contrib/welcome/walkThrough/common/walkThroughContentProvider.ts diff --git a/src/vs/workbench/contrib/welcome/walkThrough/node/walkThroughInput.ts b/src/vs/workbench/contrib/welcome/walkThrough/common/walkThroughInput.ts similarity index 100% rename from src/vs/workbench/contrib/welcome/walkThrough/node/walkThroughInput.ts rename to src/vs/workbench/contrib/welcome/walkThrough/common/walkThroughInput.ts diff --git a/src/vs/workbench/contrib/welcome/walkThrough/node/walkThroughUtils.ts b/src/vs/workbench/contrib/welcome/walkThrough/common/walkThroughUtils.ts similarity index 100% rename from src/vs/workbench/contrib/welcome/walkThrough/node/walkThroughUtils.ts rename to src/vs/workbench/contrib/welcome/walkThrough/common/walkThroughUtils.ts diff --git a/src/vs/workbench/electron-browser/actions/windowActions.ts b/src/vs/workbench/electron-browser/actions/windowActions.ts index ef640150e1f..ea55154a0ad 100644 --- a/src/vs/workbench/electron-browser/actions/windowActions.ts +++ b/src/vs/workbench/electron-browser/actions/windowActions.ts @@ -10,7 +10,6 @@ import { Action } from 'vs/base/common/actions'; import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows'; import * as nls from 'vs/nls'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { isMacintosh } from 'vs/base/common/platform'; import * as browser from 'vs/base/browser/browser'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; @@ -27,6 +26,7 @@ import { getIconClasses } from 'vs/editor/common/services/getIconClasses'; import product from 'vs/platform/product/node/product'; import { ICommandHandler } from 'vs/platform/commands/common/commands'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; export class CloseCurrentWindowAction extends Action { @@ -82,7 +82,7 @@ export abstract class BaseZoomAction extends Action { constructor( id: string, label: string, - @IWorkspaceConfigurationService private readonly configurationService: IWorkspaceConfigurationService + @IConfigurationService private readonly configurationService: IConfigurationService ) { super(id, label); } @@ -111,7 +111,7 @@ export class ZoomInAction extends BaseZoomAction { constructor( id: string, label: string, - @IWorkspaceConfigurationService configurationService: IWorkspaceConfigurationService + @IConfigurationService configurationService: IConfigurationService ) { super(id, label, configurationService); } @@ -131,7 +131,7 @@ export class ZoomOutAction extends BaseZoomAction { constructor( id: string, label: string, - @IWorkspaceConfigurationService configurationService: IWorkspaceConfigurationService + @IConfigurationService configurationService: IConfigurationService ) { super(id, label, configurationService); } @@ -151,7 +151,7 @@ export class ZoomResetAction extends BaseZoomAction { constructor( id: string, label: string, - @IWorkspaceConfigurationService configurationService: IWorkspaceConfigurationService + @IConfigurationService configurationService: IConfigurationService ) { super(id, label, configurationService); } diff --git a/src/vs/workbench/electron-browser/main.contribution.ts b/src/vs/workbench/electron-browser/main.contribution.ts index 0d9d443dc16..cd5983d7aa7 100644 --- a/src/vs/workbench/electron-browser/main.contribution.ts +++ b/src/vs/workbench/electron-browser/main.contribution.ts @@ -590,12 +590,6 @@ import { LogStorageAction } from 'vs/platform/storage/node/storageService'; 'default': 0, 'description': nls.localize('zoomLevel', "Adjust the zoom level of the window. The original size is 0 and each increment above (e.g. 1) or below (e.g. -1) represents zooming 20% larger or smaller. You can also enter decimals to adjust the zoom level with a finer granularity.") }, - 'window.title': { - 'type': 'string', - 'default': isMacintosh ? '${activeEditorShort}${separator}${rootName}' : '${dirty}${activeEditorShort}${separator}${rootName}${separator}${appName}', - 'markdownDescription': nls.localize({ comment: ['This is the description for a setting. Values surrounded by parenthesis are not to be translated.'], key: 'title' }, - "Controls the window title based on the active editor. Variables are substituted based on the context:\n- `\${activeEditorShort}`: the file name (e.g. myFile.txt).\n- `\${activeEditorMedium}`: the path of the file relative to the workspace folder (e.g. myFolder/myFileFolder/myFile.txt).\n- `\${activeEditorLong}`: the full path of the file (e.g. /Users/Development/myFolder/myFileFolder/myFile.txt).\n- `\${activeFolderShort}`: the name of the folder the file is contained in (e.g. myFileFolder).\n- `\${activeFolderMedium}`: the path of the folder the file is contained in, relative to the workspace folder (e.g. myFolder/myFileFolder).\n- `\${activeFolderLong}`: the full path of the folder the file is contained in (e.g. /Users/Development/myFolder/myFileFolder).\n- `\${folderName}`: name of the workspace folder the file is contained in (e.g. myFolder).\n- `\${folderPath}`: file path of the workspace folder the file is contained in (e.g. /Users/Development/myFolder).\n- `\${rootName}`: name of the workspace (e.g. myFolder or myWorkspace).\n- `\${rootPath}`: file path of the workspace (e.g. /Users/Development/myWorkspace).\n- `\${appName}`: e.g. VS Code.\n- `\${dirty}`: a dirty indicator if the active editor is dirty.\n- `\${separator}`: a conditional separator (\" - \") that only shows when surrounded by variables with values or static text.") - }, 'window.newWindowDimensions': { 'type': 'string', 'enum': ['default', 'inherit', 'maximized', 'fullscreen'], diff --git a/src/vs/workbench/electron-browser/main.ts b/src/vs/workbench/electron-browser/main.ts index 518080ab90d..258e2733a3f 100644 --- a/src/vs/workbench/electron-browser/main.ts +++ b/src/vs/workbench/electron-browser/main.ts @@ -47,7 +47,7 @@ import { GlobalStorageDatabaseChannelClient } from 'vs/platform/storage/node/sto import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IStorageService } from 'vs/platform/storage/common/storage'; -import { InstantiationService } from 'vs/platform/instantiation/node/instantiationService'; +import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; import { Disposable } from 'vs/base/common/lifecycle'; import { registerWindowDriver } from 'vs/platform/driver/electron-browser/driver'; diff --git a/src/vs/workbench/electron-browser/window.ts b/src/vs/workbench/electron-browser/window.ts index acb68c8ad71..0ca278cc10b 100644 --- a/src/vs/workbench/electron-browser/window.ts +++ b/src/vs/workbench/electron-browser/window.ts @@ -14,7 +14,6 @@ import { IFileService } from 'vs/platform/files/common/files'; 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, IRunKeybindingInWindowRequest } from 'vs/platform/windows/common/windows'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { ITitleService } from 'vs/workbench/services/title/common/titleService'; @@ -43,6 +42,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; import { WorkbenchState, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { coalesce } from 'vs/base/common/arrays'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; const TextInputActions: IAction[] = [ new Action('undo', nls.localize('undo', "Undo"), undefined, true, () => Promise.resolve(document.execCommand('undo'))), @@ -75,7 +75,7 @@ export class ElectronWindow extends Disposable { @IEditorService private readonly editorService: EditorServiceImpl, @IWindowsService private readonly windowsService: IWindowsService, @IWindowService private readonly windowService: IWindowService, - @IWorkspaceConfigurationService private readonly configurationService: IWorkspaceConfigurationService, + @IConfigurationService private readonly configurationService: IConfigurationService, @ITitleService private readonly titleService: ITitleService, @IWorkbenchThemeService protected themeService: IWorkbenchThemeService, @INotificationService private readonly notificationService: INotificationService, diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts index 7445d7b6645..39de9310f86 100644 --- a/src/vs/workbench/electron-browser/workbench.ts +++ b/src/vs/workbench/electron-browser/workbench.ts @@ -16,61 +16,47 @@ import { mark } from 'vs/base/common/performance'; import { onUnexpectedError, setUnexpectedErrorHandler } from 'vs/base/common/errors'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { Registry } from 'vs/platform/registry/common/platform'; -import { isWindows, isLinux, isMacintosh, language } from 'vs/base/common/platform'; +import { isWindows, isLinux, isMacintosh } from 'vs/base/common/platform'; import { IResourceInput } from 'vs/platform/editor/common/editor'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; -import { IEditorInputFactoryRegistry, Extensions as EditorExtensions, IUntitledResourceInput, IResourceDiffInput, InEditorZenModeContext } from 'vs/workbench/common/editor'; +import { IEditorInputFactoryRegistry, Extensions as EditorExtensions, IUntitledResourceInput, IResourceDiffInput } from 'vs/workbench/common/editor'; import { ActivitybarPart } from 'vs/workbench/browser/parts/activitybar/activitybarPart'; -import { SidebarPart, SidebarVisibleContext } from 'vs/workbench/browser/parts/sidebar/sidebarPart'; +import { SidebarPart } from 'vs/workbench/browser/parts/sidebar/sidebarPart'; import { PanelPart } from 'vs/workbench/browser/parts/panel/panelPart'; import { StatusbarPart } from 'vs/workbench/browser/parts/statusbar/statusbarPart'; import { TitlebarPart } from 'vs/workbench/browser/parts/titlebar/titlebarPart'; import { EditorPart } from 'vs/workbench/browser/parts/editor/editorPart'; import { IActionBarRegistry, Extensions as ActionBarExtensions } from 'vs/workbench/browser/actions'; import { PanelRegistry, Extensions as PanelExtensions } from 'vs/workbench/browser/panel'; +import { ViewletRegistry, Extensions as ViewletExtensions } from 'vs/workbench/browser/viewlet'; import { QuickOpenController } from 'vs/workbench/browser/parts/quickopen/quickOpenController'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { QuickInputService } from 'vs/workbench/browser/parts/quickinput/quickInput'; import { getServices } from 'vs/platform/instantiation/common/extensions'; -import { Position, Parts, IPartService, IDimension, PositionToString, ILayoutOptions } from 'vs/workbench/services/part/common/partService'; +import { Position, Parts, IPartService, ILayoutOptions } from 'vs/workbench/services/part/common/partService'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IStorageService, StorageScope, IWillSaveStateEvent, WillSaveStateReason } from 'vs/platform/storage/common/storage'; import { ContextMenuService as HTMLContextMenuService } from 'vs/platform/contextview/browser/contextMenuService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing'; import { ContextKeyService } from 'vs/platform/contextkey/browser/contextKeyService'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IActivityService } from 'vs/workbench/services/activity/common/activity'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IFileService } from 'vs/platform/files/common/files'; -import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { ITitleService } from 'vs/workbench/services/title/common/titleService'; import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; -import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { IHistoryService } from 'vs/workbench/services/history/common/history'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; -import { TextFileService } from 'vs/workbench/services/textfile/common/textFileService'; -import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; -import { IProgressService2 } from 'vs/platform/progress/common/progress'; -import { ProgressService2 } from 'vs/workbench/services/progress/browser/progressService2'; -import { TextModelResolverService } from 'vs/workbench/services/textmodelResolver/common/textModelResolverService'; -import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { LifecyclePhase, StartupKind, ILifecycleService, WillShutdownEvent } from 'vs/platform/lifecycle/common/lifecycle'; import { IWindowService, IWindowConfiguration, IPath, MenuBarVisibility, getTitleBarStyle, IWindowsService } from 'vs/platform/windows/common/windows'; import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar'; -import { IMenuService } from 'vs/platform/actions/common/actions'; -import { MenuService } from 'vs/platform/actions/common/menuService'; import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing'; -import { FileDecorationsService } from 'vs/workbench/services/decorations/browser/decorationsService'; -import { IDecorationsService } from 'vs/workbench/services/decorations/browser/decorations'; import { ActivityService } from 'vs/workbench/services/activity/browser/activityService'; -import { IListService, ListService } from 'vs/platform/list/browser/listService'; import { IViewsService } from 'vs/workbench/common/views'; import { ViewsService } from 'vs/workbench/browser/parts/views/views'; import { INotificationService } from 'vs/platform/notification/common/notification'; @@ -80,17 +66,13 @@ import { NotificationsAlerts } from 'vs/workbench/browser/parts/notifications/no import { NotificationsStatus } from 'vs/workbench/browser/parts/notifications/notificationsStatus'; import { registerNotificationCommands } from 'vs/workbench/browser/parts/notifications/notificationsCommands'; import { NotificationsToasts } from 'vs/workbench/browser/parts/notifications/notificationsToasts'; -import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; -import { PreferencesService } from 'vs/workbench/services/preferences/browser/preferencesService'; import { IEditorService, IResourceEditor } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { EditorService } from 'vs/workbench/services/editor/browser/editorService'; import { ContextViewService } from 'vs/platform/contextview/browser/contextViewService'; import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; -import { IFileDialogService, IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { Sizing, Direction, Grid, View } from 'vs/base/browser/ui/grid/grid'; -import { IEditor } from 'vs/editor/common/editorCommon'; -import { WorkbenchLayout } from 'vs/workbench/browser/layout'; +import { WorkbenchLegacyLayout } from 'vs/workbench/browser/legacyLayout'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { setARIAContainer } from 'vs/base/browser/ui/aria/aria'; import { restoreFontInfo, readFontInfo, saveFontInfo } from 'vs/editor/browser/config/configuration'; @@ -100,53 +82,35 @@ import { toErrorMessage } from 'vs/base/common/errorMessage'; import { ILabelService } from 'vs/platform/label/common/label'; import { LabelService } from 'vs/workbench/services/label/common/labelService'; import { ITelemetryServiceConfig, TelemetryService } from 'vs/platform/telemetry/common/telemetryService'; -import { combinedAppender, LogAppender, NullTelemetryService, configurationTelemetry } from 'vs/platform/telemetry/common/telemetryUtils'; -import ErrorTelemetry from 'vs/platform/telemetry/browser/errorTelemetry'; -import { IDownloadService } from 'vs/platform/download/common/download'; +import { combinedAppender, LogAppender, NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; import { IExtensionGalleryService, IExtensionManagementServerService, IExtensionManagementService, IExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { ExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionEnablementService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { CommandService } from 'vs/workbench/services/commands/common/commandService'; -import { IMarkerService } from 'vs/platform/markers/common/markers'; -import { MarkerService } from 'vs/platform/markers/common/markerService'; import { IModeService } from 'vs/editor/common/services/modeService'; import { WorkbenchModeServiceImpl } from 'vs/workbench/services/mode/common/workbenchModeService'; import { ITextResourceConfigurationService, ITextResourcePropertiesService } from 'vs/editor/common/services/resourceConfiguration'; import { TextResourceConfigurationService } from 'vs/editor/common/services/resourceConfigurationImpl'; import { IModelService } from 'vs/editor/common/services/modelService'; import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; -import { IMarkerDecorationsService } from 'vs/editor/common/services/markersDecorationService'; -import { MarkerDecorationsService } from 'vs/editor/common/services/markerDecorationsServiceImpl'; -import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; -import { EditorWorkerServiceImpl } from 'vs/editor/common/services/editorWorkerServiceImpl'; import { IUntitledEditorService, UntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; -import { ISearchService } from 'vs/workbench/services/search/common/search'; -import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { CodeEditorService } from 'vs/workbench/services/editor/browser/codeEditorService'; -import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { OpenerService } from 'vs/editor/browser/services/openerService'; import { ILocalizationsService } from 'vs/platform/localizations/common/localizations'; import { HistoryService } from 'vs/workbench/services/history/browser/history'; -import { ConfigurationResolverService } from 'vs/workbench/services/configurationResolver/browser/configurationResolverService'; import { WorkbenchThemeService } from 'vs/workbench/services/themes/browser/workbenchThemeService'; import { IProductService } from 'vs/platform/product/common/product'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { WorkbenchContextKeysHandler } from 'vs/workbench/browser/contextkeys'; +import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; // import@node -import { BackupFileService, InMemoryBackupFileService } from 'vs/workbench/services/backup/node/backupFileService'; -import { WorkspaceService } from 'vs/workbench/services/configuration/node/configurationService'; -import { JSONEditingService } from 'vs/workbench/services/configuration/node/jsonEditingService'; -import { WorkspaceEditingService } from 'vs/workbench/services/workspace/node/workspaceEditingService'; import { getDelayedChannel } from 'vs/base/parts/ipc/node/ipc'; import { connect as connectNet } from 'vs/base/parts/ipc/node/ipc.net'; import { DialogChannel } from 'vs/platform/dialogs/node/dialogIpc'; import { TelemetryAppenderClient } from 'vs/platform/telemetry/node/telemetryIpc'; import { resolveWorkbenchCommonProperties } from 'vs/platform/telemetry/node/workbenchCommonProperties'; import { IRequestService } from 'vs/platform/request/node/request'; -import { DownloadService } from 'vs/platform/download/node/downloadService'; import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService'; import { IRemoteAgentService } from 'vs/workbench/services/remote/node/remoteAgentService'; import { DownloadServiceChannel } from 'vs/platform/download/node/downloadIpc'; @@ -154,7 +118,6 @@ import { LogLevelSetterChannel } from 'vs/platform/log/node/logIpc'; import { ExtensionManagementChannelClient } from 'vs/platform/extensionManagement/node/extensionManagementIpc'; import { ExtensionManagementServerService } from 'vs/workbench/services/extensions/node/extensionManagementServerService'; import { MultiExtensionManagementService } from 'vs/workbench/services/extensionManagement/node/multiExtensionManagement'; -import { SearchService } from 'vs/workbench/services/search/node/searchService'; import { LocalizationsChannelClient } from 'vs/platform/localizations/node/localizationsIpc'; import { AccessibilityService } from 'vs/platform/accessibility/node/accessibilityService'; import { ProductService } from 'vs/platform/product/node/productService'; @@ -164,75 +127,47 @@ import { RemoteFileService } from 'vs/workbench/services/files/node/remoteFileSe // import@electron-browser import { ContextMenuService as NativeContextMenuService } from 'vs/workbench/services/contextmenu/electron-browser/contextmenuService'; import { WorkbenchKeybindingService } from 'vs/workbench/services/keybinding/electron-browser/keybindingService'; -import { ClipboardService } from 'vs/platform/clipboard/electron-browser/clipboardService'; import { LifecycleService } from 'vs/platform/lifecycle/electron-browser/lifecycleService'; -import { IExtensionUrlHandler, ExtensionUrlHandler } from 'vs/workbench/services/extensions/electron-browser/inactiveExtensionUrlHandler'; -import { DialogService, FileDialogService } from 'vs/workbench/services/dialogs/electron-browser/dialogService'; -import { IBroadcastService, BroadcastService } from 'vs/workbench/services/broadcast/electron-browser/broadcastService'; import { WindowService } from 'vs/platform/windows/electron-browser/windowService'; import { RemoteAuthorityResolverService } from 'vs/platform/remote/electron-browser/remoteAuthorityResolverService'; import { RemoteAgentService } from 'vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl'; import { ExtensionService } from 'vs/workbench/services/extensions/electron-browser/extensionService'; import { RequestService } from 'vs/platform/request/electron-browser/requestService'; -interface IZenModeSettings { - fullScreen: boolean; - centerLayout: boolean; - hideTabs: boolean; - hideActivityBar: boolean; - hideStatusBar: boolean; - hideLineNumbers: boolean; - restore: boolean; +enum Identifiers { + TITLEBAR_PART = 'workbench.parts.titlebar', + ACTIVITYBAR_PART = 'workbench.parts.activitybar', + SIDEBAR_PART = 'workbench.parts.sidebar', + PANEL_PART = 'workbench.parts.panel', + EDITOR_PART = 'workbench.parts.editor', + STATUSBAR_PART = 'workbench.parts.statusbar' } -interface IWorkbenchStartedInfo { - customKeybindingsCount: number; - pinnedViewlets: string[]; - restoredViewlet: string; - restoredEditorsCount: number; -} -type FontAliasingOption = 'default' | 'antialiased' | 'none' | 'auto'; +enum Settings { + MENUBAR_VISIBLE = 'window.menuBarVisibility', + ACTIVITYBAR_VISIBLE = 'workbench.activityBar.visible', + STATUSBAR_VISIBLE = 'workbench.statusBar.visible', -const fontAliasingValues: FontAliasingOption[] = ['antialiased', 'none', 'auto']; + SIDEBAR_POSITION = 'workbench.sideBar.location', + PANEL_POSITION = 'workbench.panel.defaultLocation', -const Identifiers = { - WORKBENCH_CONTAINER: 'workbench.main.container', - TITLEBAR_PART: 'workbench.parts.titlebar', - ACTIVITYBAR_PART: 'workbench.parts.activitybar', - SIDEBAR_PART: 'workbench.parts.sidebar', - PANEL_PART: 'workbench.parts.panel', - EDITOR_PART: 'workbench.parts.editor', - STATUSBAR_PART: 'workbench.parts.statusbar' -}; - -interface IZenMode { - active: boolean; - transitionedToFullScreen: boolean; - transitionedToCenteredEditorLayout: boolean; - transitionDisposeables: IDisposable[]; - wasSideBarVisible: boolean; - wasPanelVisible: boolean; + FONT_ALIASING = 'workbench.fontAliasing', + ZEN_MODE_RESTORE = 'zenMode.restore' } -interface IWorkbenchUIState { - lastPanelHeight?: number; - lastPanelWidth?: number; - lastSidebarDimension?: number; +enum Storage { + SIDEBAR_HIDDEN = 'workbench.sidebar.hidden', + + PANEL_HIDDEN = 'workbench.panel.hidden', + PANEL_POSITION = 'workbench.panel.location', + + ZEN_MODE_ENABLED = 'workbench.zenmode.active', + CENTERED_LAYOUT_ENABLED = 'workbench.centerededitorlayout.active', } export class Workbench extends Disposable implements IPartService { - private static readonly sidebarHiddenStorageKey = 'workbench.sidebar.hidden'; - private static readonly menubarVisibilityConfigurationKey = 'window.menuBarVisibility'; - private static readonly panelHiddenStorageKey = 'workbench.panel.hidden'; - private static readonly zenModeActiveStorageKey = 'workbench.zenmode.active'; - private static readonly centeredEditorLayoutActiveStorageKey = 'workbench.centerededitorlayout.active'; - private static readonly panelPositionStorageKey = 'workbench.panel.location'; - private static readonly defaultPanelPositionStorageKey = 'workbench.panel.defaultLocation'; - private static readonly sidebarPositionConfigurationKey = 'workbench.sideBar.location'; - private static readonly statusbarVisibleConfigurationKey = 'workbench.statusBar.visible'; - private static readonly activityBarVisibleConfigurationKey = 'workbench.activityBar.visible'; - private static readonly fontAliasingConfigurationKey = 'workbench.fontAliasing'; + //#region workbench _serviceBrand: any; @@ -243,28 +178,25 @@ export class Workbench extends Disposable implements IPartService { get onWillShutdown(): Event { return this._onWillShutdown.event; } private previousErrorValue: string; - private previousErrorTime: number = 0; + private previousErrorTime = 0; private workbench: HTMLElement; - private workbenchStarted: boolean; - private workbenchRestored: boolean; - private workbenchShutdown: boolean; + + private restored: boolean; + private disposed: boolean; private editorService: EditorService; private editorGroupService: IEditorGroupsService; private contextViewService: ContextViewService; - private contextKeyService: IContextKeyService; - private keybindingService: IKeybindingService; - private backupFileService: IBackupFileService; - private notificationService: NotificationService; - private themeService: WorkbenchThemeService; - private telemetryService: ITelemetryService; private windowService: IWindowService; - private lifecycleService: LifecycleService; - private fileService: IFileService; - private quickInput: QuickInputService; - private workbenchGrid: Grid | WorkbenchLayout; + private instantiationService: IInstantiationService; + private contextService: IWorkspaceContextService; + private storageService: IStorageService; + private configurationService: IConfigurationService; + private environmentService: IEnvironmentService; + private logService: ILogService; + private windowsService: IWindowsService; private titlebarPart: TitlebarPart; private activitybarPart: ActivitybarPart; @@ -273,57 +205,33 @@ export class Workbench extends Disposable implements IPartService { private editorPart: EditorPart; private statusbarPart: StatusbarPart; - private titlebarPartView: View; - private activitybarPartView: View; - private sidebarPartView: View; - private panelPartView: View; - private editorPartView: View; - private statusbarPartView: View; - private quickOpen: QuickOpenController; + private quickInput: QuickInputService; + private notificationsCenter: NotificationsCenter; private notificationsToasts: NotificationsToasts; - private editorHidden: boolean; - private sideBarHidden: boolean; - private statusBarHidden: boolean; - private activityBarHidden: boolean; - private menubarToggled: boolean; - private sideBarPosition: Position; - private panelPosition: Position; - private panelHidden: boolean; - private menubarVisibility: MenuBarVisibility; - private zenMode: IZenMode; - private fontAliasing: FontAliasingOption; - private hasInitialFilesToOpen: boolean; - private shouldCenterLayout = false; - private uiState: IWorkbenchUIState = { - lastPanelHeight: 350, - lastPanelWidth: 350, - lastSidebarDimension: 300, - }; - - private inZenModeContext: IContextKey; - private sideBarVisibleContext: IContextKey; - constructor( private container: HTMLElement, private configuration: IWindowConfiguration, private serviceCollection: ServiceCollection, - @IInstantiationService private readonly instantiationService: IInstantiationService, - @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, - @IStorageService private readonly storageService: IStorageService, - @IConfigurationService private readonly configurationService: WorkspaceService, - @IEnvironmentService private readonly environmentService: IEnvironmentService, - @ILogService private readonly logService: ILogService, - @IWindowsService private readonly windowsService: IWindowsService + @IInstantiationService instantiationService: IInstantiationService, + @IWorkspaceContextService contextService: IWorkspaceContextService, + @IStorageService storageService: IStorageService, + @IConfigurationService configurationService: IConfigurationService, + @IEnvironmentService environmentService: IEnvironmentService, + @ILogService logService: ILogService, + @IWindowsService windowsService: IWindowsService ) { super(); - this.hasInitialFilesToOpen = !!( - (configuration.filesToCreate && configuration.filesToCreate.length > 0) || - (configuration.filesToOpen && configuration.filesToOpen.length > 0) || - (configuration.filesToDiff && configuration.filesToDiff.length > 0)); + this.instantiationService = instantiationService; + this.contextService = contextService; + this.storageService = storageService; + this.configurationService = configurationService; + this.environmentService = environmentService; + this.logService = logService; + this.windowsService = windowsService; this.registerErrorHandler(); } @@ -369,11 +277,6 @@ export class Workbench extends Disposable implements IPartService { // Log it this.logService.error(errorMsg); - - // Show to user if friendly message provided - if (error && error.friendlyMessage && this.notificationService) { - this.notificationService.error(error.friendlyMessage); - } } startup(): void { @@ -387,7 +290,6 @@ export class Workbench extends Disposable implements IPartService { } private doStartup(): Promise { - this.workbenchStarted = true; // Logging this.logService.trace('workbench configuration', JSON.stringify(this.configuration)); @@ -410,28 +312,27 @@ export class Workbench extends Disposable implements IPartService { // Warm up font cache information before building up too many dom elements restoreFontInfo(this.storageService); readFontInfo(BareFontInfo.createFromRawSettings(this.configurationService.getValue('editor'), getZoomLevel())); - this._register(this.storageService.onWillSaveState(() => { - saveFontInfo(this.storageService); // Keep font info for next startup around - })); // Create Workbench Container - this.createWorkbench(); + this.createWorkbenchContainer(); // Services this.initServices(this.serviceCollection); + // Registries + this.startRegistries(); + // Context Keys this._register(this.instantiationService.createInstance(WorkbenchContextKeysHandler)); - this.inZenModeContext = InEditorZenModeContext.bindTo(this.contextKeyService); - this.sideBarVisibleContext = SidebarVisibleContext.bindTo(this.contextKeyService); // Register Listeners this.registerListeners(); + this.registerLayoutListeners(); - // Settings - this.initSettings(); + // Layout State + this.instantiationService.invokeFunction(accessor => this.initLayoutState(accessor)); - // Create Workbench and Parts + // Render Workbench this.renderWorkbench(); // Workbench Layout @@ -440,17 +341,12 @@ export class Workbench extends Disposable implements IPartService { // Layout this.layout(); - // Handle case where workbench is not starting up properly - const timeoutHandle = setTimeout(() => this.logService.warn('Workbench did not finish loading in 10 seconds, that might be a problem that should be reported.'), 10000); - this.lifecycleService.when(LifecyclePhase.Restored).then(() => clearTimeout(timeoutHandle)); - - // Restore Parts - return this.restoreParts(); + // Restore + return this.restoreWorkbench(); } - private createWorkbench(): void { + private createWorkbenchContainer(): void { this.workbench = document.createElement('div'); - this.workbench.id = Identifiers.WORKBENCH_CONTAINER; const platformClass = isWindows ? 'windows' : isLinux ? 'linux' : 'mac'; @@ -466,15 +362,8 @@ export class Workbench extends Disposable implements IPartService { // Labels serviceCollection.set(ILabelService, new SyncDescriptor(LabelService, undefined, true)); - // Clipboard - serviceCollection.set(IClipboardService, new SyncDescriptor(ClipboardService, undefined, true)); - - // Broadcast - serviceCollection.set(IBroadcastService, new SyncDescriptor(BroadcastService, [this.configuration.windowId], true)); - // Notifications - this.notificationService = new NotificationService(); - serviceCollection.set(INotificationService, this.notificationService); + serviceCollection.set(INotificationService, new SyncDescriptor(NotificationService, undefined, true)); // Window this.windowService = this.instantiationService.createInstance(WindowService, this.configuration); @@ -494,6 +383,7 @@ export class Workbench extends Disposable implements IPartService { }); // Telemetry + let telemetryService: ITelemetryService; if (!this.environmentService.isExtensionDevelopment && !this.environmentService.args['disable-telemetry'] && !!productService.enableTelemetry) { const channel = getDelayedChannel(sharedProcess.then(c => c.getChannel('telemetryAppender'))); const config: ITelemetryServiceConfig = { @@ -502,24 +392,18 @@ export class Workbench extends Disposable implements IPartService { piiPaths: [this.environmentService.appRoot, this.environmentService.extensionsPath] }; - this.telemetryService = this._register(this.instantiationService.createInstance(TelemetryService, config)); - this._register(new ErrorTelemetry(this.telemetryService)); + telemetryService = this._register(this.instantiationService.createInstance(TelemetryService, config)); } else { - this.telemetryService = NullTelemetryService; + telemetryService = NullTelemetryService; } - serviceCollection.set(ITelemetryService, this.telemetryService); - this._register(configurationTelemetry(this.telemetryService, this.configurationService)); - - // Dialogs - serviceCollection.set(IDialogService, new SyncDescriptor(DialogService, undefined, true)); + serviceCollection.set(ITelemetryService, telemetryService); // Lifecycle - this.lifecycleService = this.instantiationService.createInstance(LifecycleService); - serviceCollection.set(ILifecycleService, this.lifecycleService); - - this._register(this.lifecycleService.onWillShutdown(event => this._onWillShutdown.fire(event))); - this._register(this.lifecycleService.onShutdown(() => { + const lifecycleService = this.instantiationService.createInstance(LifecycleService); + serviceCollection.set(ILifecycleService, lifecycleService); + this._register(lifecycleService.onWillShutdown(event => this._onWillShutdown.fire(event))); + this._register(lifecycleService.onShutdown(() => { this._onShutdown.fire(); this.dispose(); })); @@ -527,26 +411,14 @@ export class Workbench extends Disposable implements IPartService { // Request Service serviceCollection.set(IRequestService, new SyncDescriptor(RequestService, undefined, true)); - // Download Service - serviceCollection.set(IDownloadService, new SyncDescriptor(DownloadService, undefined, true)); - // Extension Gallery serviceCollection.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService, undefined, true)); // Remote Resolver - const remoteAuthorityResolverService = new RemoteAuthorityResolverService(); - serviceCollection.set(IRemoteAuthorityResolverService, remoteAuthorityResolverService); + serviceCollection.set(IRemoteAuthorityResolverService, new SyncDescriptor(RemoteAuthorityResolverService, undefined, true)); // Remote Agent - const remoteAgentService = new RemoteAgentService(this.configuration, this.notificationService, this.environmentService, remoteAuthorityResolverService); - serviceCollection.set(IRemoteAgentService, remoteAgentService); - - const remoteAgentConnection = remoteAgentService.getConnection(); - if (remoteAgentConnection) { - remoteAgentConnection.registerChannel('dialog', this.instantiationService.createInstance(DialogChannel)); - remoteAgentConnection.registerChannel('download', new DownloadServiceChannel()); - remoteAgentConnection.registerChannel('loglevel', new LogLevelSetterChannel(this.logService)); - } + serviceCollection.set(IRemoteAgentService, new SyncDescriptor(RemoteAgentService, [this.configuration])); // Extensions Management const extensionManagementChannel = getDelayedChannel(sharedProcess.then(c => c.getChannel('extensions'))); @@ -558,18 +430,14 @@ export class Workbench extends Disposable implements IPartService { serviceCollection.set(IExtensionEnablementService, new SyncDescriptor(ExtensionEnablementService, undefined, true)); // Extensions - serviceCollection.set(IExtensionService, this.instantiationService.createInstance(ExtensionService)); + serviceCollection.set(IExtensionService, new SyncDescriptor(ExtensionService)); // Theming - this.themeService = this.instantiationService.createInstance(WorkbenchThemeService, document.body); - serviceCollection.set(IWorkbenchThemeService, this.themeService); + serviceCollection.set(IWorkbenchThemeService, new SyncDescriptor(WorkbenchThemeService, [document.body])); // Commands serviceCollection.set(ICommandService, new SyncDescriptor(CommandService, undefined, true)); - // Markers - serviceCollection.set(IMarkerService, new SyncDescriptor(MarkerService, undefined, true)); - // Editor Mode serviceCollection.set(IModeService, new SyncDescriptor(WorkbenchModeServiceImpl)); @@ -582,63 +450,34 @@ export class Workbench extends Disposable implements IPartService { // Editor Models serviceCollection.set(IModelService, new SyncDescriptor(ModelServiceImpl, undefined, true)); - // Marker Decorations - serviceCollection.set(IMarkerDecorationsService, new SyncDescriptor(MarkerDecorationsService)); - - // Editor Worker - serviceCollection.set(IEditorWorkerService, new SyncDescriptor(EditorWorkerServiceImpl)); - // Untitled Editors serviceCollection.set(IUntitledEditorService, new SyncDescriptor(UntitledEditorService, undefined, true)); - // Search - serviceCollection.set(ISearchService, new SyncDescriptor(SearchService)); - - // Code Editor - serviceCollection.set(ICodeEditorService, new SyncDescriptor(CodeEditorService, undefined, true)); - - // Opener - serviceCollection.set(IOpenerService, new SyncDescriptor(OpenerService, undefined, true)); - // Localization const localizationsChannel = getDelayedChannel(sharedProcess.then(c => c.getChannel('localizations'))); serviceCollection.set(ILocalizationsService, new SyncDescriptor(LocalizationsChannelClient, [localizationsChannel])); - // Hash - // serviceCollection.set(IHashService, new SyncDescriptor(HashService, undefined, true)); - // Status bar this.statusbarPart = this.instantiationService.createInstance(StatusbarPart, Identifiers.STATUSBAR_PART); serviceCollection.set(IStatusbarService, this.statusbarPart); - // Progress 2 - serviceCollection.set(IProgressService2, new SyncDescriptor(ProgressService2)); - // Context Keys - this.contextKeyService = this.instantiationService.createInstance(ContextKeyService); - serviceCollection.set(IContextKeyService, this.contextKeyService); + serviceCollection.set(IContextKeyService, new SyncDescriptor(ContextKeyService)); // Keybindings - this.keybindingService = this.instantiationService.createInstance(WorkbenchKeybindingService, window); - serviceCollection.set(IKeybindingService, this.keybindingService); - - // List - serviceCollection.set(IListService, this.instantiationService.createInstance(ListService)); + serviceCollection.set(IKeybindingService, new SyncDescriptor(WorkbenchKeybindingService, [window])); // Context view service this.contextViewService = this.instantiationService.createInstance(ContextViewService, this.workbench); serviceCollection.set(IContextViewService, this.contextViewService); // Use themable context menus when custom titlebar is enabled to match custom menubar - if (!isMacintosh && this.useCustomTitleBarStyle()) { + if (!isMacintosh && getTitleBarStyle(this.configurationService, this.environmentService) === 'custom') { serviceCollection.set(IContextMenuService, new SyncDescriptor(HTMLContextMenuService, [null])); } else { serviceCollection.set(IContextMenuService, new SyncDescriptor(NativeContextMenuService)); } - // Menus/Actions - serviceCollection.set(IMenuService, new SyncDescriptor(MenuService, undefined, true)); - // Sidebar part this.sidebarPart = this.instantiationService.createInstance(SidebarPart, Identifiers.SIDEBAR_PART); @@ -657,13 +496,10 @@ export class Workbench extends Disposable implements IPartService { serviceCollection.set(IActivityService, new SyncDescriptor(ActivityService, [this.activitybarPart, this.panelPart], true)); // File Service - this.fileService = this.instantiationService.createInstance(RemoteFileService); - serviceCollection.set(IFileService, this.fileService); - this.configurationService.acquireFileService(this.fileService); - this.themeService.acquireFileService(this.fileService); + serviceCollection.set(IFileService, new SyncDescriptor(RemoteFileService)); // Editor and Group services - this.editorPart = this.instantiationService.createInstance(EditorPart, Identifiers.EDITOR_PART, !this.hasInitialFilesToOpen); + this.editorPart = this.instantiationService.createInstance(EditorPart, Identifiers.EDITOR_PART, !this.hasInitialFilesToOpen()); this.editorGroupService = this.editorPart; serviceCollection.set(IEditorGroupsService, this.editorPart); this.editorService = this.instantiationService.createInstance(EditorService); @@ -679,38 +515,6 @@ export class Workbench extends Disposable implements IPartService { // History serviceCollection.set(IHistoryService, new SyncDescriptor(HistoryService)); - // File Dialogs - serviceCollection.set(IFileDialogService, new SyncDescriptor(FileDialogService, undefined, true)); - - // Backup File Service - if (this.configuration.backupPath) { - this.backupFileService = this.instantiationService.createInstance(BackupFileService, this.configuration.backupPath); - } else { - this.backupFileService = new InMemoryBackupFileService(); - } - serviceCollection.set(IBackupFileService, this.backupFileService); - - // Text File Service - serviceCollection.set(ITextFileService, new SyncDescriptor(TextFileService)); - - // File Decorations - serviceCollection.set(IDecorationsService, new SyncDescriptor(FileDecorationsService)); - - // Inactive extension URL handler - serviceCollection.set(IExtensionUrlHandler, new SyncDescriptor(ExtensionUrlHandler)); - - // Text Model Resolver Service - serviceCollection.set(ITextModelService, new SyncDescriptor(TextModelResolverService, undefined, true)); - - // JSON Editing - serviceCollection.set(IJSONEditingService, new SyncDescriptor(JSONEditingService, undefined, true)); - - // Workspace Editing - serviceCollection.set(IWorkspaceEditingService, new SyncDescriptor(WorkspaceEditingService, undefined, true)); - - // Configuration Resolver - serviceCollection.set(IConfigurationResolverService, new SyncDescriptor(ConfigurationResolverService, [process.env], true)); - // Quick open service (quick open controller) this.quickOpen = this.instantiationService.createInstance(QuickOpenController); serviceCollection.set(IQuickOpenService, this.quickOpen); @@ -719,122 +523,205 @@ export class Workbench extends Disposable implements IPartService { this.quickInput = this.instantiationService.createInstance(QuickInputService); serviceCollection.set(IQuickInputService, this.quickInput); - // PreferencesService - serviceCollection.set(IPreferencesService, this.instantiationService.createInstance(PreferencesService)); - // Contributed services const contributedServices = getServices(); for (let contributedService of contributedServices) { serviceCollection.set(contributedService.id, contributedService.descriptor); } - // Set the some services to registries that have been created eagerly - Registry.as(ActionBarExtensions.Actionbar).setInstantiationService(this.instantiationService); - Registry.as(WorkbenchExtensions.Workbench).start(this.instantiationService, this.lifecycleService); - Registry.as(EditorExtensions.EditorInputFactories).setInstantiationService(this.instantiationService); + // TODO@Alex TODO@Sandeep this should move somewhere else + this.instantiationService.invokeFunction(accessor => { + const remoteAgentConnection = accessor.get(IRemoteAgentService).getConnection(); + if (remoteAgentConnection) { + remoteAgentConnection.registerChannel('dialog', this.instantiationService.createInstance(DialogChannel)); + remoteAgentConnection.registerChannel('download', new DownloadServiceChannel()); + remoteAgentConnection.registerChannel('loglevel', new LogLevelSetterChannel(this.logService)); + } + }); - // TODO@Sandeep debt around cyclic dependencies - this.configurationService.acquireInstantiationService(this.instantiationService); + // TODO@Sandeep TODO@Martin debt around cyclic dependencies + this.instantiationService.invokeFunction(accessor => { + const fileService = accessor.get(IFileService); + const instantiationService = accessor.get(IInstantiationService); + const configurationService = accessor.get(IConfigurationService) as any; + const themeService = accessor.get(IWorkbenchThemeService) as any; + + if (typeof configurationService.acquireFileService === 'function') { + configurationService.acquireFileService(fileService); + } + + if (typeof configurationService.acquireInstantiationService === 'function') { + configurationService.acquireInstantiationService(instantiationService); + } + + if (typeof themeService.acquireFileService === 'function') { + themeService.acquireFileService(fileService); + } + }); } - //#region event handling + private startRegistries(): void { + this.instantiationService.invokeFunction(accessor => { + Registry.as(ActionBarExtensions.Actionbar).start(accessor); + Registry.as(WorkbenchExtensions.Workbench).start(accessor); + Registry.as(EditorExtensions.EditorInputFactories).start(accessor); + }); + } + + private hasInitialFilesToOpen(): boolean { + return !!( + (this.configuration.filesToCreate && this.configuration.filesToCreate.length > 0) || + (this.configuration.filesToOpen && this.configuration.filesToOpen.length > 0) || + (this.configuration.filesToDiff && this.configuration.filesToDiff.length > 0)); + } private registerListeners(): void { // Storage this._register(this.storageService.onWillSaveState(e => this.saveState(e))); - // Restore editor if hidden and it changes - this._register(this.editorService.onDidVisibleEditorsChange(() => this.restoreHiddenEditor())); - this._register(this.editorPart.onDidActivateGroup(() => this.restoreHiddenEditor())); - // Configuration changes - this._register(this.configurationService.onDidChangeConfiguration(() => this.onDidUpdateConfiguration())); - - // Fullscreen changes - this._register(onDidChangeFullscreen(() => this.onFullscreenChanged())); - - // Group changes - this._register(this.editorGroupService.onDidAddGroup(() => this.centerEditorLayout(this.shouldCenterLayout))); - this._register(this.editorGroupService.onDidRemoveGroup(() => this.centerEditorLayout(this.shouldCenterLayout))); - - // Prevent workbench from scrolling #55456 - this._register(addDisposableListener(this.workbench, EventType.SCROLL, () => this.workbench.scrollTop = 0)); + this._register(this.configurationService.onDidChangeConfiguration(() => this.setFontAliasing())); } - private onFullscreenChanged(): void { + private fontAliasing: 'default' | 'antialiased' | 'none' | 'auto'; + private setFontAliasing() { + const aliasing = this.configurationService.getValue<'default' | 'antialiased' | 'none' | 'auto'>(Settings.FONT_ALIASING); + if (this.fontAliasing === aliasing) { + return; + } - // Apply as CSS class - if (isFullscreen()) { + this.fontAliasing = aliasing; + + // Remove all + const fontAliasingValues: (typeof aliasing)[] = ['antialiased', 'none', 'auto']; + removeClasses(this.workbench, ...fontAliasingValues.map(value => `monaco-font-aliasing-${value}`)); + + // Add specific + if (fontAliasingValues.some(option => option === aliasing)) { + addClass(this.workbench, `monaco-font-aliasing-${aliasing}`); + } + } + + private renderWorkbench(): void { + if (this.state.sideBar.hidden) { + addClass(this.workbench, 'nosidebar'); + } + + if (this.state.panel.hidden) { + addClass(this.workbench, 'nopanel'); + } + + if (this.state.statusBar.hidden) { + addClass(this.workbench, 'nostatusbar'); + } + + if (this.state.fullscreen) { addClass(this.workbench, 'fullscreen'); - } else { - removeClass(this.workbench, 'fullscreen'); - - if (this.zenMode.transitionedToFullScreen && this.zenMode.active) { - this.toggleZenMode(); - } } - // Changing fullscreen state of the window has an impact on custom title bar visibility, so we need to update - if (this.useCustomTitleBarStyle()) { - this._onTitleBarVisibilityChange.fire(); - this.layout(); // handle title bar when fullscreen changes - } + // Apply font aliasing + this.setFontAliasing(); + + // Create Parts + this.createTitlebarPart(); + this.createActivityBarPart(); + this.createSidebarPart(); + this.createEditorPart(); + this.createPanelPart(); + this.createStatusbarPart(); + + // Notification Handlers + this.instantiationService.invokeFunction(accessor => this.createNotificationsHandlers(accessor)); + + // Add Workbench to DOM + this.container.appendChild(this.workbench); } - private onMenubarToggled(visible: boolean) { - if (visible !== this.menubarToggled) { - this.menubarToggled = visible; + private createTitlebarPart(): void { + const titlebarContainer = this.createPart(Identifiers.TITLEBAR_PART, 'contentinfo', 'titlebar'); - if (isFullscreen() && (this.menubarVisibility === 'toggle' || this.menubarVisibility === 'default')) { - this._onTitleBarVisibilityChange.fire(); - this.layout(); - } - } + this.titlebarPart.create(titlebarContainer); } - private restoreHiddenEditor(): void { - if (this.editorHidden) { - this.setEditorHidden(false); - } + private createActivityBarPart(): void { + const activitybarPartContainer = this.createPart(Identifiers.ACTIVITYBAR_PART, 'navigation', 'activitybar', this.state.sideBar.position === Position.LEFT ? 'left' : 'right'); + + this.activitybarPart.create(activitybarPartContainer); } - private onDidUpdateConfiguration(skipLayout?: boolean): void { - const newSidebarPositionValue = this.configurationService.getValue(Workbench.sidebarPositionConfigurationKey); - const newSidebarPosition = (newSidebarPositionValue === 'right') ? Position.RIGHT : Position.LEFT; - if (newSidebarPosition !== this.getSideBarPosition()) { - this.setSideBarPosition(newSidebarPosition); - } + private createSidebarPart(): void { + const sidebarPartContainer = this.createPart(Identifiers.SIDEBAR_PART, 'complementary', 'sidebar', this.state.sideBar.position === Position.LEFT ? 'left' : 'right'); - this.setPanelPositionFromStorageOrConfig(); - - const fontAliasing = this.configurationService.getValue(Workbench.fontAliasingConfigurationKey); - if (fontAliasing !== this.fontAliasing) { - this.setFontAliasing(fontAliasing); - } - - if (!this.zenMode.active) { - const newStatusbarHiddenValue = !this.configurationService.getValue(Workbench.statusbarVisibleConfigurationKey); - if (newStatusbarHiddenValue !== this.statusBarHidden) { - this.setStatusBarHidden(newStatusbarHiddenValue, skipLayout); - } - - const newActivityBarHiddenValue = !this.configurationService.getValue(Workbench.activityBarVisibleConfigurationKey); - if (newActivityBarHiddenValue !== this.activityBarHidden) { - this.setActivityBarHidden(newActivityBarHiddenValue, skipLayout); - } - } - - const newMenubarVisibility = this.configurationService.getValue(Workbench.menubarVisibilityConfigurationKey); - this.setMenubarVisibility(newMenubarVisibility, !!skipLayout); + this.sidebarPart.create(sidebarPartContainer); } - //#endregion + private createPanelPart(): void { + const panelPartContainer = this.createPart(Identifiers.PANEL_PART, 'complementary', 'panel', this.state.panel.position === Position.BOTTOM ? 'bottom' : 'right'); - private restoreParts(): Promise { + this.panelPart.create(panelPartContainer); + } + + private createEditorPart(): void { + const editorContainer = this.createPart(Identifiers.EDITOR_PART, 'main', 'editor'); + + this.editorPart.create(editorContainer); + } + + private createStatusbarPart(): void { + const statusbarContainer = this.createPart(Identifiers.STATUSBAR_PART, 'contentinfo', 'statusbar'); + + this.statusbarPart.create(statusbarContainer); + } + + private createPart(id: string, role: string, ...classes: string[]): HTMLElement { + const part = document.createElement('div'); + addClasses(part, 'part', ...classes); + part.id = id; + part.setAttribute('role', role); + + if (!this.configurationService.getValue('workbench.useExperimentalGridLayout')) { + // Insert all workbench parts at the beginning. Issue #52531 + // This is primarily for the title bar to allow overriding -webkit-app-region + this.workbench.insertBefore(part, this.workbench.lastChild); + } + + return part; + } + + private createNotificationsHandlers(accessor: ServicesAccessor): void { + const notificationService = accessor.get(INotificationService) as NotificationService; + + // Notifications Center + this.notificationsCenter = this._register(this.instantiationService.createInstance(NotificationsCenter, this.workbench, notificationService.model)); + + // Notifications Toasts + this.notificationsToasts = this._register(this.instantiationService.createInstance(NotificationsToasts, this.workbench, notificationService.model)); + + // Notifications Alerts + this._register(this.instantiationService.createInstance(NotificationsAlerts, notificationService.model)); + + // Notifications Status + const notificationsStatus = this.instantiationService.createInstance(NotificationsStatus, notificationService.model); + + // Eventing + this._register(this.notificationsCenter.onDidChangeVisibility(() => { + + // Update status + notificationsStatus.update(this.notificationsCenter.isVisible); + + // Update toasts + this.notificationsToasts.update(this.notificationsCenter.isVisible); + })); + + // Register Commands + registerNotificationCommands(this.notificationsCenter, this.notificationsToasts); + } + + private restoreWorkbench(): Promise { const restorePromises: Promise[] = []; - // Restore Editorpart + // Restore editors mark('willRestoreEditors'); restorePromises.push(this.editorPart.whenRestored.then(() => { @@ -846,146 +733,379 @@ export class Workbench extends Disposable implements IPartService { return Promise.resolve(undefined); } - const editorsToOpen = this.resolveEditorsToOpen(); - - if (Array.isArray(editorsToOpen)) { - return openEditors(editorsToOpen, this.editorService); + if (Array.isArray(this.state.editor.editorsToOpen)) { + return openEditors(this.state.editor.editorsToOpen, this.editorService); } - return editorsToOpen.then(editors => openEditors(editors, this.editorService)); + return this.state.editor.editorsToOpen.then(editors => openEditors(editors, this.editorService)); }).then(() => mark('didRestoreEditors'))); // Restore Sidebar - let viewletIdToRestore: string | undefined; - if (!this.sideBarHidden) { - this.sideBarVisibleContext.set(true); - - if (this.shouldRestoreLastOpenedViewlet()) { - viewletIdToRestore = this.storageService.get(SidebarPart.activeViewletSettingsKey, StorageScope.WORKSPACE); - } - - if (!viewletIdToRestore) { - viewletIdToRestore = this.sidebarPart.getDefaultViewletId(); - } - + if (this.state.sideBar.viewletToRestore) { mark('willRestoreViewlet'); - restorePromises.push(this.sidebarPart.openViewlet(viewletIdToRestore) - .then(viewlet => viewlet || this.sidebarPart.openViewlet(this.sidebarPart.getDefaultViewletId())) + restorePromises.push(this.sidebarPart.openViewlet(this.state.sideBar.viewletToRestore) + .then(viewlet => { + if (!viewlet) { + return this.sidebarPart.openViewlet(this.sidebarPart.getDefaultViewletId()); // fallback to default viewlet as needed + } + + return viewlet; + }) .then(() => mark('didRestoreViewlet'))); } // Restore Panel - const panelRegistry = Registry.as(PanelExtensions.Panels); - const panelId = this.storageService.get(PanelPart.activePanelSettingsKey, StorageScope.WORKSPACE, panelRegistry.getDefaultPanelId()); - if (!this.panelHidden && !!panelId) { + if (this.state.panel.panelToRestore) { mark('willRestorePanel'); - const isPanelToRestoreEnabled = !!this.panelPart.getPanels().filter(p => p.id === panelId).length; - const panelIdToRestore = isPanelToRestoreEnabled ? panelId : panelRegistry.getDefaultPanelId(); - this.panelPart.openPanel(panelIdToRestore, false); + this.panelPart.openPanel(this.state.panel.panelToRestore); mark('didRestorePanel'); } - // Restore Zen Mode if active and supported for restore on startup - const zenConfig = this.configurationService.getValue('zenMode'); - const wasZenActive = this.storageService.getBoolean(Workbench.zenModeActiveStorageKey, StorageScope.WORKSPACE, false); - if (wasZenActive && zenConfig.restore) { + // Restore Zen Mode + if (this.state.zenMode.restore) { this.toggleZenMode(true, true); } - // Restore Forced Editor Center Mode - if (this.storageService.getBoolean(Workbench.centeredEditorLayoutActiveStorageKey, StorageScope.WORKSPACE, false)) { + // Restore Editor Center Mode + if (this.state.editor.restoreCentered) { this.centerEditorLayout(true); } - const onRestored = (error?: Error): void => { - this.workbenchRestored = true; + // Emit a warning after 10s if restore does not complete + const restoreTimeoutHandle = setTimeout(() => this.logService.warn('Workbench did not finish loading in 10 seconds, that might be a problem that should be reported.'), 10000); - // Set lifecycle phase to `Restored` - this.lifecycleService.phase = LifecyclePhase.Restored; + let error: Error; + return Promise.all(restorePromises) + .then(() => clearTimeout(restoreTimeoutHandle)) + .catch(err => error = err) + .finally(() => this.instantiationService.invokeFunction(accessor => this.whenRestored(accessor, error))); - // Set lifecycle phase to `Eventually` after a short delay and when - // idle (min 2.5sec, max 5sec) - setTimeout(() => { - this._register(runWhenIdle(() => { - this.lifecycleService.phase = LifecyclePhase.Eventually; - }, 2500)); - }, 2500); - - if (error) { - onUnexpectedError(error); - } - - this.logStartupTelemetry({ - customKeybindingsCount: this.keybindingService.customKeybindingsCount(), - pinnedViewlets: this.activitybarPart.getPinnedViewletIds(), - restoredViewlet: viewletIdToRestore, - restoredEditorsCount: this.editorService.visibleEditors.length - }); - }; - - return Promise.all(restorePromises).then(() => onRestored(), error => onRestored(error)); } - private logStartupTelemetry(info: IWorkbenchStartedInfo): void { - const { filesToOpen, filesToCreate, filesToDiff } = this.configuration; + private whenRestored(accessor: ServicesAccessor, error?: Error): void { + const lifecycleService = accessor.get(ILifecycleService); - /* __GDPR__ - "workspaceLoad" : { - "userAgent" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "windowSize.innerHeight": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "windowSize.innerWidth": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "windowSize.outerHeight": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "windowSize.outerWidth": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "emptyWorkbench": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workbench.filesToOpen": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workbench.filesToCreate": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workbench.filesToDiff": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "customKeybindingsCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "theme": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "language": { "classification": "SystemMetaData", "purpose": "BusinessInsight" }, - "pinnedViewlets": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "restoredViewlet": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "restoredEditors": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "pinnedViewlets": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "startupKind": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } - } - */ - this.telemetryService.publicLog('workspaceLoad', { - userAgent: navigator.userAgent, - windowSize: { innerHeight: window.innerHeight, innerWidth: window.innerWidth, outerHeight: window.outerHeight, outerWidth: window.outerWidth }, - emptyWorkbench: this.contextService.getWorkbenchState() === WorkbenchState.EMPTY, - 'workbench.filesToOpen': filesToOpen && filesToOpen.length || 0, - 'workbench.filesToCreate': filesToCreate && filesToCreate.length || 0, - 'workbench.filesToDiff': filesToDiff && filesToDiff.length || 0, - customKeybindingsCount: info.customKeybindingsCount, - theme: this.themeService.getColorTheme().id, - language, - pinnedViewlets: info.pinnedViewlets, - restoredViewlet: info.restoredViewlet, - restoredEditors: info.restoredEditorsCount, - startupKind: this.lifecycleService.startupKind - }); + this.restored = true; + + // Set lifecycle phase to `Restored` + lifecycleService.phase = LifecyclePhase.Restored; + + // Set lifecycle phase to `Eventually` after a short delay and when + // idle (min 2.5sec, max 5sec) + setTimeout(() => { + this._register(runWhenIdle(() => { + lifecycleService.phase = LifecyclePhase.Eventually; + }, 2500)); + }, 2500); + + if (error) { + onUnexpectedError(error); + } // Telemetry: startup metrics mark('didStartWorkbench'); } - private shouldRestoreLastOpenedViewlet(): boolean { - if (!this.environmentService.isBuilt) { - return true; // always restore sidebar when we are in development mode - } + private saveState(e: IWillSaveStateEvent): void { - // always restore sidebar when the window was reloaded - return this.lifecycleService.startupKind === StartupKind.ReloadedWindow; + // Font info + saveFontInfo(this.storageService); } - private resolveEditorsToOpen(): Promise | IResourceEditor[] { + dispose(): void { + super.dispose(); + + this.disposed = true; + } + + //#endregion + + //#region IPartService + + private readonly _onTitleBarVisibilityChange: Emitter = this._register(new Emitter()); + get onTitleBarVisibilityChange(): Event { return this._onTitleBarVisibilityChange.event; } + + private readonly _onZenMode: Emitter = this._register(new Emitter()); + get onZenModeChange(): Event { return this._onZenMode.event; } + + private workbenchGrid: Grid | WorkbenchLegacyLayout; + + private titleBarPartView: View; + private activityBarPartView: View; + private sideBarPartView: View; + private panelPartView: View; + private editorPartView: View; + private statusBarPartView: View; + + private readonly state = { + fullscreen: false, + + menuBar: { + visibility: undefined as MenuBarVisibility, + toggled: false + }, + + activityBar: { + hidden: false + }, + + sideBar: { + hidden: false, + position: undefined as Position, + width: 300, + viewletToRestore: undefined as string + }, + + editor: { + hidden: false, + centered: false, + restoreCentered: false, + editorsToOpen: undefined as Promise | IResourceEditor[] + }, + + panel: { + hidden: false, + position: undefined as Position, + height: 350, + width: 350, + panelToRestore: undefined as string + }, + + statusBar: { + hidden: false + }, + + zenMode: { + active: false, + restore: false, + transitionedToFullScreen: false, + transitionedToCenteredEditorLayout: false, + wasSideBarVisible: false, + wasPanelVisible: false, + transitionDisposeables: [] as IDisposable[] + } + }; + + private registerLayoutListeners(): void { + + // Storage + this._register(this.storageService.onWillSaveState(e => this.saveLayoutState(e))); + + // Restore editor if hidden and it changes + this._register(this.editorService.onDidVisibleEditorsChange(() => this.setEditorHidden(false))); + this._register(this.editorPart.onDidActivateGroup(() => this.setEditorHidden(false))); + + // Configuration changes + this._register(this.configurationService.onDidChangeConfiguration(() => this.doUpdateLayoutConfiguration())); + + // Fullscreen changes + this._register(onDidChangeFullscreen(() => this.onFullscreenChanged())); + + // Group changes + this._register(this.editorGroupService.onDidAddGroup(() => this.centerEditorLayout(this.state.editor.centered))); + this._register(this.editorGroupService.onDidRemoveGroup(() => this.centerEditorLayout(this.state.editor.centered))); + + // Prevent workbench from scrolling #55456 + this._register(addDisposableListener(this.workbench, EventType.SCROLL, () => this.workbench.scrollTop = 0)); + + // Menubar visibility changes + if ((isWindows || isLinux) && getTitleBarStyle(this.configurationService, this.environmentService) === 'custom') { + this._register(this.titlebarPart.onMenubarVisibilityChange(visible => this.onMenubarToggled(visible))); + } + } + + private onMenubarToggled(visible: boolean) { + if (visible !== this.state.menuBar.toggled) { + this.state.menuBar.toggled = visible; + + if (this.state.fullscreen && (this.state.menuBar.visibility === 'toggle' || this.state.menuBar.visibility === 'default')) { + this._onTitleBarVisibilityChange.fire(); + this.layout(); + } + } + } + + private onFullscreenChanged(): void { + this.state.fullscreen = isFullscreen(); + + // Apply as CSS class + if (this.state.fullscreen) { + addClass(this.workbench, 'fullscreen'); + } else { + removeClass(this.workbench, 'fullscreen'); + + if (this.state.zenMode.transitionedToFullScreen && this.state.zenMode.active) { + this.toggleZenMode(); + } + } + + // Changing fullscreen state of the window has an impact on custom title bar visibility, so we need to update + if (getTitleBarStyle(this.configurationService, this.environmentService) === 'custom') { + this._onTitleBarVisibilityChange.fire(); + this.layout(); // handle title bar when fullscreen changes + } + } + + private doUpdateLayoutConfiguration(skipLayout?: boolean): void { + + // Sidebar position + const newSidebarPositionValue = this.configurationService.getValue(Settings.SIDEBAR_POSITION); + const newSidebarPosition = (newSidebarPositionValue === 'right') ? Position.RIGHT : Position.LEFT; + if (newSidebarPosition !== this.getSideBarPosition()) { + this.setSideBarPosition(newSidebarPosition); + } + + // Panel position + this.updatePanelPosition(); + + if (!this.state.zenMode.active) { + + // Statusbar visibility + const newStatusbarHiddenValue = !this.configurationService.getValue(Settings.STATUSBAR_VISIBLE); + if (newStatusbarHiddenValue !== this.state.statusBar.hidden) { + this.setStatusBarHidden(newStatusbarHiddenValue, skipLayout); + } + + // Activitybar visibility + const newActivityBarHiddenValue = !this.configurationService.getValue(Settings.ACTIVITYBAR_VISIBLE); + if (newActivityBarHiddenValue !== this.state.activityBar.hidden) { + this.setActivityBarHidden(newActivityBarHiddenValue, skipLayout); + } + } + + // Menubar visibility + const newMenubarVisibility = this.configurationService.getValue(Settings.MENUBAR_VISIBLE); + this.setMenubarVisibility(newMenubarVisibility, !!skipLayout); + } + + private setSideBarPosition(position: Position): void { + const wasHidden = this.state.sideBar.hidden; + + if (this.state.sideBar.hidden) { + this.setSideBarHidden(false, true /* Skip Layout */); + } + + const newPositionValue = (position === Position.LEFT) ? 'left' : 'right'; + const oldPositionValue = (this.state.sideBar.position === Position.LEFT) ? 'left' : 'right'; + this.state.sideBar.position = position; + + // Adjust CSS + removeClass(this.activitybarPart.getContainer(), oldPositionValue); + removeClass(this.sidebarPart.getContainer(), oldPositionValue); + addClass(this.activitybarPart.getContainer(), newPositionValue); + addClass(this.sidebarPart.getContainer(), newPositionValue); + + // Update Styles + this.activitybarPart.updateStyles(); + this.sidebarPart.updateStyles(); + + // Layout + if (this.workbenchGrid instanceof Grid) { + if (!wasHidden) { + this.state.sideBar.width = this.workbenchGrid.getViewSize(this.sideBarPartView); + } + + this.workbenchGrid.removeView(this.sideBarPartView); + this.workbenchGrid.removeView(this.activityBarPartView); + + if (!this.state.panel.hidden && this.state.panel.position === Position.BOTTOM) { + this.workbenchGrid.removeView(this.panelPartView); + } + + this.layout(); + } else { + this.workbenchGrid.layout(); + } + } + + private initLayoutState(accessor: ServicesAccessor): void { + const configurationService = accessor.get(IConfigurationService); + const storageService = accessor.get(IStorageService); + const lifecycleService = accessor.get(ILifecycleService); + const contextService = accessor.get(IWorkspaceContextService); + const environmentService = accessor.get(IEnvironmentService); + + // Fullscreen + this.state.fullscreen = isFullscreen(); + + // Menubar visibility + this.state.menuBar.visibility = configurationService.getValue(Settings.MENUBAR_VISIBLE); + + // Activity bar visibility + this.state.activityBar.hidden = !configurationService.getValue(Settings.ACTIVITYBAR_VISIBLE); + + // Sidebar visibility + this.state.sideBar.hidden = storageService.getBoolean(Storage.SIDEBAR_HIDDEN, StorageScope.WORKSPACE, contextService.getWorkbenchState() === WorkbenchState.EMPTY); + + // Sidebar position + this.state.sideBar.position = (configurationService.getValue(Settings.SIDEBAR_POSITION) === 'right') ? Position.RIGHT : Position.LEFT; + + // Sidebar viewlet + if (!this.state.sideBar.hidden) { + const viewletRegistry = Registry.as(ViewletExtensions.Viewlets); + + // Only restore last viewlet if window was reloaded or we are in development mode + let viewletToRestore: string; + if (!environmentService.isBuilt || lifecycleService.startupKind === StartupKind.ReloadedWindow) { + viewletToRestore = storageService.get(SidebarPart.activeViewletSettingsKey, StorageScope.WORKSPACE, viewletRegistry.getDefaultViewletId()); + } else { + viewletToRestore = viewletRegistry.getDefaultViewletId(); + } + + if (viewletToRestore) { + this.state.sideBar.viewletToRestore = viewletToRestore; + } else { + this.state.sideBar.hidden = true; // we hide sidebar if there is no viewlet to restore + } + } + + // Editor centered layout + this.state.editor.restoreCentered = storageService.getBoolean(Storage.CENTERED_LAYOUT_ENABLED, StorageScope.WORKSPACE, false); + + // Editors to open + this.state.editor.editorsToOpen = this.resolveEditorsToOpen(accessor); + + // Panel visibility + this.state.panel.hidden = storageService.getBoolean(Storage.PANEL_HIDDEN, StorageScope.WORKSPACE, true); + + // Panel position + this.updatePanelPosition(); + + // Panel to restore + if (!this.state.panel.hidden) { + const panelRegistry = Registry.as(PanelExtensions.Panels); + + let panelToRestore = storageService.get(PanelPart.activePanelSettingsKey, StorageScope.WORKSPACE, panelRegistry.getDefaultPanelId()); + if (!panelRegistry.hasPanel(panelToRestore)) { + panelToRestore = panelRegistry.getDefaultPanelId(); // fallback to default if panel is unknown + } + + if (panelToRestore) { + this.state.panel.panelToRestore = panelToRestore; + } else { + this.state.panel.hidden = true; // we hide panel if there is no panel to restore + } + } + + // Statusbar visibility + this.state.statusBar.hidden = !configurationService.getValue(Settings.STATUSBAR_VISIBLE); + + // Zen mode enablement + this.state.zenMode.restore = storageService.getBoolean(Storage.ZEN_MODE_ENABLED, StorageScope.WORKSPACE, false) && configurationService.getValue(Settings.ZEN_MODE_RESTORE); + } + + private resolveEditorsToOpen(accessor: ServicesAccessor): Promise | IResourceEditor[] { + const configuration = accessor.get(IWindowService).getConfiguration(); + const configurationService = accessor.get(IConfigurationService); + const contextService = accessor.get(IWorkspaceContextService); + const editorGroupService = accessor.get(IEditorGroupsService); + const backupFileService = accessor.get(IBackupFileService); // Files to open, diff or create - if (this.hasInitialFilesToOpen) { + if (this.hasInitialFilesToOpen()) { // Files to diff is exclusive - const filesToDiff = this.toInputs(this.configuration.filesToDiff, false); + const filesToDiff = this.toInputs(configuration.filesToDiff, false); if (filesToDiff && filesToDiff.length === 2) { return [{ leftResource: filesToDiff[0].resource, @@ -995,21 +1115,21 @@ export class Workbench extends Disposable implements IPartService { }]; } - const filesToCreate = this.toInputs(this.configuration.filesToCreate, true); - const filesToOpen = this.toInputs(this.configuration.filesToOpen, false); + const filesToCreate = this.toInputs(configuration.filesToCreate, true); + const filesToOpen = this.toInputs(configuration.filesToOpen, false); // Otherwise: Open/Create files return [...filesToOpen, ...filesToCreate]; } // Empty workbench - else if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY && this.openUntitledFile()) { - const isEmpty = this.editorGroupService.count === 1 && this.editorGroupService.activeGroup.count === 0; + else if (contextService.getWorkbenchState() === WorkbenchState.EMPTY && configurationService.inspect('workbench.startupEditor').value === 'newUntitledFile') { + const isEmpty = editorGroupService.count === 1 && editorGroupService.activeGroup.count === 0; if (!isEmpty) { return []; // do not open any empty untitled file if we restored editors from previous session } - return this.backupFileService.hasBackups().then(hasBackups => { + return backupFileService.hasBackups().then(hasBackups => { if (hasBackups) { return []; // do not open any empty untitled file if we have backups to restore } @@ -1046,316 +1166,15 @@ export class Workbench extends Disposable implements IPartService { }); } - private openUntitledFile() { - const startupEditor = this.configurationService.inspect('workbench.startupEditor'); + private updatePanelPosition() { + const defaultPanelPosition = this.configurationService.getValue(Settings.PANEL_POSITION); + const panelPosition = this.storageService.get(Storage.PANEL_POSITION, StorageScope.WORKSPACE, defaultPanelPosition); - // Fallback to previous workbench.welcome.enabled setting in case startupEditor is not defined - if (!startupEditor.user && !startupEditor.workspace) { - const welcomeEnabledValue = this.configurationService.getValue('workbench.welcome.enabled'); - if (typeof welcomeEnabledValue === 'boolean') { - return !welcomeEnabledValue; - } - } - - return startupEditor.value === 'newUntitledFile'; + this.state.panel.position = (panelPosition === 'right') ? Position.RIGHT : Position.BOTTOM; } - private initSettings(): void { - - // Editor visiblity - this.editorHidden = false; - - // Sidebar visibility - this.sideBarHidden = this.storageService.getBoolean(Workbench.sidebarHiddenStorageKey, StorageScope.WORKSPACE, this.contextService.getWorkbenchState() === WorkbenchState.EMPTY); - - // Panel part visibility - const panelRegistry = Registry.as(PanelExtensions.Panels); - this.panelHidden = this.storageService.getBoolean(Workbench.panelHiddenStorageKey, StorageScope.WORKSPACE, true); - if (!panelRegistry.getDefaultPanelId()) { - this.panelHidden = true; // we hide panel part if there is no default panel - } - - // Sidebar position - const sideBarPosition = this.configurationService.getValue(Workbench.sidebarPositionConfigurationKey); - this.sideBarPosition = (sideBarPosition === 'right') ? Position.RIGHT : Position.LEFT; - - // Panel position - this.setPanelPositionFromStorageOrConfig(); - - // Menubar visibility - const menuBarVisibility = this.configurationService.getValue(Workbench.menubarVisibilityConfigurationKey); - this.setMenubarVisibility(menuBarVisibility, true); - - // Statusbar visibility - const statusBarVisible = this.configurationService.getValue(Workbench.statusbarVisibleConfigurationKey); - this.statusBarHidden = !statusBarVisible; - - // Activity bar visibility - const activityBarVisible = this.configurationService.getValue(Workbench.activityBarVisibleConfigurationKey); - this.activityBarHidden = !activityBarVisible; - - // Font aliasing - this.fontAliasing = this.configurationService.getValue(Workbench.fontAliasingConfigurationKey); - - // Zen mode - this.zenMode = { - active: false, - transitionedToFullScreen: false, - transitionedToCenteredEditorLayout: false, - wasSideBarVisible: false, - wasPanelVisible: false, - transitionDisposeables: [] - }; - } - - private setPanelPositionFromStorageOrConfig() { - const defaultPanelPosition = this.configurationService.getValue(Workbench.defaultPanelPositionStorageKey); - const panelPosition = this.storageService.get(Workbench.panelPositionStorageKey, StorageScope.WORKSPACE, defaultPanelPosition); - - this.panelPosition = (panelPosition === 'right') ? Position.RIGHT : Position.BOTTOM; - } - - private useCustomTitleBarStyle(): boolean { - return getTitleBarStyle(this.configurationService, this.environmentService) === 'custom'; - } - - private saveLastPanelDimension(): void { - if (!(this.workbenchGrid instanceof Grid)) { - return; - } - - if (this.panelPosition === Position.BOTTOM) { - this.uiState.lastPanelHeight = this.workbenchGrid.getViewSize(this.panelPartView); - } else { - this.uiState.lastPanelWidth = this.workbenchGrid.getViewSize(this.panelPartView); - } - } - - private getLastPanelDimension(position: Position): number | undefined { - return position === Position.BOTTOM ? this.uiState.lastPanelHeight : this.uiState.lastPanelWidth; - } - - private setStatusBarHidden(hidden: boolean, skipLayout?: boolean): void { - this.statusBarHidden = hidden; - - // Adjust CSS - if (hidden) { - addClass(this.workbench, 'nostatusbar'); - } else { - removeClass(this.workbench, 'nostatusbar'); - } - - // Layout - if (!skipLayout) { - if (this.workbenchGrid instanceof Grid) { - this.layout(); - } else { - this.workbenchGrid.layout(); - } - } - } - - private setFontAliasing(aliasing: FontAliasingOption) { - this.fontAliasing = aliasing; - - // Remove all - removeClasses(this.workbench, ...fontAliasingValues.map(value => `monaco-font-aliasing-${value}`)); - - // Add specific - if (fontAliasingValues.some(option => option === aliasing)) { - addClass(this.workbench, `monaco-font-aliasing-${aliasing}`); - } - } - - private createWorkbenchLayout(): void { - if (this.configurationService.getValue('workbench.useExperimentalGridLayout')) { - - // Create view wrappers for all parts - this.titlebarPartView = new View(this.titlebarPart); - this.sidebarPartView = new View(this.sidebarPart); - this.activitybarPartView = new View(this.activitybarPart); - this.editorPartView = new View(this.editorPart); - this.panelPartView = new View(this.panelPart); - this.statusbarPartView = new View(this.statusbarPart); - - this.workbenchGrid = new Grid(this.editorPartView, { proportionalLayout: false }); - - this.workbench.prepend(this.workbenchGrid.element); - } else { - this.workbenchGrid = this.instantiationService.createInstance( - WorkbenchLayout, - this.container, - this.workbench, - { - titlebar: this.titlebarPart, - activitybar: this.activitybarPart, - editor: this.editorPart, - sidebar: this.sidebarPart, - panel: this.panelPart, - statusbar: this.statusbarPart, - }, - this.quickOpen, - this.quickInput, - this.notificationsCenter, - this.notificationsToasts - ); - } - } - - private renderWorkbench(): void { - - // Apply sidebar state as CSS class - if (this.sideBarHidden) { - addClass(this.workbench, 'nosidebar'); - } - - if (this.panelHidden) { - addClass(this.workbench, 'nopanel'); - } - - if (this.statusBarHidden) { - addClass(this.workbench, 'nostatusbar'); - } - - // Apply font aliasing - this.setFontAliasing(this.fontAliasing); - - // Apply fullscreen state - if (isFullscreen()) { - addClass(this.workbench, 'fullscreen'); - } - - // Create Parts - this.createTitlebarPart(); - this.createActivityBarPart(); - this.createSidebarPart(); - this.createEditorPart(); - this.createPanelPart(); - this.createStatusbarPart(); - - // Notification Handlers - this.createNotificationsHandlers(); - - - // Menubar visibility changes - if ((isWindows || isLinux) && this.useCustomTitleBarStyle()) { - this.titlebarPart.onMenubarVisibilityChange()(e => this.onMenubarToggled(e)); - } - - // Add Workbench to DOM - this.container.appendChild(this.workbench); - } - - private createTitlebarPart(): void { - const titlebarContainer = this.createPart(Identifiers.TITLEBAR_PART, ['part', 'titlebar'], 'contentinfo'); - - this.titlebarPart.create(titlebarContainer); - } - - private createActivityBarPart(): void { - const activitybarPartContainer = this.createPart(Identifiers.ACTIVITYBAR_PART, ['part', 'activitybar', this.sideBarPosition === Position.LEFT ? 'left' : 'right'], 'navigation'); - - this.activitybarPart.create(activitybarPartContainer); - } - - private createSidebarPart(): void { - const sidebarPartContainer = this.createPart(Identifiers.SIDEBAR_PART, ['part', 'sidebar', this.sideBarPosition === Position.LEFT ? 'left' : 'right'], 'complementary'); - - this.sidebarPart.create(sidebarPartContainer); - } - - private createPanelPart(): void { - const panelPartContainer = this.createPart(Identifiers.PANEL_PART, ['part', 'panel', this.panelPosition === Position.BOTTOM ? 'bottom' : 'right'], 'complementary'); - - this.panelPart.create(panelPartContainer); - } - - private createEditorPart(): void { - const editorContainer = this.createPart(Identifiers.EDITOR_PART, ['part', 'editor'], 'main'); - - this.editorPart.create(editorContainer); - } - - private createStatusbarPart(): void { - const statusbarContainer = this.createPart(Identifiers.STATUSBAR_PART, ['part', 'statusbar'], 'contentinfo'); - - this.statusbarPart.create(statusbarContainer); - } - - private createPart(id: string, classes: string[], role: string): HTMLElement { - const part = document.createElement('div'); - classes.forEach(clazz => addClass(part, clazz)); - part.id = id; - part.setAttribute('role', role); - - if (!this.configurationService.getValue('workbench.useExperimentalGridLayout')) { - // Insert all workbench parts at the beginning. Issue #52531 - // This is primarily for the title bar to allow overriding -webkit-app-region - this.workbench.insertBefore(part, this.workbench.lastChild); - } - - return part; - } - - private createNotificationsHandlers(): void { - - // Notifications Center - this.notificationsCenter = this._register(this.instantiationService.createInstance(NotificationsCenter, this.workbench, this.notificationService.model)); - - // Notifications Toasts - this.notificationsToasts = this._register(this.instantiationService.createInstance(NotificationsToasts, this.workbench, this.notificationService.model)); - - // Notifications Alerts - this._register(this.instantiationService.createInstance(NotificationsAlerts, this.notificationService.model)); - - // Notifications Status - const notificationsStatus = this.instantiationService.createInstance(NotificationsStatus, this.notificationService.model); - - // Eventing - this._register(this.notificationsCenter.onDidChangeVisibility(() => { - - // Update status - notificationsStatus.update(this.notificationsCenter.isVisible); - - // Update toasts - this.notificationsToasts.update(this.notificationsCenter.isVisible); - })); - - // Register Commands - registerNotificationCommands(this.notificationsCenter, this.notificationsToasts); - } - - private saveState(e: IWillSaveStateEvent): void { - if (this.zenMode.active) { - this.storageService.store(Workbench.zenModeActiveStorageKey, true, StorageScope.WORKSPACE); - } else { - this.storageService.remove(Workbench.zenModeActiveStorageKey, StorageScope.WORKSPACE); - } - - if (e.reason === WillSaveStateReason.SHUTDOWN && this.zenMode.active) { - const zenConfig = this.configurationService.getValue('zenMode'); - if (!zenConfig.restore) { - // We will not restore zen mode, need to clear all zen mode state changes - this.toggleZenMode(true); - } - } - } - - dispose(): void { - super.dispose(); - - this.workbenchShutdown = true; - } - - //#region IPartService - - private readonly _onTitleBarVisibilityChange: Emitter = this._register(new Emitter()); - get onTitleBarVisibilityChange(): Event { return this._onTitleBarVisibilityChange.event; } - - get onEditorLayout(): Event { return this.editorPart.onDidLayout; } - isRestored(): boolean { - return !!(this.workbenchRestored && this.workbenchStarted); + return this.restored; } hasFocus(part: Parts): boolean { @@ -1365,6 +1184,7 @@ export class Workbench extends Disposable implements IPartService { } const container = this.getContainer(part); + return isAncestor(activeElement, container); } @@ -1390,29 +1210,29 @@ export class Workbench extends Disposable implements IPartService { isVisible(part: Parts): boolean { switch (part) { case Parts.TITLEBAR_PART: - if (!this.useCustomTitleBarStyle()) { + if (getTitleBarStyle(this.configurationService, this.environmentService) === 'native') { return false; - } else if (!isFullscreen()) { + } else if (!this.state.fullscreen) { return true; } else if (isMacintosh) { return false; - } else if (this.menubarVisibility === 'visible') { + } else if (this.state.menuBar.visibility === 'visible') { return true; - } else if (this.menubarVisibility === 'toggle' || this.menubarVisibility === 'default') { - return this.menubarToggled; + } else if (this.state.menuBar.visibility === 'toggle' || this.state.menuBar.visibility === 'default') { + return this.state.menuBar.toggled; } return false; case Parts.SIDEBAR_PART: - return !this.sideBarHidden; + return !this.state.sideBar.hidden; case Parts.PANEL_PART: - return !this.panelHidden; + return !this.state.panel.hidden; case Parts.STATUSBAR_PART: - return !this.statusBarHidden; + return !this.state.statusBar.hidden; case Parts.ACTIVITYBAR_PART: - return !this.activityBarHidden; + return !this.state.activityBar.hidden; case Parts.EDITOR_PART: - return this.workbenchGrid instanceof Grid ? !this.editorHidden : true; + return this.workbenchGrid instanceof Grid ? !this.state.editor.hidden : true; } return true; // any other part cannot be hidden @@ -1426,7 +1246,7 @@ export class Workbench extends Disposable implements IPartService { } else { offset = this.workbenchGrid.partLayoutInfo.titlebar.height; - if (isMacintosh || this.menubarVisibility === 'hidden') { + if (isMacintosh || this.state.menuBar.visibility === 'hidden') { offset /= getZoomFactor(); } } @@ -1440,30 +1260,32 @@ export class Workbench extends Disposable implements IPartService { } toggleZenMode(skipLayout?: boolean, restoring = false): void { - this.zenMode.active = !this.zenMode.active; - this.zenMode.transitionDisposeables = dispose(this.zenMode.transitionDisposeables); + this.state.zenMode.active = !this.state.zenMode.active; + this.state.zenMode.transitionDisposeables = dispose(this.state.zenMode.transitionDisposeables); + + const setLineNumbers = (lineNumbers: any) => this.editorService.visibleTextEditorWidgets.forEach(editor => editor.updateOptions({ lineNumbers })); // Check if zen mode transitioned to full screen and if now we are out of zen mode // -> we need to go out of full screen (same goes for the centered editor layout) let toggleFullScreen = false; - const setLineNumbers = (lineNumbers: any) => { - this.editorService.visibleControls.forEach(editor => { - const control = editor.getControl(); - if (control) { - control.updateOptions({ lineNumbers }); - } - }); - }; // Zen Mode Active - if (this.zenMode.active) { - const config = this.configurationService.getValue('zenMode'); + if (this.state.zenMode.active) { + const config: { + fullScreen: boolean; + centerLayout: boolean; + hideTabs: boolean; + hideActivityBar: boolean; + hideStatusBar: boolean; + hideLineNumbers: boolean; + } = this.configurationService.getValue('zenMode'); - toggleFullScreen = !isFullscreen() && config.fullScreen; - this.zenMode.transitionedToFullScreen = restoring ? config.fullScreen : toggleFullScreen; - this.zenMode.transitionedToCenteredEditorLayout = !this.isEditorLayoutCentered() && config.centerLayout; - this.zenMode.wasSideBarVisible = this.isVisible(Parts.SIDEBAR_PART); - this.zenMode.wasPanelVisible = this.isVisible(Parts.PANEL_PART); + toggleFullScreen = !this.state.fullscreen && config.fullScreen; + + this.state.zenMode.transitionedToFullScreen = restoring ? config.fullScreen : toggleFullScreen; + this.state.zenMode.transitionedToCenteredEditorLayout = !this.isEditorLayoutCentered() && config.centerLayout; + this.state.zenMode.wasSideBarVisible = this.isVisible(Parts.SIDEBAR_PART); + this.state.zenMode.wasPanelVisible = this.isVisible(Parts.PANEL_PART); this.setPanelHidden(true, true); this.setSideBarHidden(true, true); @@ -1478,11 +1300,11 @@ export class Workbench extends Disposable implements IPartService { if (config.hideLineNumbers) { setLineNumbers('off'); - this.zenMode.transitionDisposeables.push(this.editorService.onDidVisibleEditorsChange(() => setLineNumbers('off'))); + this.state.zenMode.transitionDisposeables.push(this.editorService.onDidVisibleEditorsChange(() => setLineNumbers('off'))); } if (config.hideTabs && this.editorPart.partOptions.showTabs) { - this.zenMode.transitionDisposeables.push(this.editorPart.enforcePartOptions({ showTabs: false })); + this.state.zenMode.transitionDisposeables.push(this.editorPart.enforcePartOptions({ showTabs: false })); } if (config.centerLayout) { @@ -1492,29 +1314,27 @@ export class Workbench extends Disposable implements IPartService { // Zen Mode Inactive else { - if (this.zenMode.wasPanelVisible) { + if (this.state.zenMode.wasPanelVisible) { this.setPanelHidden(false, true); } - if (this.zenMode.wasSideBarVisible) { + if (this.state.zenMode.wasSideBarVisible) { this.setSideBarHidden(false, true); } - if (this.zenMode.transitionedToCenteredEditorLayout) { + if (this.state.zenMode.transitionedToCenteredEditorLayout) { this.centerEditorLayout(false, true); } setLineNumbers(this.configurationService.getValue('editor.lineNumbers')); // Status bar and activity bar visibility come from settings -> update their visibility. - this.onDidUpdateConfiguration(true); + this.doUpdateLayoutConfiguration(true); this.editorGroupService.activeGroup.focus(); - toggleFullScreen = this.zenMode.transitionedToFullScreen && isFullscreen(); + toggleFullScreen = this.state.zenMode.transitionedToFullScreen && this.state.fullscreen; } - this.inZenModeContext.set(this.zenMode.active); - if (!skipLayout) { this.layout(); } @@ -1522,100 +1342,70 @@ export class Workbench extends Disposable implements IPartService { if (toggleFullScreen) { this.windowService.toggleFullScreen(); } + + // Event + this._onZenMode.fire(this.state.zenMode.active); } - private updateGrid(): void { - if (!(this.workbenchGrid instanceof Grid)) { - return; + private setStatusBarHidden(hidden: boolean, skipLayout?: boolean): void { + this.state.statusBar.hidden = hidden; + + // Adjust CSS + if (hidden) { + addClass(this.workbench, 'nostatusbar'); + } else { + removeClass(this.workbench, 'nostatusbar'); } - let panelInGrid = this.workbenchGrid.hasView(this.panelPartView); - let sidebarInGrid = this.workbenchGrid.hasView(this.sidebarPartView); - let activityBarInGrid = this.workbenchGrid.hasView(this.activitybarPartView); - let statusBarInGrid = this.workbenchGrid.hasView(this.statusbarPartView); - let titlebarInGrid = this.workbenchGrid.hasView(this.titlebarPartView); - - // Add parts to grid - if (!statusBarInGrid) { - this.workbenchGrid.addView(this.statusbarPartView, Sizing.Split, this.editorPartView, Direction.Down); - statusBarInGrid = true; + // Layout + if (!skipLayout) { + if (this.workbenchGrid instanceof Grid) { + this.layout(); + } else { + this.workbenchGrid.layout(); + } } + } - if (!titlebarInGrid && this.useCustomTitleBarStyle()) { - this.workbenchGrid.addView(this.titlebarPartView, Sizing.Split, this.editorPartView, Direction.Up); - titlebarInGrid = true; - } + private createWorkbenchLayout(): void { + if (this.configurationService.getValue('workbench.useExperimentalGridLayout')) { - if (!activityBarInGrid) { - this.workbenchGrid.addView(this.activitybarPartView, Sizing.Split, panelInGrid && this.sideBarPosition === this.panelPosition ? this.panelPartView : this.editorPartView, this.sideBarPosition === Position.RIGHT ? Direction.Right : Direction.Left); - activityBarInGrid = true; - } + // Create view wrappers for all parts + this.titleBarPartView = new View(this.titlebarPart); + this.sideBarPartView = new View(this.sidebarPart); + this.activityBarPartView = new View(this.activitybarPart); + this.editorPartView = new View(this.editorPart); + this.panelPartView = new View(this.panelPart); + this.statusBarPartView = new View(this.statusbarPart); - if (!sidebarInGrid) { - this.workbenchGrid.addView(this.sidebarPartView, this.uiState.lastSidebarDimension !== undefined ? this.uiState.lastSidebarDimension : Sizing.Split, this.activitybarPartView, this.sideBarPosition === Position.LEFT ? Direction.Right : Direction.Left); - sidebarInGrid = true; - } + this.workbenchGrid = new Grid(this.editorPartView, { proportionalLayout: false }); - if (!panelInGrid) { - this.workbenchGrid.addView(this.panelPartView, this.getLastPanelDimension(this.panelPosition) !== undefined ? this.getLastPanelDimension(this.panelPosition) : Sizing.Split, this.editorPartView, this.panelPosition === Position.BOTTOM ? Direction.Down : Direction.Right); - panelInGrid = true; - } - - // Hide parts - if (this.panelHidden) { - this.panelPartView.hide(); - } - - if (this.statusBarHidden) { - this.statusbarPartView.hide(); - } - - if (!this.isVisible(Parts.TITLEBAR_PART)) { - this.titlebarPartView.hide(); - } - - if (this.activityBarHidden) { - this.activitybarPartView.hide(); - } - - if (this.sideBarHidden) { - this.sidebarPartView.hide(); - } - - if (this.editorHidden) { - this.editorPartView.hide(); - } - - // Show visible parts - if (!this.editorHidden) { - this.editorPartView.show(); - } - - if (!this.statusBarHidden) { - this.statusbarPartView.show(); - } - - if (this.isVisible(Parts.TITLEBAR_PART)) { - this.titlebarPartView.show(); - } - - if (!this.activityBarHidden) { - this.activitybarPartView.show(); - } - - if (!this.sideBarHidden) { - this.sidebarPartView.show(); - } - - if (!this.panelHidden) { - this.panelPartView.show(); + this.workbench.prepend(this.workbenchGrid.element); + } else { + this.workbenchGrid = this.instantiationService.createInstance( + WorkbenchLegacyLayout, + this.container, + this.workbench, + { + titlebar: this.titlebarPart, + activitybar: this.activitybarPart, + editor: this.editorPart, + sidebar: this.sidebarPart, + panel: this.panelPart, + statusbar: this.statusbarPart, + }, + this.quickOpen, + this.quickInput, + this.notificationsCenter, + this.notificationsToasts + ); } } layout(options?: ILayoutOptions): void { this.contextViewService.layout(); - if (this.workbenchStarted && !this.workbenchShutdown) { + if (!this.disposed) { if (this.workbenchGrid instanceof Grid) { const dimensions = getClientArea(this.container); position(this.workbench, 0, 0, 0, 0, 'relative'); @@ -1630,21 +1420,115 @@ export class Workbench extends Disposable implements IPartService { this.notificationsCenter.layout(dimensions); this.notificationsToasts.layout(dimensions); - // Update grid view membership - this.updateGrid(); + // Layout Grid + this.layoutGrid(); } else { this.workbenchGrid.layout(options); } } } + private layoutGrid(): void { + if (!(this.workbenchGrid instanceof Grid)) { + return; + } + + let panelInGrid = this.workbenchGrid.hasView(this.panelPartView); + let sidebarInGrid = this.workbenchGrid.hasView(this.sideBarPartView); + let activityBarInGrid = this.workbenchGrid.hasView(this.activityBarPartView); + let statusBarInGrid = this.workbenchGrid.hasView(this.statusBarPartView); + let titlebarInGrid = this.workbenchGrid.hasView(this.titleBarPartView); + + // Add parts to grid + if (!statusBarInGrid) { + this.workbenchGrid.addView(this.statusBarPartView, Sizing.Split, this.editorPartView, Direction.Down); + statusBarInGrid = true; + } + + if (!titlebarInGrid && getTitleBarStyle(this.configurationService, this.environmentService) === 'custom') { + this.workbenchGrid.addView(this.titleBarPartView, Sizing.Split, this.editorPartView, Direction.Up); + titlebarInGrid = true; + } + + if (!activityBarInGrid) { + this.workbenchGrid.addView(this.activityBarPartView, Sizing.Split, panelInGrid && this.state.sideBar.position === this.state.panel.position ? this.panelPartView : this.editorPartView, this.state.sideBar.position === Position.RIGHT ? Direction.Right : Direction.Left); + activityBarInGrid = true; + } + + if (!sidebarInGrid) { + this.workbenchGrid.addView(this.sideBarPartView, this.state.sideBar.width !== undefined ? this.state.sideBar.width : Sizing.Split, this.activityBarPartView, this.state.sideBar.position === Position.LEFT ? Direction.Right : Direction.Left); + sidebarInGrid = true; + } + + if (!panelInGrid) { + this.workbenchGrid.addView(this.panelPartView, this.getPanelDimension(this.state.panel.position) !== undefined ? this.getPanelDimension(this.state.panel.position) : Sizing.Split, this.editorPartView, this.state.panel.position === Position.BOTTOM ? Direction.Down : Direction.Right); + panelInGrid = true; + } + + // Hide parts + if (this.state.panel.hidden) { + this.panelPartView.hide(); + } + + if (this.state.statusBar.hidden) { + this.statusBarPartView.hide(); + } + + if (!this.isVisible(Parts.TITLEBAR_PART)) { + this.titleBarPartView.hide(); + } + + if (this.state.activityBar.hidden) { + this.activityBarPartView.hide(); + } + + if (this.state.sideBar.hidden) { + this.sideBarPartView.hide(); + } + + if (this.state.editor.hidden) { + this.editorPartView.hide(); + } + + // Show visible parts + if (!this.state.editor.hidden) { + this.editorPartView.show(); + } + + if (!this.state.statusBar.hidden) { + this.statusBarPartView.show(); + } + + if (this.isVisible(Parts.TITLEBAR_PART)) { + this.titleBarPartView.show(); + } + + if (!this.state.activityBar.hidden) { + this.activityBarPartView.show(); + } + + if (!this.state.sideBar.hidden) { + this.sideBarPartView.show(); + } + + if (!this.state.panel.hidden) { + this.panelPartView.show(); + } + } + + private getPanelDimension(position: Position): number | undefined { + return position === Position.BOTTOM ? this.state.panel.height : this.state.panel.width; + } + isEditorLayoutCentered(): boolean { - return this.shouldCenterLayout; + return this.state.editor.centered; } centerEditorLayout(active: boolean, skipLayout?: boolean): void { - this.storageService.store(Workbench.centeredEditorLayoutActiveStorageKey, active, StorageScope.WORKSPACE); - this.shouldCenterLayout = active; + this.state.editor.centered = active; + + this.storageService.store(Storage.CENTERED_LAYOUT_ENABLED, active, StorageScope.WORKSPACE); + let smartActive = active; if (this.editorPart.groups.length > 1 && this.configurationService.getValue('workbench.editor.centeredLayoutAutoResize')) { smartActive = false; // Respect the auto resize setting - do not go into centered layout if there is more than 1 group. @@ -1664,7 +1548,7 @@ export class Workbench extends Disposable implements IPartService { let view: View; switch (part) { case Parts.SIDEBAR_PART: - view = this.sidebarPartView; + view = this.sideBarPartView; case Parts.PANEL_PART: view = this.panelPartView; case Parts.EDITOR_PART: @@ -1681,7 +1565,7 @@ export class Workbench extends Disposable implements IPartService { } setActivityBarHidden(hidden: boolean, skipLayout?: boolean): void { - this.activityBarHidden = hidden; + this.state.activityBar.hidden = hidden; // Layout if (!skipLayout) { @@ -1694,14 +1578,14 @@ export class Workbench extends Disposable implements IPartService { } setEditorHidden(hidden: boolean, skipLayout?: boolean): void { - if (!(this.workbenchGrid instanceof Grid)) { + if (!(this.workbenchGrid instanceof Grid) || hidden === this.state.editor.hidden) { return; } - this.editorHidden = hidden; + this.state.editor.hidden = hidden; // The editor and the panel cannot be hidden at the same time - if (this.editorHidden && this.panelHidden) { + if (this.state.editor.hidden && this.state.panel.hidden) { this.setPanelHidden(false, true); } @@ -1711,8 +1595,7 @@ export class Workbench extends Disposable implements IPartService { } setSideBarHidden(hidden: boolean, skipLayout?: boolean): void { - this.sideBarHidden = hidden; - this.sideBarVisibleContext.set(!hidden); + this.state.sideBar.hidden = hidden; // Adjust CSS if (hidden) { @@ -1724,9 +1607,9 @@ export class Workbench extends Disposable implements IPartService { // If sidebar becomes hidden, also hide the current active Viewlet if any if (hidden && this.sidebarPart.getActiveViewlet()) { this.sidebarPart.hideActiveViewlet(); - const activePanel = this.panelPart.getActivePanel(); // Pass Focus to Editor or Panel if Sidebar is now hidden + const activePanel = this.panelPart.getActivePanel(); if (this.hasFocus(Parts.PANEL_PART) && activePanel) { activePanel.focus(); } else { @@ -1748,9 +1631,9 @@ export class Workbench extends Disposable implements IPartService { // Remember in settings const defaultHidden = this.contextService.getWorkbenchState() === WorkbenchState.EMPTY; if (hidden !== defaultHidden) { - this.storageService.store(Workbench.sidebarHiddenStorageKey, hidden ? 'true' : 'false', StorageScope.WORKSPACE); + this.storageService.store(Storage.SIDEBAR_HIDDEN, hidden ? 'true' : 'false', StorageScope.WORKSPACE); } else { - this.storageService.remove(Workbench.sidebarHiddenStorageKey, StorageScope.WORKSPACE); + this.storageService.remove(Storage.SIDEBAR_HIDDEN, StorageScope.WORKSPACE); } // Layout @@ -1764,7 +1647,7 @@ export class Workbench extends Disposable implements IPartService { } setPanelHidden(hidden: boolean, skipLayout?: boolean): void { - this.panelHidden = hidden; + this.state.panel.hidden = hidden; // Adjust CSS if (hidden) { @@ -1790,13 +1673,13 @@ export class Workbench extends Disposable implements IPartService { // Remember in settings if (!hidden) { - this.storageService.store(Workbench.panelHiddenStorageKey, 'false', StorageScope.WORKSPACE); + this.storageService.store(Storage.PANEL_HIDDEN, 'false', StorageScope.WORKSPACE); } else { - this.storageService.remove(Workbench.panelHiddenStorageKey, StorageScope.WORKSPACE); + this.storageService.remove(Storage.PANEL_HIDDEN, StorageScope.WORKSPACE); } - // The editor and panel cannot be hiddne at the same time - if (hidden && this.editorHidden) { + // The editor and panel cannot be hidden at the same time + if (hidden && this.state.editor.hidden) { this.setEditorHidden(false, true); } @@ -1831,53 +1714,12 @@ export class Workbench extends Disposable implements IPartService { } getSideBarPosition(): Position { - return this.sideBarPosition; - } - - setSideBarPosition(position: Position): void { - const wasHidden = this.sideBarHidden; - - if (this.sideBarHidden) { - this.setSideBarHidden(false, true /* Skip Layout */); - } - - const newPositionValue = (position === Position.LEFT) ? 'left' : 'right'; - const oldPositionValue = (this.sideBarPosition === Position.LEFT) ? 'left' : 'right'; - this.sideBarPosition = position; - - // Adjust CSS - removeClass(this.activitybarPart.getContainer(), oldPositionValue); - removeClass(this.sidebarPart.getContainer(), oldPositionValue); - addClass(this.activitybarPart.getContainer(), newPositionValue); - addClass(this.sidebarPart.getContainer(), newPositionValue); - - // Update Styles - this.activitybarPart.updateStyles(); - this.sidebarPart.updateStyles(); - - // Layout - if (this.workbenchGrid instanceof Grid) { - - if (!wasHidden) { - this.uiState.lastSidebarDimension = this.workbenchGrid.getViewSize(this.sidebarPartView); - } - - this.workbenchGrid.removeView(this.sidebarPartView); - this.workbenchGrid.removeView(this.activitybarPartView); - - if (!this.panelHidden && this.panelPosition === Position.BOTTOM) { - this.workbenchGrid.removeView(this.panelPartView); - } - - this.layout(); - } else { - this.workbenchGrid.layout(); - } + return this.state.sideBar.position; } setMenubarVisibility(visibility: MenuBarVisibility, skipLayout: boolean): void { - if (this.menubarVisibility !== visibility) { - this.menubarVisibility = visibility; + if (this.state.menuBar.visibility !== visibility) { + this.state.menuBar.visibility = visibility; // Layout if (!skipLayout) { @@ -1892,26 +1734,35 @@ export class Workbench extends Disposable implements IPartService { } getMenubarVisibility(): MenuBarVisibility { - return this.menubarVisibility; + return this.state.menuBar.visibility; } getPanelPosition(): Position { - return this.panelPosition; + return this.state.panel.position; } setPanelPosition(position: Position): void { - const wasHidden = this.panelHidden; + const wasHidden = this.state.panel.hidden; - if (this.panelHidden) { + if (this.state.panel.hidden) { this.setPanelHidden(false, true /* Skip Layout */); } else { - this.saveLastPanelDimension(); + this.savePanelDimension(); } const newPositionValue = (position === Position.BOTTOM) ? 'bottom' : 'right'; - const oldPositionValue = (this.panelPosition === Position.BOTTOM) ? 'bottom' : 'right'; - this.panelPosition = position; - this.storageService.store(Workbench.panelPositionStorageKey, PositionToString(this.panelPosition).toLowerCase(), StorageScope.WORKSPACE); + const oldPositionValue = (this.state.panel.position === Position.BOTTOM) ? 'bottom' : 'right'; + this.state.panel.position = position; + + function positionToString(position: Position): string { + switch (position) { + case Position.LEFT: return 'left'; + case Position.RIGHT: return 'right'; + case Position.BOTTOM: return 'bottom'; + } + } + + this.storageService.store(Storage.PANEL_POSITION, positionToString(this.state.panel.position), StorageScope.WORKSPACE); // Adjust CSS removeClass(this.panelPart.getContainer(), oldPositionValue); @@ -1923,7 +1774,7 @@ export class Workbench extends Disposable implements IPartService { // Layout if (this.workbenchGrid instanceof Grid) { if (!wasHidden) { - this.saveLastPanelDimension(); + this.savePanelDimension(); } this.workbenchGrid.removeView(this.panelPartView); @@ -1933,5 +1784,33 @@ export class Workbench extends Disposable implements IPartService { } } + private savePanelDimension(): void { + if (!(this.workbenchGrid instanceof Grid)) { + return; + } + + if (this.state.panel.position === Position.BOTTOM) { + this.state.panel.height = this.workbenchGrid.getViewSize(this.panelPartView); + } else { + this.state.panel.width = this.workbenchGrid.getViewSize(this.panelPartView); + } + } + + private saveLayoutState(e: IWillSaveStateEvent): void { + + // Zen Mode + if (this.state.zenMode.active) { + this.storageService.store(Storage.ZEN_MODE_ENABLED, true, StorageScope.WORKSPACE); + } else { + this.storageService.remove(Storage.ZEN_MODE_ENABLED, StorageScope.WORKSPACE); + } + + if (e.reason === WillSaveStateReason.SHUTDOWN && this.state.zenMode.active) { + if (!this.configurationService.getValue(Settings.ZEN_MODE_RESTORE)) { + this.toggleZenMode(true); // We will not restore zen mode, need to clear all zen mode state changes + } + } + } + //#endregion } diff --git a/src/vs/workbench/services/activity/browser/activityService.ts b/src/vs/workbench/services/activity/browser/activityService.ts index a5230bd32ae..e75331d2e3c 100644 --- a/src/vs/workbench/services/activity/browser/activityService.ts +++ b/src/vs/workbench/services/activity/browser/activityService.ts @@ -30,5 +30,4 @@ export class ActivityService implements IActivityService { getPinnedViewletIds(): string[] { return this.activitybarPart.getPinnedViewletIds(); } - } diff --git a/src/vs/workbench/services/backup/node/backupFileService.ts b/src/vs/workbench/services/backup/node/backupFileService.ts index a089102b7f9..61fafbaa6f6 100644 --- a/src/vs/workbench/services/backup/node/backupFileService.ts +++ b/src/vs/workbench/services/backup/node/backupFileService.ts @@ -15,6 +15,8 @@ import { ITextBufferFactory } from 'vs/editor/common/model'; import { createTextBufferFactoryFromStream, createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel'; import { keys } from 'vs/base/common/map'; import { Schemas } from 'vs/base/common/network'; +import { IWindowService } from 'vs/platform/windows/common/windows'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; export interface IBackupFilesModel { resolve(backupRoot: string): Promise; @@ -107,6 +109,63 @@ export class BackupFilesModel implements IBackupFilesModel { export class BackupFileService implements IBackupFileService { + _serviceBrand: any; + + private impl: IBackupFileService; + + constructor( + @IWindowService windowService: IWindowService, + @IFileService fileService: IFileService + ) { + const backupWorkspacePath = windowService.getConfiguration().backupPath; + if (backupWorkspacePath) { + this.impl = new BackupFileServiceImpl(backupWorkspacePath, fileService); + } else { + this.impl = new InMemoryBackupFileService(); + } + } + + initialize(backupWorkspacePath: string): void { + if (this.impl instanceof BackupFileServiceImpl) { + this.impl.initialize(backupWorkspacePath); + } + } + + hasBackups(): Promise { + return this.impl.hasBackups(); + } + + loadBackupResource(resource: Uri): Promise { + return this.impl.loadBackupResource(resource); + } + + backupResource(resource: Uri, content: ITextSnapshot, versionId?: number): Promise { + return this.impl.backupResource(resource, content, versionId); + } + + discardResourceBackup(resource: Uri): Promise { + return this.impl.discardResourceBackup(resource); + } + + discardAllWorkspaceBackups(): Promise { + return this.impl.discardAllWorkspaceBackups(); + } + + getWorkspaceFileBackups(): Promise { + return this.impl.getWorkspaceFileBackups(); + } + + resolveBackupContent(backup: Uri): Promise { + return this.impl.resolveBackupContent(backup); + } + + toBackupResource(resource: Uri): Uri { + return this.impl.toBackupResource(resource); + } +} + +class BackupFileServiceImpl implements IBackupFileService { + private static readonly META_MARKER = '\n'; _serviceBrand: any; @@ -170,7 +229,7 @@ export class BackupFileService implements IBackupFileService { } return this.ioOperationQueues.queueFor(backupResource).queue(() => { - const preamble = `${resource.toString()}${BackupFileService.META_MARKER}`; + const preamble = `${resource.toString()}${BackupFileServiceImpl.META_MARKER}`; // Update content with value return this.fileService.updateContent(backupResource, new BackupSnapshot(content, preamble), BACKUP_FILE_UPDATE_OPTIONS).then(() => model.add(backupResource, versionId)); @@ -202,7 +261,7 @@ export class BackupFileService implements IBackupFileService { model.get().forEach(fileBackup => { readPromises.push( - readToMatchingString(fileBackup.fsPath, BackupFileService.META_MARKER, 2000, 10000).then(Uri.parse) + readToMatchingString(fileBackup.fsPath, BackupFileServiceImpl.META_MARKER, 2000, 10000).then(Uri.parse) ); }); @@ -217,7 +276,7 @@ export class BackupFileService implements IBackupFileService { let metaFound = false; const metaPreambleFilter = (chunk: string) => { if (!metaFound && chunk) { - const metaIndex = chunk.indexOf(BackupFileService.META_MARKER); + const metaIndex = chunk.indexOf(BackupFileServiceImpl.META_MARKER); if (metaIndex === -1) { return ''; // meta not yet found, return empty string } @@ -292,7 +351,6 @@ export class InMemoryBackupFileService implements IBackupFileService { toBackupResource(resource: Uri): Uri { return Uri.file(path.join(resource.scheme, hashPath(resource))); } - } /* @@ -302,3 +360,5 @@ export function hashPath(resource: Uri): string { const str = resource.scheme === Schemas.file ? resource.fsPath : resource.toString(); return crypto.createHash('md5').update(str).digest('hex'); } + +registerSingleton(IBackupFileService, BackupFileService); \ No newline at end of file diff --git a/src/vs/workbench/services/backup/test/electron-browser/backupFileService.test.ts b/src/vs/workbench/services/backup/test/electron-browser/backupFileService.test.ts index 5f1ea987873..d2d9a82d2c2 100644 --- a/src/vs/workbench/services/backup/test/electron-browser/backupFileService.test.ts +++ b/src/vs/workbench/services/backup/test/electron-browser/backupFileService.test.ts @@ -13,7 +13,7 @@ import { URI as Uri } from 'vs/base/common/uri'; import { BackupFileService, BackupFilesModel, hashPath } from 'vs/workbench/services/backup/node/backupFileService'; import { FileService } from 'vs/workbench/services/files/node/fileService'; import { TextModel, createTextBufferFactory } from 'vs/editor/common/model/textModel'; -import { TestContextService, TestTextResourceConfigurationService, TestLifecycleService, TestEnvironmentService, TestStorageService } from 'vs/workbench/test/workbenchTestServices'; +import { TestContextService, TestTextResourceConfigurationService, TestLifecycleService, TestEnvironmentService, TestStorageService, TestWindowService } from 'vs/workbench/test/workbenchTestServices'; import { getRandomTestPath } from 'vs/base/test/node/testUtils'; import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService'; import { Workspace, toWorkspaceFolders } from 'vs/platform/workspace/common/workspace'; @@ -21,6 +21,7 @@ import { TestConfigurationService } from 'vs/platform/configuration/test/common/ import { DefaultEndOfLine } from 'vs/editor/common/model'; import { snapshotToString } from 'vs/platform/files/common/files'; import { Schemas } from 'vs/base/common/network'; +import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; const parentDir = getRandomTestPath(os.tmpdir(), 'vsctests', 'backupfileservice'); const backupHome = path.join(parentDir, 'Backups'); @@ -35,11 +36,28 @@ const fooBackupPath = path.join(workspaceBackupPath, 'file', hashPath(fooFile)); const barBackupPath = path.join(workspaceBackupPath, 'file', hashPath(barFile)); const untitledBackupPath = path.join(workspaceBackupPath, 'untitled', hashPath(untitledFile)); +class TestBackupWindowService extends TestWindowService { + + private config: IWindowConfiguration; + + constructor(workspaceBackupPath: string) { + super(); + + this.config = Object.create(null); + this.config.backupPath = workspaceBackupPath; + } + + getConfiguration(): IWindowConfiguration { + return this.config; + } +} + class TestBackupFileService extends BackupFileService { constructor(workspace: Uri, backupHome: string, workspacesJsonPath: string) { const fileService = new FileService(new TestContextService(new Workspace(workspace.fsPath, toWorkspaceFolders([{ path: workspace.fsPath }]))), TestEnvironmentService, new TestTextResourceConfigurationService(), new TestConfigurationService(), new TestLifecycleService(), new TestStorageService(), new TestNotificationService(), { disableWatcher: true }); + const windowService = new TestBackupWindowService(workspaceBackupPath); - super(workspaceBackupPath, fileService); + super(windowService, fileService); } public toBackupResource(resource: Uri): Uri { diff --git a/src/vs/workbench/services/broadcast/electron-browser/broadcastService.ts b/src/vs/workbench/services/broadcast/electron-browser/broadcastService.ts index a22264520c3..8c1b3c9f6aa 100644 --- a/src/vs/workbench/services/broadcast/electron-browser/broadcastService.ts +++ b/src/vs/workbench/services/broadcast/electron-browser/broadcastService.ts @@ -8,6 +8,8 @@ import { Event, Emitter } from 'vs/base/common/event'; import { ipcRenderer as ipc } from 'electron'; import { ILogService } from 'vs/platform/log/common/log'; import { Disposable } from 'vs/base/common/lifecycle'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IWindowService } from 'vs/platform/windows/common/windows'; export const IBroadcastService = createDecorator('broadcastService'); @@ -30,12 +32,16 @@ export class BroadcastService extends Disposable implements IBroadcastService { private readonly _onBroadcast: Emitter = this._register(new Emitter()); get onBroadcast(): Event { return this._onBroadcast.event; } + private windowId: number; + constructor( - private windowId: number, + @IWindowService readonly windowService: IWindowService, @ILogService private readonly logService: ILogService ) { super(); + this.windowId = windowService.getCurrentWindowId(); + this.registerListeners(); } @@ -55,4 +61,6 @@ export class BroadcastService extends Disposable implements IBroadcastService { payload: b.payload }); } -} \ No newline at end of file +} + +registerSingleton(IBroadcastService, BroadcastService, true); \ No newline at end of file diff --git a/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts b/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts index 28142311db4..f532d978e35 100644 --- a/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts +++ b/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts @@ -13,7 +13,7 @@ import { Range } from 'vs/editor/common/core/range'; import { EndOfLineSequence, IIdentifiedSingleEditOperation, ITextModel } from 'vs/editor/common/model'; import { isResourceFileEdit, isResourceTextEdit, ResourceFileEdit, ResourceTextEdit, WorkspaceEdit } from 'vs/editor/common/modes'; import { IModelService } from 'vs/editor/common/services/modelService'; -import { ITextEditorModel, ITextModelService } from 'vs/editor/common/services/resolverService'; +import { ITextModelService, IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService'; import { localize } from 'vs/nls'; import { IFileService } from 'vs/platform/files/common/files'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; @@ -53,7 +53,7 @@ class ModelEditTask implements IDisposable { private _expectedModelVersionId: number | undefined; protected _newEol: EndOfLineSequence; - constructor(private readonly _modelReference: IReference) { + constructor(private readonly _modelReference: IReference) { this._model = this._modelReference.object.textEditorModel; this._edits = []; } @@ -115,7 +115,7 @@ class EditorEditTask extends ModelEditTask { private _editor: ICodeEditor; - constructor(modelReference: IReference, editor: ICodeEditor) { + constructor(modelReference: IReference, editor: ICodeEditor) { super(modelReference); this._editor = editor; } @@ -410,6 +410,10 @@ export class BulkEditService implements IBulkEditService { } } + if (codeEditor && codeEditor.getConfiguration().readOnly) { + // If the code editor is readonly still allow bulk edits to be applied #68549 + codeEditor = undefined; + } const bulkEdit = new BulkEdit(codeEditor, options.progress, this._logService, this._textModelService, this._fileService, this._textFileService, this._labelService, this._configurationService); bulkEdit.add(edits); diff --git a/src/vs/workbench/services/configuration/common/configuration.ts b/src/vs/workbench/services/configuration/common/configuration.ts index 344bfa78905..ad2658aa60e 100644 --- a/src/vs/workbench/services/configuration/common/configuration.ts +++ b/src/vs/workbench/services/configuration/common/configuration.ts @@ -3,18 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; - export const FOLDER_CONFIG_FOLDER_NAME = '.vscode'; export const FOLDER_SETTINGS_NAME = 'settings'; export const FOLDER_SETTINGS_PATH = `${FOLDER_CONFIG_FOLDER_NAME}/${FOLDER_SETTINGS_NAME}.json`; -export const IWorkspaceConfigurationService = createDecorator('configurationService'); - -export interface IWorkspaceConfigurationService extends IConfigurationService { -} - export const defaultSettingsSchemaId = 'vscode://schemas/settings/default'; export const userSettingsSchemaId = 'vscode://schemas/settings/user'; export const workspaceSettingsSchemaId = 'vscode://schemas/settings/workspace'; diff --git a/src/vs/workbench/services/configuration/node/configuration.ts b/src/vs/workbench/services/configuration/node/configuration.ts index cf439e7a9ef..b10f358735a 100644 --- a/src/vs/workbench/services/configuration/node/configuration.ts +++ b/src/vs/workbench/services/configuration/node/configuration.ts @@ -12,7 +12,7 @@ import * as errors from 'vs/base/common/errors'; import * as collections from 'vs/base/common/collections'; import { Disposable, IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; import { RunOnceScheduler, Delayer } from 'vs/base/common/async'; -import { FileChangeType, FileChangesEvent, IContent, IFileService, FileListener } from 'vs/platform/files/common/files'; +import { FileChangeType, FileChangesEvent, IContent, IFileService } from 'vs/platform/files/common/files'; import { ConfigurationModel } from 'vs/platform/configuration/common/configurationModels'; import { WorkspaceConfigurationModelParser, FolderSettingsModelParser, StandaloneConfigurationModelParser } from 'vs/workbench/services/configuration/common/configurationModels'; import { FOLDER_SETTINGS_PATH, TASKS_CONFIGURATION_KEY, FOLDER_SETTINGS_NAME, LAUNCH_CONFIGURATION_KEY } from 'vs/workbench/services/configuration/common/configuration'; @@ -219,21 +219,33 @@ class FileServiceBasedWorkspaceConfiguration extends AbstractWorkspaceConfigurat private workspaceConfig: URI | null = null; private readonly reloadConfigurationScheduler: RunOnceScheduler; - private fileListener: FileListener | null; - private fileListenerDisposables: IDisposable[] = []; constructor(private fileService: IFileService, from?: IWorkspaceConfiguration) { super(from); this.workspaceConfig = from && from.workspaceIdentifier ? from.workspaceIdentifier.configPath : null; + this._register(fileService.onFileChanges(e => this.handleWorkspaceFileEvents(e))); this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this._onDidChange.fire(), 50)); - this.listenToWorkspaceConfigurationFile(); - this._register(toDisposable(() => dispose(this.fileListenerDisposables))); + this.watchWorkspaceConfigurationFile(); + this._register(toDisposable(() => this.unWatchWorkspaceConfigurtionFile())); + } + + private watchWorkspaceConfigurationFile(): void { + if (this.workspaceConfig) { + this.fileService.watchFileChanges(this.workspaceConfig); + } + } + + private unWatchWorkspaceConfigurtionFile(): void { + if (this.workspaceConfig) { + this.fileService.unwatchFileChanges(this.workspaceConfig); + } } protected loadWorkspaceConfigurationContents(workspaceIdentifier: IWorkspaceIdentifier): Promise { if (!(this.workspaceConfig && resources.isEqual(this.workspaceConfig, workspaceIdentifier.configPath))) { + this.unWatchWorkspaceConfigurtionFile(); this.workspaceConfig = workspaceIdentifier.configPath; - this.listenToWorkspaceConfigurationFile(); + this.watchWorkspaceConfigurationFile(); } return this.fileService.resolveContent(this.workspaceConfig) .then(content => content.value, e => { @@ -242,16 +254,19 @@ class FileServiceBasedWorkspaceConfiguration extends AbstractWorkspaceConfigurat }); } - private listenToWorkspaceConfigurationFile(): void { - if (this.fileListener) { - this.fileListenerDisposables = dispose(this.fileListenerDisposables); - this.fileListener = null; - } + private handleWorkspaceFileEvents(event: FileChangesEvent): void { if (this.workspaceConfig) { - this.fileListener = new FileListener(this.workspaceConfig, this.fileService); - this.fileListenerDisposables.push(this.fileListener); - this.fileListener.watch(); - this.fileListener.onDidContentChange(() => this.reloadConfigurationScheduler.schedule(), this, this.fileListenerDisposables); + const events = event.changes; + + let affectedByChanges = false; + // Find changes that affect workspace file + for (let i = 0, len = events.length; i < len && !affectedByChanges; i++) { + affectedByChanges = resources.isEqual(this.workspaceConfig, events[i].resource); + } + + if (affectedByChanges) { + this.reloadConfigurationScheduler.schedule(); + } } } } diff --git a/src/vs/workbench/services/configuration/node/configurationEditingService.ts b/src/vs/workbench/services/configuration/node/configurationEditingService.ts index 6943c5b3819..d559ff78819 100644 --- a/src/vs/workbench/services/configuration/node/configurationEditingService.ts +++ b/src/vs/workbench/services/configuration/node/configurationEditingService.ts @@ -22,7 +22,7 @@ import { ITextFileService } from 'vs/workbench/services/textfile/common/textfile import { IConfigurationService, IConfigurationOverrides, keyFromOverrideIdentifier, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { FOLDER_SETTINGS_PATH, WORKSPACE_STANDALONE_CONFIGURATIONS, TASKS_CONFIGURATION_KEY, LAUNCH_CONFIGURATION_KEY } from 'vs/workbench/services/configuration/common/configuration'; import { IFileService } from 'vs/platform/files/common/files'; -import { ITextModelService, ITextEditorModel } from 'vs/editor/common/services/resolverService'; +import { ITextModelService, IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService'; import { OVERRIDE_PROPERTY_PATTERN, IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ITextModel } from 'vs/editor/common/model'; @@ -106,7 +106,7 @@ export interface IConfigurationEditingOptions { interface IConfigurationEditOperation extends IConfigurationValue { target: ConfigurationTarget; jsonPath: json.JSONPath; - resource: URI; + resource?: URI; workspaceStandAloneConfigurationKey?: string; } @@ -158,7 +158,7 @@ export class ConfigurationEditingService { private async writeToBuffer(model: ITextModel, operation: IConfigurationEditOperation, save: boolean): Promise { const edit = this.getEdits(model, operation)[0]; if (edit && this.applyEditsToBuffer(edit, model) && save) { - return this.textFileService.save(operation.resource, { skipSaveParticipants: true /* programmatic change */ }); + return this.textFileService.save(operation.resource!, { skipSaveParticipants: true /* programmatic change */ }); } } @@ -175,7 +175,7 @@ export class ConfigurationEditingService { return false; } - private onError(error: ConfigurationEditingError, operation: IConfigurationEditOperation, scopes: IConfigurationOverrides): void { + private onError(error: ConfigurationEditingError, operation: IConfigurationEditOperation, scopes: IConfigurationOverrides | undefined): void { switch (error.code) { case ConfigurationEditingErrorCode.ERROR_INVALID_CONFIGURATION: this.onInvalidConfigurationError(error, operation); @@ -196,7 +196,7 @@ export class ConfigurationEditingService { this.notificationService.prompt(Severity.Error, error.message, [{ label: openStandAloneConfigurationActionLabel, - run: () => this.openFile(operation.resource) + run: () => this.openFile(operation.resource!) }] ); } else { @@ -209,7 +209,7 @@ export class ConfigurationEditingService { } } - private onConfigurationFileDirtyError(error: ConfigurationEditingError, operation: IConfigurationEditOperation, scopes: IConfigurationOverrides): void { + private onConfigurationFileDirtyError(error: ConfigurationEditingError, operation: IConfigurationEditOperation, scopes: IConfigurationOverrides | undefined): void { const openStandAloneConfigurationActionLabel = operation.workspaceStandAloneConfigurationKey === TASKS_CONFIGURATION_KEY ? nls.localize('openTasksConfiguration', "Open Tasks Configuration") : operation.workspaceStandAloneConfigurationKey === LAUNCH_CONFIGURATION_KEY ? nls.localize('openLaunchConfiguration', "Open Launch Configuration") : null; @@ -218,13 +218,13 @@ export class ConfigurationEditingService { [{ label: nls.localize('saveAndRetry', "Save and Retry"), run: () => { - const key = operation.key ? `${operation.workspaceStandAloneConfigurationKey}.${operation.key}` : operation.workspaceStandAloneConfigurationKey; + const key = operation.key ? `${operation.workspaceStandAloneConfigurationKey}.${operation.key}` : operation.workspaceStandAloneConfigurationKey!; this.writeConfiguration(operation.target, { key, value: operation.value }, { force: true, scopes }); } }, { label: openStandAloneConfigurationActionLabel, - run: () => this.openFile(operation.resource) + run: () => this.openFile(operation.resource!) }] ); } else { @@ -296,7 +296,13 @@ export class ConfigurationEditingService { case ConfigurationTarget.WORKSPACE: return nls.localize('errorInvalidConfigurationWorkspace', "Unable to write into workspace settings. Please open the workspace settings to correct errors/warnings in the file and try again."); case ConfigurationTarget.WORKSPACE_FOLDER: - const workspaceFolderName = this.contextService.getWorkspaceFolder(operation.resource).name; + let workspaceFolderName: string = '<>'; + if (operation.resource) { + const folder = this.contextService.getWorkspaceFolder(operation.resource); + if (folder) { + workspaceFolderName = folder.name; + } + } return nls.localize('errorInvalidConfigurationFolder', "Unable to write into folder settings. Please open the '{0}' folder settings to correct errors/warnings in it and try again.", workspaceFolderName); } return ''; @@ -314,7 +320,13 @@ export class ConfigurationEditingService { case ConfigurationTarget.WORKSPACE: return nls.localize('errorConfigurationFileDirtyWorkspace', "Unable to write into workspace settings because the file is dirty. Please save the workspace settings file first and then try again."); case ConfigurationTarget.WORKSPACE_FOLDER: - const workspaceFolderName = this.contextService.getWorkspaceFolder(operation.resource).name; + let workspaceFolderName: string = '<>'; + if (operation.resource) { + const folder = this.contextService.getWorkspaceFolder(operation.resource); + if (folder) { + workspaceFolderName = folder.name; + } + } return nls.localize('errorConfigurationFileDirtyFolder', "Unable to write into folder settings because the file is dirty. Please save the '{0}' folder settings file first and then try again.", workspaceFolderName); } return ''; @@ -352,7 +364,7 @@ export class ConfigurationEditingService { return setProperty(model.getValue(), jsonPath, value, { tabSize, insertSpaces, eol }); } - private async resolveModelReference(resource: URI): Promise> { + private async resolveModelReference(resource: URI): Promise> { const exists = await this.fileService.existsFile(resource); if (!exists) { await this.fileService.updateContent(resource, '{}', { encoding: encoding.UTF8 }); @@ -371,7 +383,7 @@ export class ConfigurationEditingService { return parseErrors.length > 0; } - private resolveAndValidate(target: ConfigurationTarget, operation: IConfigurationEditOperation, checkDirty: boolean, overrides: IConfigurationOverrides): Promise> { + private resolveAndValidate(target: ConfigurationTarget, operation: IConfigurationEditOperation, checkDirty: boolean, overrides: IConfigurationOverrides): Promise> { // Any key must be a known setting from the registry (unless this is a standalone config) if (!operation.workspaceStandAloneConfigurationKey) { @@ -420,6 +432,10 @@ export class ConfigurationEditingService { } } + if (!operation.resource) { + return this.reject(ConfigurationEditingErrorCode.ERROR_INVALID_FOLDER_TARGET, target, operation); + } + return this.resolveModelReference(operation.resource) .then(reference => { const model = reference.object.textEditorModel; @@ -447,14 +463,14 @@ export class ConfigurationEditingService { // Check for prefix if (config.key === key) { const jsonPath = this.isWorkspaceConfigurationResource(resource) ? [key] : []; - return { key: jsonPath[jsonPath.length - 1], jsonPath, value: config.value, resource, workspaceStandAloneConfigurationKey: key, target }; + return { key: jsonPath[jsonPath.length - 1], jsonPath, value: config.value, resource: resource || undefined, workspaceStandAloneConfigurationKey: key, target }; } // Check for prefix. const keyPrefix = `${key}.`; if (config.key.indexOf(keyPrefix) === 0) { const jsonPath = this.isWorkspaceConfigurationResource(resource) ? [key, config.key.substr(keyPrefix.length)] : [config.key.substr(keyPrefix.length)]; - return { key: jsonPath[jsonPath.length - 1], jsonPath, value: config.value, resource, workspaceStandAloneConfigurationKey: key, target }; + return { key: jsonPath[jsonPath.length - 1], jsonPath, value: config.value, resource: resource || undefined, workspaceStandAloneConfigurationKey: key, target }; } } } @@ -469,15 +485,15 @@ export class ConfigurationEditingService { if (this.isWorkspaceConfigurationResource(resource)) { jsonPath = ['settings', ...jsonPath]; } - return { key, jsonPath, value: config.value, resource, target }; + return { key, jsonPath, value: config.value, resource: resource || undefined, target }; } - private isWorkspaceConfigurationResource(resource: URI): boolean { + private isWorkspaceConfigurationResource(resource: URI | null | undefined): boolean { const workspace = this.contextService.getWorkspace(); return !!(workspace.configuration && resource && workspace.configuration.fsPath === resource.fsPath); } - private getConfigurationFileResource(target: ConfigurationTarget, relativePath: string, resource: URI): URI { + private getConfigurationFileResource(target: ConfigurationTarget, relativePath: string, resource: URI | null | undefined): URI | null { if (target === ConfigurationTarget.USER) { return URI.file(this.environmentService.appSettingsPath); } @@ -489,7 +505,7 @@ export class ConfigurationEditingService { if (target === ConfigurationTarget.WORKSPACE) { if (workbenchState === WorkbenchState.WORKSPACE) { - return workspace.configuration; + return workspace.configuration || null; } if (workbenchState === WorkbenchState.FOLDER) { return workspace.folders[0].toResource(relativePath); diff --git a/src/vs/workbench/services/configuration/node/configurationService.ts b/src/vs/workbench/services/configuration/node/configurationService.ts index 1e90cfd6285..61a17082dca 100644 --- a/src/vs/workbench/services/configuration/node/configurationService.ts +++ b/src/vs/workbench/services/configuration/node/configurationService.ts @@ -9,7 +9,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { ResourceMap } from 'vs/base/common/map'; import { equals, deepClone } from 'vs/base/common/objects'; import { Disposable } from 'vs/base/common/lifecycle'; -import { Queue } from 'vs/base/common/async'; +import { Queue, Barrier } from 'vs/base/common/async'; import { writeFile } from 'vs/base/node/pfs'; import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import { IWorkspaceContextService, Workspace, WorkbenchState, IWorkspaceFolder, toWorkspaceFolders, IWorkspaceFoldersChangeEvent, WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; @@ -17,9 +17,9 @@ import { isLinux } from 'vs/base/common/platform'; import { IFileService } from 'vs/platform/files/common/files'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ConfigurationChangeEvent, ConfigurationModel, DefaultConfigurationModel } from 'vs/platform/configuration/common/configurationModels'; -import { IConfigurationChangeEvent, ConfigurationTarget, IConfigurationOverrides, keyFromOverrideIdentifier, isConfigurationOverrides, IConfigurationData } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationChangeEvent, ConfigurationTarget, IConfigurationOverrides, keyFromOverrideIdentifier, isConfigurationOverrides, IConfigurationData, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { Configuration, WorkspaceConfigurationChangeEvent, AllKeysConfigurationChangeEvent } from 'vs/workbench/services/configuration/common/configurationModels'; -import { IWorkspaceConfigurationService, FOLDER_CONFIG_FOLDER_NAME, defaultSettingsSchemaId, userSettingsSchemaId, workspaceSettingsSchemaId, folderSettingsSchemaId } from 'vs/workbench/services/configuration/common/configuration'; +import { FOLDER_CONFIG_FOLDER_NAME, defaultSettingsSchemaId, userSettingsSchemaId, workspaceSettingsSchemaId, folderSettingsSchemaId } from 'vs/workbench/services/configuration/common/configuration'; import { Registry } from 'vs/platform/registry/common/platform'; import { IConfigurationNode, IConfigurationRegistry, Extensions, IConfigurationPropertySchema, allSettings, windowSettings, resourceSettings, applicationSettings } from 'vs/platform/configuration/common/configurationRegistry'; import { IWorkspaceIdentifier, isWorkspaceIdentifier, IStoredWorkspaceFolder, isStoredWorkspaceFolder, IWorkspaceFolderCreationData, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, IWorkspaceInitializationPayload, isSingleFolderWorkspaceInitializationPayload, ISingleFolderWorkspaceInitializationPayload, IEmptyWorkspaceInitializationPayload, useSlashForPath, getStoredWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces'; @@ -35,14 +35,14 @@ import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema'; import { localize } from 'vs/nls'; import { isEqual, dirname } from 'vs/base/common/resources'; import { mark } from 'vs/base/common/performance'; +import { Schemas } from 'vs/base/common/network'; -export class WorkspaceService extends Disposable implements IWorkspaceConfigurationService, IWorkspaceContextService { +export class WorkspaceService extends Disposable implements IConfigurationService, IWorkspaceContextService { public _serviceBrand: any; private workspace: Workspace; - private resolvePromise: Promise; - private resolveCallback: () => void; + private completeWorkspaceBarrier: Barrier; private _configuration: Configuration; private defaultConfiguration: DefaultConfigurationModel; private userConfiguration: UserConfiguration; @@ -70,7 +70,7 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat constructor(private environmentService: IEnvironmentService, private workspaceSettingsRootFolder: string = FOLDER_CONFIG_FOLDER_NAME) { super(); - this.resolvePromise = new Promise(c => this.resolveCallback = c); + this.completeWorkspaceBarrier = new Barrier(); this.defaultConfiguration = new DefaultConfigurationModel(); this.userConfiguration = this._register(new UserConfiguration(environmentService.appSettingsPath)); this.workspaceConfiguration = this._register(new WorkspaceConfiguration(environmentService)); @@ -86,7 +86,7 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat // Workspace Context Service Impl public getCompleteWorkspace(): Promise { - return this.resolvePromise.then(() => this.getWorkspace()); + return this.completeWorkspaceBarrier.wait().then(() => this.getWorkspace()); } public getWorkspace(): Workspace { @@ -304,7 +304,7 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat for (const workspaceFolder of changedWorkspaceFolders) { this.onWorkspaceFolderConfigurationChanged(workspaceFolder); } - this.resolveCallback(); + this.releaseWorkspaceBarrier(); }); } @@ -331,7 +331,11 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat const workspaceConfigPath = workspaceIdentifier.configPath; const workspaceFolders = toWorkspaceFolders(this.workspaceConfiguration.getFolders(), dirname(workspaceConfigPath)); const workspaceId = workspaceIdentifier.id; - return new Workspace(workspaceId, workspaceFolders, workspaceConfigPath); + const workspace = new Workspace(workspaceId, workspaceFolders, workspaceConfigPath); + if (workspace.configuration.scheme === Schemas.file) { + this.releaseWorkspaceBarrier(); // Release barrier as workspace is complete because it is from disk. + } + return workspace; }); } @@ -345,11 +349,21 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat configuredFolders = [{ uri: folder.toString() }]; } - return Promise.resolve(new Workspace(singleFolder.id, toWorkspaceFolders(configuredFolders))); + const workspace = new Workspace(singleFolder.id, toWorkspaceFolders(configuredFolders)); + this.releaseWorkspaceBarrier(); // Release barrier as workspace is complete because it is single folder. + return Promise.resolve(workspace); } private createEmptyWorkspace(emptyWorkspace: IEmptyWorkspaceInitializationPayload): Promise { - return Promise.resolve(new Workspace(emptyWorkspace.id)); + const workspace = new Workspace(emptyWorkspace.id); + this.releaseWorkspaceBarrier(); // Release barrier as workspace is complete because it is an empty workspace. + return Promise.resolve(workspace); + } + + private releaseWorkspaceBarrier(): void { + if (!this.completeWorkspaceBarrier.isOpen()) { + this.completeWorkspaceBarrier.open(); + } } private updateWorkspaceAndInitializeConfiguration(workspace: Workspace, postInitialisationTask: () => void): Promise { diff --git a/src/vs/workbench/services/configuration/node/jsonEditingService.ts b/src/vs/workbench/services/configuration/node/jsonEditingService.ts index c02b276b524..5c08f612786 100644 --- a/src/vs/workbench/services/configuration/node/jsonEditingService.ts +++ b/src/vs/workbench/services/configuration/node/jsonEditingService.ts @@ -17,9 +17,10 @@ import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IFileService } from 'vs/platform/files/common/files'; -import { ITextModelService, ITextEditorModel } from 'vs/editor/common/services/resolverService'; +import { ITextModelService, IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService'; import { IJSONEditingService, IJSONValue, JSONEditingError, JSONEditingErrorCode } from 'vs/workbench/services/configuration/common/jsonEditing'; import { ITextModel } from 'vs/editor/common/model'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; export class JSONEditingService implements IJSONEditingService { @@ -83,7 +84,7 @@ export class JSONEditingService implements IJSONEditingService { return setProperty(model.getValue(), [key], value, { tabSize, insertSpaces, eol }); } - private async resolveModelReference(resource: URI): Promise> { + private async resolveModelReference(resource: URI): Promise> { const exists = await this.fileService.existsFile(resource); if (!exists) { await this.fileService.updateContent(resource, '{}', { encoding: encoding.UTF8 }); @@ -97,18 +98,18 @@ export class JSONEditingService implements IJSONEditingService { return parseErrors.length > 0; } - private resolveAndValidate(resource: URI, checkDirty: boolean): Promise> { + private resolveAndValidate(resource: URI, checkDirty: boolean): Promise> { return this.resolveModelReference(resource) .then(reference => { const model = reference.object.textEditorModel; if (this.hasParseErrors(model)) { - return this.reject>(JSONEditingErrorCode.ERROR_INVALID_FILE); + return this.reject>(JSONEditingErrorCode.ERROR_INVALID_FILE); } // Target cannot be dirty if not writing into buffer if (checkDirty && this.textFileService.isDirty(resource)) { - return this.reject>(JSONEditingErrorCode.ERROR_FILE_DIRTY); + return this.reject>(JSONEditingErrorCode.ERROR_FILE_DIRTY); } return reference; }); @@ -131,3 +132,5 @@ export class JSONEditingService implements IJSONEditingService { } } } + +registerSingleton(IJSONEditingService, JSONEditingService, true); \ No newline at end of file diff --git a/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts b/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts index 6d45e9d1c3d..d7e050e7a94 100644 --- a/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts +++ b/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts @@ -31,7 +31,6 @@ import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { TextModelResolverService } from 'vs/workbench/services/textmodelResolver/common/textModelResolverService'; import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing'; import { JSONEditingService } from 'vs/workbench/services/configuration/node/jsonEditingService'; -import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { createHash } from 'crypto'; import { Emitter, Event } from 'vs/base/common/event'; import { Schemas } from 'vs/base/common/network'; @@ -140,10 +139,66 @@ suite('WorkspaceContextService - Folder', () => { test('isCurrentWorkspace() => false', () => { assert.ok(!workspaceContextService.isCurrentWorkspace(URI.file(workspaceResource + 'abc'))); }); + + test('workspace is complete', () => workspaceContextService.getCompleteWorkspace()); }); suite('WorkspaceContextService - Workspace', () => { + let parentResource: string, testObject: WorkspaceService, instantiationService: TestInstantiationService; + + setup(() => { + return setUpWorkspace(['a', 'b']) + .then(({ parentDir, configPath }) => { + + parentResource = parentDir; + + const environmentService = new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, path.join(parentDir, 'settings.json')); + const workspaceService = new WorkspaceService(environmentService); + + instantiationService = workbenchInstantiationService(); + instantiationService.stub(IWorkspaceContextService, workspaceService); + instantiationService.stub(IConfigurationService, workspaceService); + instantiationService.stub(IEnvironmentService, environmentService); + + return workspaceService.initialize(getWorkspaceIdentifier(configPath)).then(() => { + workspaceService.acquireInstantiationService(instantiationService); + testObject = workspaceService; + }); + }); + }); + + teardown(() => { + if (testObject) { + (testObject).dispose(); + } + if (parentResource) { + return pfs.del(parentResource, os.tmpdir()); + } + return undefined; + }); + + test('workspace folders', () => { + const actual = testObject.getWorkspace().folders; + + assert.equal(actual.length, 2); + assert.equal(path.basename(actual[0].uri.fsPath), 'a'); + assert.equal(path.basename(actual[1].uri.fsPath), 'b'); + }); + + test('getWorkbenchState()', () => { + const actual = testObject.getWorkbenchState(); + + assert.equal(actual, WorkbenchState.WORKSPACE); + }); + + + test('workspace is complete', () => testObject.getCompleteWorkspace()); + +}); + +suite('WorkspaceContextService - Workspace Editing', () => { + let parentResource: string, testObject: WorkspaceService, instantiationService: TestInstantiationService, fileChangeEvent: Emitter = new Emitter(); setup(() => { @@ -186,14 +241,6 @@ suite('WorkspaceContextService - Workspace', () => { return undefined; }); - test('workspace folders', () => { - const actual = testObject.getWorkspace().folders; - - assert.equal(actual.length, 2); - assert.equal(path.basename(actual[0].uri.fsPath), 'a'); - assert.equal(path.basename(actual[1].uri.fsPath), 'b'); - }); - test('add folders', () => { const workspaceDir = path.dirname(testObject.getWorkspace().folders[0].uri.fsPath); return testObject.addFolders([{ uri: URI.file(path.join(workspaceDir, 'd')) }, { uri: URI.file(path.join(workspaceDir, 'c')) }]) @@ -649,7 +696,7 @@ suite('WorkspaceService - Initialization', () => { suite('WorkspaceConfigurationService - Folder', () => { - let workspaceName = `testWorkspace${uuid.generateUuid()}`, parentResource: string, workspaceDir: string, testObject: IWorkspaceConfigurationService, globalSettingsFile: string; + let workspaceName = `testWorkspace${uuid.generateUuid()}`, parentResource: string, workspaceDir: string, testObject: IConfigurationService, globalSettingsFile: string; const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); suiteSetup(() => { @@ -935,7 +982,7 @@ suite('WorkspaceConfigurationService - Folder', () => { suite('WorkspaceConfigurationService-Multiroot', () => { - let parentResource: string, workspaceContextService: IWorkspaceContextService, environmentService: IEnvironmentService, jsonEditingServce: IJSONEditingService, testObject: IWorkspaceConfigurationService; + let parentResource: string, workspaceContextService: IWorkspaceContextService, environmentService: IEnvironmentService, jsonEditingServce: IJSONEditingService, testObject: IConfigurationService; const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); suiteSetup(() => { diff --git a/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts b/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts index 24b3264415f..71aa1fd6d00 100644 --- a/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts +++ b/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts @@ -6,7 +6,6 @@ import { URI as uri } from 'vs/base/common/uri'; import * as nls from 'vs/nls'; import * as path from 'vs/base/common/path'; -import * as platform from 'vs/base/common/platform'; import * as Types from 'vs/base/common/types'; import { Schemas } from 'vs/base/common/network'; import { toResource } from 'vs/workbench/common/editor'; @@ -20,14 +19,16 @@ import { AbstractVariableResolverService } from 'vs/workbench/services/configura import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import { IQuickInputService, IInputOptions, IQuickPickItem, IPickOptions } from 'vs/platform/quickinput/common/quickInput'; -import { ConfiguredInput } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; +import { ConfiguredInput, IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; +import { IWindowService } from 'vs/platform/windows/common/windows'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; export class ConfigurationResolverService extends AbstractVariableResolverService { static INPUT_OR_COMMAND_VARIABLES_PATTERN = /\${((input|command):(.*?))}/g; constructor( - envVariables: platform.IProcessEnvironment, + @IWindowService windowService: IWindowService, @IEditorService editorService: IEditorService, @IEnvironmentService environmentService: IEnvironmentService, @IConfigurationService private readonly configurationService: IConfigurationService, @@ -79,7 +80,7 @@ export class ConfigurationResolverService extends AbstractVariableResolverServic } return undefined; } - }, envVariables); + }, windowService.getConfiguration().userEnv); } public resolveWithInteractionReplace(folder: IWorkspaceFolder, config: any, section?: string, variables?: IStringDictionary): Promise { @@ -287,4 +288,6 @@ export class ConfigurationResolverService extends AbstractVariableResolverServic } return Promise.reject(new Error(nls.localize('inputVariable.undefinedVariable', "Undefined input variable '{0}' encountered. Remove or define '{0}' to continue.", variable))); } -} \ No newline at end of file +} + +registerSingleton(IConfigurationResolverService, ConfigurationResolverService, true); \ No newline at end of file diff --git a/src/vs/workbench/services/configurationResolver/common/variableResolver.ts b/src/vs/workbench/services/configurationResolver/common/variableResolver.ts index 88d1c2de2f0..82e83a3c2cd 100644 --- a/src/vs/workbench/services/configurationResolver/common/variableResolver.ts +++ b/src/vs/workbench/services/configurationResolver/common/variableResolver.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as paths from 'vs/base/common/path'; +import * as process from 'vs/base/common/process'; import * as types from 'vs/base/common/types'; import * as objects from 'vs/base/common/objects'; import { IStringDictionary } from 'vs/base/common/collections'; @@ -32,7 +33,7 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe constructor( private _context: IVariableResolveContext, - private _envVariables: IProcessEnvironment = process.env + private _envVariables: IProcessEnvironment ) { if (isWindows) { this._envVariables = Object.create(null); 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 af81556238a..4fc62f2dfbc 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 @@ -11,16 +11,18 @@ import { ICommandService } from 'vs/platform/commands/common/commands'; import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; import { ConfigurationResolverService } from 'vs/workbench/services/configurationResolver/browser/configurationResolverService'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; -import { TestEnvironmentService, TestEditorService, TestContextService } from 'vs/workbench/test/workbenchTestServices'; +import { TestEnvironmentService, TestEditorService, TestContextService, TestWindowService } from 'vs/workbench/test/workbenchTestServices'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { Disposable } from 'vs/base/common/lifecycle'; import { IQuickInputService, IQuickPickItem, QuickPickInput, IPickOptions, Omit, IInputOptions, IQuickInputButton, IQuickPick, IInputBox, IQuickNavigateConfiguration } from 'vs/platform/quickinput/common/quickInput'; import { CancellationToken } from 'vs/base/common/cancellation'; import * as Types from 'vs/base/common/types'; +import { IWindowService, IWindowConfiguration } from 'vs/platform/windows/common/windows'; suite('Configuration Resolver Service', () => { let configurationResolverService: IConfigurationResolverService | null; let envVariables: { [key: string]: string } = { key1: 'Value for key1', key2: 'Value for key2' }; + let windowService: IWindowService; let mockCommandService: MockCommandService; let editorService: TestEditorService; let workspace: IWorkspaceFolder; @@ -30,13 +32,14 @@ suite('Configuration Resolver Service', () => { mockCommandService = new MockCommandService(); editorService = new TestEditorService(); quickInputService = new MockQuickInputService(); + windowService = new MockWindowService(envVariables); workspace = { uri: uri.parse('file:///VSCode/workspaceLocation'), name: 'hey', index: 0, toResource: () => null }; - configurationResolverService = new ConfigurationResolverService(envVariables, editorService, TestEnvironmentService, new MockInputsConfigurationService(), mockCommandService, new TestContextService(), quickInputService); + configurationResolverService = new ConfigurationResolverService(windowService, editorService, TestEnvironmentService, new MockInputsConfigurationService(), mockCommandService, new TestContextService(), quickInputService); }); teardown(() => { @@ -120,7 +123,7 @@ suite('Configuration Resolver Service', () => { } }); - let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService, new TestContextService(), quickInputService); + let service = new ConfigurationResolverService(windowService, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService, new TestContextService(), quickInputService); assert.strictEqual(service.resolve(workspace, 'abc ${config:editor.fontFamily} xyz'), 'abc foo xyz'); }); @@ -137,7 +140,7 @@ suite('Configuration Resolver Service', () => { } }); - let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService, new TestContextService(), quickInputService); + let service = new ConfigurationResolverService(windowService, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService, new TestContextService(), quickInputService); assert.strictEqual(service.resolve(workspace, 'abc ${config:editor.fontFamily} ${config:terminal.integrated.fontFamily} xyz'), 'abc foo bar xyz'); }); @@ -154,7 +157,7 @@ suite('Configuration Resolver Service', () => { } }); - let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService, new TestContextService(), quickInputService); + let service = new ConfigurationResolverService(windowService, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService, new TestContextService(), quickInputService); if (platform.isWindows) { assert.strictEqual(service.resolve(workspace, 'abc ${config:editor.fontFamily} ${workspaceFolder} ${env:key1} xyz'), 'abc foo \\VSCode\\workspaceLocation Value for key1 xyz'); } else { @@ -175,7 +178,7 @@ suite('Configuration Resolver Service', () => { } }); - let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService, new TestContextService(), quickInputService); + let service = new ConfigurationResolverService(windowService, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService, new TestContextService(), quickInputService); if (platform.isWindows) { assert.strictEqual(service.resolve(workspace, '${config:editor.fontFamily} ${config:terminal.integrated.fontFamily} ${workspaceFolder} - ${workspaceFolder} ${env:key1} - ${env:key2}'), 'foo bar \\VSCode\\workspaceLocation - \\VSCode\\workspaceLocation Value for key1 - Value for key2'); } else { @@ -209,7 +212,7 @@ suite('Configuration Resolver Service', () => { } }); - let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService, new TestContextService(), quickInputService); + let service = new ConfigurationResolverService(windowService, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService, new TestContextService(), quickInputService); assert.strictEqual(service.resolve(workspace, 'abc ${config:editor.fontFamily} ${config:editor.lineNumbers} ${config:editor.insertSpaces} xyz'), 'abc foo 123 false xyz'); }); @@ -219,7 +222,7 @@ suite('Configuration Resolver Service', () => { editor: {} }); - let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService, new TestContextService(), quickInputService); + let service = new ConfigurationResolverService(windowService, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService, new TestContextService(), quickInputService); assert.strictEqual(service.resolve(workspace, 'abc ${unknownVariable} xyz'), 'abc ${unknownVariable} xyz'); assert.strictEqual(service.resolve(workspace, 'abc ${env:unknownVariable} xyz'), 'abc xyz'); }); @@ -232,7 +235,7 @@ suite('Configuration Resolver Service', () => { } }); - let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService, new TestContextService(), quickInputService); + let service = new ConfigurationResolverService(windowService, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService, new TestContextService(), quickInputService); assert.throws(() => service.resolve(workspace, 'abc ${env} xyz')); assert.throws(() => service.resolve(workspace, 'abc ${env:} xyz')); @@ -611,3 +614,14 @@ class MockInputsConfigurationService extends TestConfigurationService { return configuration; } } + +class MockWindowService extends TestWindowService { + + constructor(private env: platform.IProcessEnvironment) { + super(); + } + + getConfiguration(): IWindowConfiguration { + return { userEnv: this.env } as IWindowConfiguration; + } +} diff --git a/src/vs/workbench/services/decorations/browser/decorationsService.ts b/src/vs/workbench/services/decorations/browser/decorationsService.ts index e0ef2f3814f..5b3859f5fd8 100644 --- a/src/vs/workbench/services/decorations/browser/decorationsService.ts +++ b/src/vs/workbench/services/decorations/browser/decorationsService.ts @@ -18,6 +18,7 @@ import { isFalsyOrWhitespace } from 'vs/base/common/strings'; import { localize } from 'vs/nls'; import { isPromiseCanceledError } from 'vs/base/common/errors'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; class DecorationRule { @@ -353,8 +354,7 @@ export class FileDecorationsService implements IDecorationsService { ); constructor( - @IThemeService themeService: IThemeService, - cleanUpCount: number = 17 + @IThemeService themeService: IThemeService ) { this._decorationStyles = new DecorationStyles(themeService); @@ -362,7 +362,7 @@ export class FileDecorationsService implements IDecorationsService { // css styles that we don't need anymore let count = 0; let reg = this.onDidChangeDecorations(() => { - if (++count % cleanUpCount === 0) { + if (++count % 17 === 0) { this._decorationStyles.cleanUp(this._data.iterator()); } }); @@ -442,3 +442,4 @@ function getColor(theme: ITheme, color: string | undefined) { return 'inherit'; } +registerSingleton(IDecorationsService, FileDecorationsService); \ No newline at end of file diff --git a/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts b/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts index 4d16b57ee49..bf501e161f9 100644 --- a/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts +++ b/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts @@ -22,6 +22,7 @@ import { RemoteFileDialog } from 'vs/workbench/services/dialogs/electron-browser import { WORKSPACE_EXTENSION } from 'vs/platform/workspaces/common/workspaces'; import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; interface IMassagedMessageBoxOptions { @@ -362,7 +363,7 @@ export class FileDialogService implements IFileDialogService { if (urisToOpen) { return this.windowService.openWindow(urisToOpen, { forceNewWindow, forceOpenWorkspaceAsFile }); } - return void 0; + return undefined; }); } @@ -388,4 +389,7 @@ export class FileDialogService implements IFileDialogService { function isUntitledWorkspace(path: URI, environmentService: IEnvironmentService): boolean { return resources.isEqualOrParent(path, environmentService.untitledWorkspacesHome); -} \ No newline at end of file +} + +registerSingleton(IFileDialogService, FileDialogService, true); +registerSingleton(IDialogService, DialogService, true); \ No newline at end of file diff --git a/src/vs/workbench/services/dialogs/electron-browser/remoteFileDialog.ts b/src/vs/workbench/services/dialogs/electron-browser/remoteFileDialog.ts index c8264fa5a65..08f9ea471b8 100644 --- a/src/vs/workbench/services/dialogs/electron-browser/remoteFileDialog.ts +++ b/src/vs/workbench/services/dialogs/electron-browser/remoteFileDialog.ts @@ -90,7 +90,7 @@ export class RemoteFileDialog { newOptions.canSelectFolders = true; newOptions.canSelectFiles = true; return new Promise((resolve) => { - this.pickResource(newOptions).then(folderUri => { + this.pickResource(newOptions, true).then(folderUri => { resolve(folderUri); }); }); @@ -116,12 +116,13 @@ export class RemoteFileDialog { this.scheme = defaultUri ? defaultUri.scheme : (available ? available[0] : Schemas.file); } - private async pickResource(options: IOpenDialogOptions): Promise { + private async pickResource(options: IOpenDialogOptions, isSave: boolean = false): Promise { this.allowFolderSelection = !!options.canSelectFolders; this.allowFileSelection = !!options.canSelectFiles; let homedir: URI = options.defaultUri && options.defaultUri.scheme === REMOTE_HOST_SCHEME ? options.defaultUri : this.workspaceContextService.getWorkspace().folders[0].uri; let trailing: string | undefined; let stat: IFileStat | undefined; + let ext: string = resources.extname(options.defaultUri); if (options.defaultUri) { try { stat = await this.remoteFileService.resolveFile(options.defaultUri); @@ -132,6 +133,16 @@ export class RemoteFileDialog { homedir = resources.dirname(options.defaultUri); trailing = resources.basename(options.defaultUri); } + // append extension + if (isSave && !ext && options.filters) { + for (let i = 0; i < options.filters.length; i++) { + if (options.filters[i].extensions[0] !== '*') { + ext = '.' + options.filters[i].extensions[0]; + trailing = trailing ? trailing + ext : ext; + break; + } + } + } } return new Promise((resolve) => { @@ -150,7 +161,9 @@ export class RemoteFileDialog { } this.filePickBox.onDidTriggerButton(button => { if (button === this.fallbackPickerButton) { - options.availableFileSystems.shift(); + if (options.availableFileSystems) { + options.availableFileSystems.shift(); + } isResolved = true; if (this.requiresTrailing) { this.fileDialogService.showSaveDialog(options).then(result => { @@ -201,7 +214,7 @@ export class RemoteFileDialog { if (value !== this.userValue) { const trimmedPickBoxValue = ((this.filePickBox.value.length > 1) && this.endsWithSlash(this.filePickBox.value)) ? this.filePickBox.value.substr(0, this.filePickBox.value.length - 1) : this.filePickBox.value; const valueUri = this.remoteUriFrom(trimmedPickBoxValue); - if (!resources.isEqual(this.currentFolder, valueUri)) { + if (!resources.isEqual(this.currentFolder, valueUri, true)) { await this.tryUpdateItems(value, valueUri); } this.setActiveItems(value); @@ -219,6 +232,9 @@ export class RemoteFileDialog { this.filePickBox.show(); this.updateItems(homedir, trailing); + if (trailing) { + this.filePickBox.valueSelection = [this.filePickBox.value.length - trailing.length, this.filePickBox.value.length - ext.length]; + } this.userValue = this.filePickBox.value; }); } @@ -240,7 +256,7 @@ export class RemoteFileDialog { // Find resolve value if (this.filePickBox.activeItems.length === 0) { - if (!this.requiresTrailing && resources.isEqual(this.currentFolder, inputUri)) { + if (!this.requiresTrailing && resources.isEqual(this.currentFolder, inputUri, true)) { resolveValue = inputUri; } else if (this.requiresTrailing && statDirname && statDirname.isDirectory) { resolveValue = inputUri; @@ -272,7 +288,7 @@ export class RemoteFileDialog { } private async tryUpdateItems(value: string, valueUri: URI) { - if (this.endsWithSlash(value) || (!resources.isEqual(this.currentFolder, resources.dirname(valueUri)) && resources.isEqualOrParent(this.currentFolder, resources.dirname(valueUri)))) { + if (this.endsWithSlash(value) || (!resources.isEqual(this.currentFolder, resources.dirname(valueUri), true) && resources.isEqualOrParent(this.currentFolder, resources.dirname(valueUri), true))) { let stat: IFileStat | undefined; try { stat = await this.remoteFileService.resolveFile(valueUri); @@ -283,7 +299,7 @@ export class RemoteFileDialog { this.updateItems(valueUri); } else { const inputUriDirname = resources.dirname(valueUri); - if (!resources.isEqual(this.currentFolder, inputUriDirname)) { + if (!resources.isEqual(this.currentFolder, inputUriDirname, true)) { let statWithoutTrailing: IFileStat | undefined; try { statWithoutTrailing = await this.remoteFileService.resolveFile(inputUriDirname); @@ -305,7 +321,7 @@ export class RemoteFileDialog { for (let i = 0; i < this.filePickBox.items.length; i++) { const item = this.filePickBox.items[i]; const itemBasename = resources.basename(item.uri); - if ((itemBasename.length >= inputBasename.length) && (itemBasename.substr(0, inputBasename.length) === inputBasename)) { + if ((itemBasename.length >= inputBasename.length) && (itemBasename.substr(0, inputBasename.length).toLowerCase() === inputBasename.toLowerCase())) { this.filePickBox.activeItems = [item]; this.filePickBox.value = this.filePickBox.value + itemBasename.substr(inputBasename.length); this.filePickBox.valueSelection = [value.length, this.filePickBox.value.length]; @@ -373,7 +389,12 @@ export class RemoteFileDialog { private pathFromUri(uri: URI, endWithSeparator: boolean = false): string { const sep = this.labelService.getSeparator(uri.scheme, uri.authority); - let result = uri.path.replace(/\//g, sep); + let result: string; + if (sep === '/') { + result = uri.fsPath.replace(/\\/g, sep); + } else { + result = uri.fsPath.replace(/\//g, sep); + } if (endWithSeparator && !this.endsWithSlash(result)) { result = result + sep; } @@ -421,7 +442,7 @@ export class RemoteFileDialog { private createBackItem(currFolder: URI): FileQuickPickItem | null { const parentFolder = resources.dirname(currFolder)!; - if (!resources.isEqual(currFolder, parentFolder)) { + if (!resources.isEqual(currFolder, parentFolder, true)) { return { label: '..', uri: resources.dirname(currFolder), isFolder: true }; } return null; @@ -431,9 +452,6 @@ export class RemoteFileDialog { const result: FileQuickPickItem[] = []; const backDir = this.createBackItem(currentFolder); - if (backDir) { - result.push(backDir); - } try { const fileNames = await this.remoteFileService.readFolder(currentFolder); const items = await Promise.all(fileNames.map(fileName => this.createItem(fileName, currentFolder))); @@ -446,12 +464,17 @@ export class RemoteFileDialog { // ignore console.log(e); } - return result.sort((i1, i2) => { + const sorted = result.sort((i1, i2) => { if (i1.isFolder !== i2.isFolder) { return i1.isFolder ? -1 : 1; } return i1.label.localeCompare(i2.label); }); + + if (backDir) { + sorted.unshift(backDir); + } + return sorted; } private async createItem(filename: string, parent: URI): Promise { diff --git a/src/vs/workbench/services/editor/browser/codeEditorService.ts b/src/vs/workbench/services/editor/browser/codeEditorService.ts index 0c98821626b..fe5bb0a5372 100644 --- a/src/vs/workbench/services/editor/browser/codeEditorService.ts +++ b/src/vs/workbench/services/editor/browser/codeEditorService.ts @@ -10,6 +10,8 @@ import { IResourceInput } from 'vs/platform/editor/common/editor'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { TextEditorOptions } from 'vs/workbench/common/editor'; import { ACTIVE_GROUP, IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; +import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; export class CodeEditorService extends CodeEditorServiceImpl { @@ -72,4 +74,6 @@ export class CodeEditorService extends CodeEditorServiceImpl { return null; }); } -} \ No newline at end of file +} + +registerSingleton(ICodeEditorService, CodeEditorService, true); \ No newline at end of file diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index 228dfe7afb4..d04efc8119d 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -54,7 +54,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { private fileInputFactory: IFileInputFactory; private openEditorHandlers: IOpenEditorOverrideHandler[] = []; - private lastActiveEditor: IEditorInput; + private lastActiveEditor: IEditorInput | null; private lastActiveGroupId: GroupIdentifier; constructor( @@ -214,11 +214,11 @@ export class EditorService extends Disposable implements EditorServiceImpl { //#region openEditor() - openEditor(editor: IEditorInput, options?: IEditorOptions | ITextEditorOptions, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; - openEditor(editor: IResourceInput | IUntitledResourceInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; - openEditor(editor: IResourceDiffInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; - openEditor(editor: IResourceSideBySideInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; - openEditor(editor: IEditorInput | IResourceEditor, optionsOrGroup?: IEditorOptions | ITextEditorOptions | IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE, group?: GroupIdentifier): Promise { + openEditor(editor: IEditorInput, options?: IEditorOptions | ITextEditorOptions, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; + openEditor(editor: IResourceInput | IUntitledResourceInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; + openEditor(editor: IResourceDiffInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; + openEditor(editor: IResourceSideBySideInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; + openEditor(editor: IEditorInput | IResourceEditor, optionsOrGroup?: IEditorOptions | ITextEditorOptions | IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE, group?: GroupIdentifier): Promise { // Typed Editor Support if (editor instanceof EditorInput) { @@ -241,7 +241,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { return Promise.resolve(null); } - protected doOpenEditor(group: IEditorGroup, editor: IEditorInput, options?: IEditorOptions): Promise { + protected doOpenEditor(group: IEditorGroup, editor: IEditorInput, options?: IEditorOptions): Promise { return group.openEditor(editor, options); } @@ -377,7 +377,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { //#region getOpend() - getOpened(editor: IResourceInput | IUntitledResourceInput, group?: IEditorGroup | GroupIdentifier): IEditorInput { + getOpened(editor: IResourceInput | IUntitledResourceInput, group?: IEditorGroup | GroupIdentifier): IEditorInput | undefined { return this.doGetOpened(editor); } @@ -626,7 +626,7 @@ export class DelegatingEditorService extends EditorService { this.editorOpenHandler = handler; } - protected doOpenEditor(group: IEditorGroup, editor: IEditorInput, options?: IEditorOptions): Promise { + protected doOpenEditor(group: IEditorGroup, editor: IEditorInput, options?: IEditorOptions): Promise { if (!this.editorOpenHandler) { return super.doOpenEditor(group, editor, options); } diff --git a/src/vs/workbench/services/editor/common/editorGroupsService.ts b/src/vs/workbench/services/editor/common/editorGroupsService.ts index 882c487b795..948c1ec185c 100644 --- a/src/vs/workbench/services/editor/common/editorGroupsService.ts +++ b/src/vs/workbench/services/editor/common/editorGroupsService.ts @@ -9,6 +9,7 @@ import { IEditorInput, IEditor, GroupIdentifier, IEditorInputWithOptions, CloseD import { IEditorOptions, ITextEditorOptions } from 'vs/platform/editor/common/editor'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IActiveEditor } from 'vs/workbench/services/editor/common/editorService'; +import { IDimension } from 'vs/editor/common/editorCommon'; export const IEditorGroupsService = createDecorator('editorGroupsService'); @@ -164,6 +165,11 @@ export interface IEditorGroupsService { */ readonly onDidMoveGroup: Event; + /** + * An event for when the group container is layed out. + */ + readonly onDidLayout: Event; + /** * An active group is the default location for new editors to open. */ diff --git a/src/vs/workbench/services/editor/common/editorService.ts b/src/vs/workbench/services/editor/common/editorService.ts index 18fb4e87975..e9b88ee2c46 100644 --- a/src/vs/workbench/services/editor/common/editorService.ts +++ b/src/vs/workbench/services/editor/common/editorService.ts @@ -36,7 +36,7 @@ export interface IOpenEditorOverride { * If defined, will prevent the opening of an editor and replace the resulting * promise with the provided promise for the openEditor() call. */ - override?: Promise; + override?: Promise; } export interface IActiveEditor extends IEditor { @@ -119,10 +119,10 @@ export interface IEditorService { * @returns the editor that opened or NULL if the operation failed or the editor was not * opened to be active. */ - openEditor(editor: IEditorInput, options?: IEditorOptions | ITextEditorOptions, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; - openEditor(editor: IResourceInput | IUntitledResourceInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; - openEditor(editor: IResourceDiffInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; - openEditor(editor: IResourceSideBySideInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; + openEditor(editor: IEditorInput, options?: IEditorOptions | ITextEditorOptions, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; + openEditor(editor: IResourceInput | IUntitledResourceInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; + openEditor(editor: IResourceDiffInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; + openEditor(editor: IResourceSideBySideInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; /** * Open editors in an editor group. @@ -166,7 +166,7 @@ export interface IEditorService { * * @param group optional to specify a group to check for the editor */ - getOpened(editor: IResourceInput | IUntitledResourceInput, group?: IEditorGroup | GroupIdentifier): IEditorInput; + getOpened(editor: IResourceInput | IUntitledResourceInput, group?: IEditorGroup | GroupIdentifier): IEditorInput | undefined; /** * Allows to override the opening of editors by installing a handler that will diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts index f889ee4c832..43324615a90 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts @@ -33,7 +33,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IInitData } from 'vs/workbench/api/node/extHost.protocol'; -import { MessageType, createMessageOfType, isMessageOfType } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; +import { MessageType, createMessageOfType, isMessageOfType } from 'vs/workbench/services/extensions/node/extensionHostProtocol'; import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; export interface IExtensionHostStarter { diff --git a/src/vs/workbench/services/extensions/electron-browser/inactiveExtensionUrlHandler.ts b/src/vs/workbench/services/extensions/electron-browser/inactiveExtensionUrlHandler.ts index 80700c6aa9a..2713af6dad8 100644 --- a/src/vs/workbench/services/extensions/electron-browser/inactiveExtensionUrlHandler.ts +++ b/src/vs/workbench/services/extensions/electron-browser/inactiveExtensionUrlHandler.ts @@ -17,6 +17,7 @@ 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 { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; const FIVE_MINUTES = 5 * 60 * 1000; const THIRTY_SECONDS = 30 * 1000; @@ -271,3 +272,5 @@ export class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { this.uriBuffer.clear(); } } + +registerSingleton(IExtensionUrlHandler, ExtensionUrlHandler); \ No newline at end of file diff --git a/src/vs/workbench/services/extensions/node/extensionHostProcess.ts b/src/vs/workbench/services/extensions/node/extensionHostProcess.ts index 484cef96b1c..dab5ec2b636 100644 --- a/src/vs/workbench/services/extensions/node/extensionHostProcess.ts +++ b/src/vs/workbench/services/extensions/node/extensionHostProcess.ts @@ -11,7 +11,7 @@ import { IMessagePassingProtocol } from 'vs/base/parts/ipc/node/ipc'; import { Protocol } from 'vs/base/parts/ipc/node/ipc.net'; import product from 'vs/platform/product/node/product'; import { IInitData } from 'vs/workbench/api/node/extHost.protocol'; -import { MessageType, createMessageOfType, isMessageOfType } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; +import { MessageType, createMessageOfType, isMessageOfType } from 'vs/workbench/services/extensions/node/extensionHostProtocol'; import { exit, ExtensionHostMain } from 'vs/workbench/services/extensions/node/extensionHostMain'; // With Electron 2.x and node.js 8.x the "natives" module diff --git a/src/vs/workbench/services/extensions/common/extensionHostProtocol.ts b/src/vs/workbench/services/extensions/node/extensionHostProtocol.ts similarity index 100% rename from src/vs/workbench/services/extensions/common/extensionHostProtocol.ts rename to src/vs/workbench/services/extensions/node/extensionHostProtocol.ts diff --git a/src/vs/workbench/services/extensions/node/proxyResolver.ts b/src/vs/workbench/services/extensions/node/proxyResolver.ts index 98fed1fac42..1643badcc71 100644 --- a/src/vs/workbench/services/extensions/node/proxyResolver.ts +++ b/src/vs/workbench/services/extensions/node/proxyResolver.ts @@ -8,6 +8,7 @@ import * as https from 'https'; import * as nodeurl from 'url'; import { assign } from 'vs/base/common/objects'; +import { endsWith } from 'vs/base/common/strings'; import { IExtHostWorkspaceProvider } from 'vs/workbench/api/node/extHostWorkspace'; import { ExtHostConfigProvider } from 'vs/workbench/api/node/extHostConfiguration'; import { ProxyAgent } from 'vscode-proxy-agent'; @@ -44,15 +45,18 @@ function setupProxyResolution( extHostLogService: ExtHostLogService, mainThreadTelemetry: MainThreadTelemetryShape ) { + const env = process.env; + let settingsProxy = proxyFromConfigURL(configProvider.getConfiguration('http') .get('proxy')); configProvider.onDidChangeConfiguration(e => { settingsProxy = proxyFromConfigURL(configProvider.getConfiguration('http') .get('proxy')); }); - const env = process.env; let envProxy = proxyFromConfigURL(env.https_proxy || env.HTTPS_PROXY || env.http_proxy || env.HTTP_PROXY); // Not standardized. + let envNoProxy = noProxyFromEnv(env.no_proxy || env.NO_PROXY); // Not standardized. + let cacheRolls = 0; let oldCache = new Map(); let cache = new Map(); @@ -90,6 +94,7 @@ function setupProxyResolution( let envCount = 0; let settingsCount = 0; let localhostCount = 0; + let envNoProxyCount = 0; let results: ConnectionResult[] = []; function logEvent() { timeout = undefined; @@ -104,11 +109,12 @@ function setupProxyResolution( "envCount": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "settingsCount": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "localhostCount": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "envNoProxyCount": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "results": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } } */ - mainThreadTelemetry.$publicLog('resolveProxy', { count, duration, errorCount, cacheCount, cacheSize: cache.size, cacheRolls, envCount, settingsCount, localhostCount, results }); - count = duration = errorCount = cacheCount = envCount = settingsCount = localhostCount = 0; + mainThreadTelemetry.$publicLog('resolveProxy', { count, duration, errorCount, cacheCount, cacheSize: cache.size, cacheRolls, envCount, settingsCount, localhostCount, envNoProxyCount, results }); + count = duration = errorCount = cacheCount = envCount = settingsCount = localhostCount = envNoProxyCount = 0; results = []; } @@ -127,6 +133,13 @@ function setupProxyResolution( return; } + if (envNoProxy(hostname, String(parsedUrl.port || (opts.agent).defaultPort))) { + envNoProxyCount++; + callback('DIRECT'); + extHostLogService.trace('ProxyResolver#resolveProxy envNoProxy', url, 'DIRECT'); + return; + } + if (settingsProxy) { settingsCount++; callback(settingsProxy); @@ -214,6 +227,32 @@ function proxyFromConfigURL(configURL: string | undefined) { return undefined; } +function noProxyFromEnv(envValue?: string) { + const value = (envValue || '') + .trim() + .toLowerCase(); + + if (value === '*') { + return () => true; + } + + const filters = value + .split(',') + .map(s => s.trim().split(':', 2)) + .map(([name, port]) => ({ name, port })) + .filter(filter => !!filter.name) + .map(({ name, port }) => { + const domain = name[0] === '.' ? name : `.${name}`; + return { domain, port }; + }); + if (!filters.length) { + return () => false; + } + return (hostname: string, port: string) => filters.some(({ domain, port: filterPort }) => { + return endsWith(`.${hostname.toLowerCase()}`, domain) && (!filterPort || port === filterPort); + }); +} + function createPatchedModules(configProvider: ExtHostConfigProvider, resolveProxy: ReturnType) { const setting = { config: configProvider.getConfiguration('http') diff --git a/src/vs/workbench/services/files/node/encoding.ts b/src/vs/workbench/services/files/node/encoding.ts index b600d76c684..62067cb504f 100644 --- a/src/vs/workbench/services/files/node/encoding.ts +++ b/src/vs/workbench/services/files/node/encoding.ts @@ -49,7 +49,7 @@ export class ResourceEncodings extends Disposable implements IResourceEncodings })); } - getReadEncoding(resource: uri, options: IResolveContentOptions, detected: encoding.IDetectedEncodingResult): string { + getReadEncoding(resource: uri, options: IResolveContentOptions | undefined, detected: encoding.IDetectedEncodingResult): string { let preferredEncoding: string | undefined; // Encoding passed in as option diff --git a/src/vs/workbench/services/files/node/fileService.ts b/src/vs/workbench/services/files/node/fileService.ts index f9b20baae2e..3302a7b90a0 100644 --- a/src/vs/workbench/services/files/node/fileService.ts +++ b/src/vs/workbench/services/files/node/fileService.ts @@ -70,7 +70,7 @@ export class FileService extends Disposable implements IFileService { protected readonly _onDidChangeFileSystemProviderRegistrations = this._register(new Emitter()); get onDidChangeFileSystemProviderRegistrations(): Event { return this._onDidChangeFileSystemProviderRegistrations.event; } - private activeWorkspaceFileChangeWatcher: IDisposable; + private activeWorkspaceFileChangeWatcher: IDisposable | null; private activeFileChangesWatchers: ResourceMap<{ unwatch: Function, count: number }>; private fileChangesWatchDelayer: ThrottledDelayer; private undeliveredRawFileChangesEvents: IRawFileChange[]; @@ -262,7 +262,7 @@ export class FileService extends Disposable implements IFileService { )); } - const result: IStreamContent = { + const result: Partial = { resource: undefined, name: undefined, mtime: undefined, @@ -311,7 +311,7 @@ export class FileService extends Disposable implements IFileService { // Return early if file is too large to load if (typeof stat.size === 'number') { - if (stat.size > Math.max(parseInt(this.environmentService.args['max-memory']) * 1024 * 1024 || 0, MAX_HEAP_SIZE)) { + if (stat.size > Math.max(typeof this.environmentService.args['max-memory'] === 'string' ? parseInt(this.environmentService.args['max-memory']) * 1024 * 1024 || 0 : 0, MAX_HEAP_SIZE)) { return onStatError(new FileOperationError( nls.localize('fileTooLargeForHeapError', "To open a file of this size, you need to restart VS Code and allow it to use more memory"), FileOperationResult.FILE_EXCEED_MEMORY_LIMIT @@ -391,17 +391,17 @@ export class FileService extends Disposable implements IFileService { }); } - private fillInContents(content: IStreamContent, resource: uri, options: IResolveContentOptions, token: CancellationToken): Promise { + private fillInContents(content: Partial, resource: uri, options: IResolveContentOptions | undefined, token: CancellationToken): Promise { return this.resolveFileData(resource, options, token).then(data => { content.encoding = data.encoding; content.value = data.stream; }); } - private resolveFileData(resource: uri, options: IResolveContentOptions, token: CancellationToken): Promise { + private resolveFileData(resource: uri, options: IResolveContentOptions | undefined, token: CancellationToken): Promise { const chunkBuffer = Buffer.allocUnsafe(64 * 1024); - const result: IContentData = { + const result: Partial = { encoding: undefined, stream: undefined }; @@ -473,7 +473,7 @@ export class FileService extends Disposable implements IFileService { } }; - let currentPosition: number = (options && options.position) || null; + let currentPosition: number | null = (options && options.position) || null; const readChunk = () => { fs.read(fd, chunkBuffer, 0, chunkBuffer.length, currentPosition, (err, bytesRead) => { @@ -485,7 +485,7 @@ export class FileService extends Disposable implements IFileService { currentPosition += bytesRead; } - if (totalBytesRead > Math.max(parseInt(this.environmentService.args['max-memory']) * 1024 * 1024 || 0, MAX_HEAP_SIZE)) { + if (totalBytesRead > Math.max(typeof this.environmentService.args['max-memory'] === 'number' ? parseInt(this.environmentService.args['max-memory']) * 1024 * 1024 || 0 : 0, MAX_HEAP_SIZE)) { finish(new FileOperationError( nls.localize('fileTooLargeForHeapError', "To open a file of this size, you need to restart VS Code and allow it to use more memory"), FileOperationResult.FILE_EXCEED_MEMORY_LIMIT @@ -525,7 +525,7 @@ export class FileService extends Disposable implements IFileService { } else { result.encoding = this._encoding.getReadEncoding(resource, options, detected); result.stream = decoder = encoding.decodeStream(result.encoding); - resolve(result); + resolve(result as IContentData); handleChunk(bytesRead); } }).then(undefined, err => { @@ -1168,16 +1168,18 @@ export class StatResolver { let absoluteTargetPaths: string[] | null = null; if (options && options.resolveTo) { absoluteTargetPaths = []; - options.resolveTo.forEach(resource => { + for (const resource of options.resolveTo) { absoluteTargetPaths.push(resource.fsPath); - }); + } } return new Promise(resolve => { // Load children - this.resolveChildren(this.resource.fsPath, absoluteTargetPaths, options && options.resolveSingleChildDescendants, children => { - children = arrays.coalesce(children); // we don't want those null children (could be permission denied when reading a child) + this.resolveChildren(this.resource.fsPath, absoluteTargetPaths, !!(options && options.resolveSingleChildDescendants), children => { + if (children) { + children = arrays.coalesce(children); // we don't want those null children (could be permission denied when reading a child) + } fileStat.children = children || []; resolve(fileStat); @@ -1186,7 +1188,7 @@ export class StatResolver { } } - private resolveChildren(absolutePath: string, absoluteTargetPaths: string[], resolveSingleChildDescendants: boolean, callback: (children: IFileStat[]) => void): void { + private resolveChildren(absolutePath: string, absoluteTargetPaths: string[] | null, resolveSingleChildDescendants: boolean, callback: (children: IFileStat[] | null) => void): void { extfs.readdir(absolutePath, (error: Error, files: string[]) => { if (error) { if (this.errorLogger) { @@ -1197,7 +1199,7 @@ export class StatResolver { } // for each file in the folder - flow.parallel(files, (file: string, clb: (error: Error, children: IFileStat) => void) => { + flow.parallel(files, (file: string, clb: (error: Error | null, children: IFileStat | null) => void) => { const fileResource = uri.file(paths.resolve(absolutePath, file)); let fileStat: fs.Stats; let isSymbolicLink = false; @@ -1257,7 +1259,9 @@ export class StatResolver { // Continue resolving children based on condition if (resolveFolderChildren) { $this.resolveChildren(fileResource.fsPath, absoluteTargetPaths, resolveSingleChildDescendants, children => { - children = arrays.coalesce(children); // we don't want those null children + if (children) { + children = arrays.coalesce(children); // we don't want those null children + } childStat.children = children || []; clb(null, childStat); diff --git a/src/vs/workbench/services/files/node/remoteFileService.ts b/src/vs/workbench/services/files/node/remoteFileService.ts index e9beb32812c..36687a83c68 100644 --- a/src/vs/workbench/services/files/node/remoteFileService.ts +++ b/src/vs/workbench/services/files/node/remoteFileService.ts @@ -70,7 +70,7 @@ function toIFileStat(provider: IFileSystemProvider, tuple: [URI, IStat], recurse return Promise.resolve(fileStat); } -export function toDeepIFileStat(provider: IFileSystemProvider, tuple: [URI, IStat], to: URI[]): Promise { +export function toDeepIFileStat(provider: IFileSystemProvider, tuple: [URI, IStat], to?: URI[]): Promise { const trie = TernarySearchTree.forPaths(); trie.set(tuple[0].toString(), true); @@ -281,7 +281,7 @@ export class RemoteFileService extends FileService { FileOperationResult.FILE_NOT_FOUND ); } else { - return data[0].stat; + return data[0].stat!; } }); } @@ -319,7 +319,7 @@ export class RemoteFileService extends FileService { return toDeepIFileStat(provider, [item.resource, stat], item.options && item.options.resolveTo).then(fileStat => { result[idx] = { stat: fileStat, success: true }; }); - }, err => { + }, _err => { result[idx] = { stat: undefined, success: false }; }); }); @@ -440,7 +440,7 @@ export class RemoteFileService extends FileService { return RemoteFileService._mkdirp(provider, resources.dirname(resource)).then(() => { const encoding = this.encoding.getWriteEncoding(resource); - return this._writeFile(provider, resource, new StringSnapshot(content), encoding, { create: true, overwrite: Boolean(options && options.overwrite) }); + return this._writeFile(provider, resource, new StringSnapshot(content || ''), encoding, { create: true, overwrite: Boolean(options && options.overwrite) }); }); }).then(fileStat => { @@ -449,7 +449,7 @@ export class RemoteFileService extends FileService { }, err => { const message = localize('err.create', "Failed to create file {0}", resource.toString(false)); const result = this._tryParseFileOperationResult(err); - throw new FileOperationError(message, result, options); + throw new FileOperationError(message, result || -1, options); }); } } @@ -467,7 +467,7 @@ export class RemoteFileService extends FileService { } } - private _writeFile(provider: IFileSystemProvider, resource: URI, snapshot: ITextSnapshot, preferredEncoding: string, options: FileWriteOptions): Promise { + private _writeFile(provider: IFileSystemProvider, resource: URI, snapshot: ITextSnapshot, preferredEncoding: string | undefined = undefined, options: FileWriteOptions): Promise { const readable = createReadableOfSnapshot(snapshot); const encoding = this.encoding.getWriteEncoding(resource, preferredEncoding); const encoder = encodeStream(encoding); @@ -549,13 +549,13 @@ export class RemoteFileService extends FileService { } } - private _doMoveWithInScheme(source: URI, target: URI, overwrite?: boolean): Promise { + private async _doMoveWithInScheme(source: URI, target: URI, overwrite: boolean = false): Promise { - const prepare = overwrite - ? Promise.resolve(this.del(target, { recursive: true }).then(undefined, err => { /*ignore*/ })) - : Promise.resolve(null); + if (overwrite) { + await this.del(target, { recursive: true }).catch(_err => { /*ignore*/ }); + } - return prepare.then(() => this._withProvider(source)).then(RemoteFileService._throwIfFileSystemIsReadonly).then(provider => { + return this._withProvider(source).then(RemoteFileService._throwIfFileSystemIsReadonly).then(provider => { return RemoteFileService._mkdirp(provider, resources.dirname(target)).then(() => { return provider.rename(source, target, { overwrite }).then(() => { return this.resolveFile(target); @@ -589,11 +589,11 @@ export class RemoteFileService extends FileService { return super.copyFile(source, target, overwrite); } - return this._withProvider(target).then(RemoteFileService._throwIfFileSystemIsReadonly).then(provider => { + return this._withProvider(target).then(RemoteFileService._throwIfFileSystemIsReadonly).then(async provider => { if (source.scheme === target.scheme && (provider.capabilities & FileSystemProviderCapabilities.FileFolderCopy)) { // good: provider supports copy withing scheme - return provider.copy(source, target, { overwrite: !!overwrite }).then(() => { + return provider.copy!(source, target, { overwrite: !!overwrite }).then(() => { return this.resolveFile(target); }).then(fileStat => { this._onAfterOperation.fire(new FileOperationEvent(source, FileOperation.COPY, fileStat)); @@ -607,51 +607,46 @@ export class RemoteFileService extends FileService { }); } - const prepare = overwrite - ? Promise.resolve(this.del(target, { recursive: true }).then(undefined, err => { /*ignore*/ })) - : Promise.resolve(null); + if (overwrite) { + await this.del(target, { recursive: true }).catch(_err => { /*ignore*/ }); + } - return prepare.then(() => { - // todo@ben, can only copy text files - // https://github.com/Microsoft/vscode/issues/41543 - return this.resolveContent(source, { acceptTextOnly: true }).then(content => { - return this._withProvider(target).then(provider => { - return this._writeFile( - provider, target, - new StringSnapshot(content.value), - content.encoding, - { create: true, overwrite: !!overwrite } - ).then(fileStat => { - this._onAfterOperation.fire(new FileOperationEvent(source, FileOperation.COPY, fileStat)); - return fileStat; - }); - }, err => { - const result = this._tryParseFileOperationResult(err); - if (result === FileOperationResult.FILE_MOVE_CONFLICT) { - throw new FileOperationError(localize('fileMoveConflict', "Unable to move/copy. File already exists at destination."), result); - } else if (err instanceof Error && err.name === 'ENOPRO') { - // file scheme - return super.updateContent(target, content.value, { encoding: content.encoding }); - } else { - return Promise.reject(err); - } + // todo@ben, can only copy text files + // https://github.com/Microsoft/vscode/issues/41543 + return this.resolveContent(source, { acceptTextOnly: true }).then(content => { + return this._withProvider(target).then(provider => { + return this._writeFile( + provider, target, + new StringSnapshot(content.value), + content.encoding, + { create: true, overwrite: !!overwrite } + ).then(fileStat => { + this._onAfterOperation.fire(new FileOperationEvent(source, FileOperation.COPY, fileStat)); + return fileStat; }); + }, err => { + const result = this._tryParseFileOperationResult(err); + if (result === FileOperationResult.FILE_MOVE_CONFLICT) { + throw new FileOperationError(localize('fileMoveConflict', "Unable to move/copy. File already exists at destination."), result); + } else if (err instanceof Error && err.name === 'ENOPRO') { + // file scheme + return super.updateContent(target, content.value, { encoding: content.encoding }); + } else { + return Promise.reject(err); + } }); }); + }); } private _activeWatches = new Map, count: number }>(); - watchFileChanges(resource: URI, opts?: IWatchOptions): void { + watchFileChanges(resource: URI, opts: IWatchOptions = { recursive: false, excludes: [] }): void { if (resource.scheme === Schemas.file) { return super.watchFileChanges(resource); } - if (!opts) { - opts = { recursive: false, excludes: [] }; - } - const key = resource.toString(); const entry = this._activeWatches.get(key); if (entry) { @@ -663,7 +658,7 @@ export class RemoteFileService extends FileService { count: 1, unwatch: this._withProvider(resource).then(provider => { return provider.watch(resource, opts); - }, err => { + }, _err => { return { dispose() { } }; }) }); diff --git a/src/vs/workbench/services/history/browser/history.ts b/src/vs/workbench/services/history/browser/history.ts index 03234a94c8e..7f52cda2121 100644 --- a/src/vs/workbench/services/history/browser/history.ts +++ b/src/vs/workbench/services/history/browser/history.ts @@ -420,7 +420,7 @@ export class HistoryService extends Disposable implements IHistoryService { this.doNavigate(this.stack[this.index], !acrossEditors).finally(() => this.navigatingInStack = false); } - private doNavigate(location: IStackEntry, withSelection: boolean): Promise { + private doNavigate(location: IStackEntry, withSelection: boolean): Promise { const options: ITextEditorOptions = { revealIfOpened: true // support to navigate across editor groups }; diff --git a/src/vs/workbench/services/keybinding/common/keybindingEditing.ts b/src/vs/workbench/services/keybinding/common/keybindingEditing.ts index e0c03b9aef7..94b9c9bc0d8 100644 --- a/src/vs/workbench/services/keybinding/common/keybindingEditing.ts +++ b/src/vs/workbench/services/keybinding/common/keybindingEditing.ts @@ -141,7 +141,10 @@ export class KeybindingsEditingService extends Disposable implements IKeybinding private removeDefaultKeybinding(keybindingItem: ResolvedKeybindingItem, model: ITextModel): void { const { tabSize, insertSpaces } = model.getOptions(); const eol = model.getEOL(); - this.applyEditsToBuffer(setProperty(model.getValue(), [-1], this.asObject(keybindingItem.resolvedKeybinding.getUserSettingsLabel(), keybindingItem.command, keybindingItem.when ? keybindingItem.when.serialize() : undefined, true), { tabSize, insertSpaces, eol })[0], model); + const key = keybindingItem.resolvedKeybinding ? keybindingItem.resolvedKeybinding.getUserSettingsLabel() : null; + if (key) { + this.applyEditsToBuffer(setProperty(model.getValue(), [-1], this.asObject(key, keybindingItem.command, keybindingItem.when ? keybindingItem.when.serialize() : undefined, true), { tabSize, insertSpaces, eol })[0], model); + } } private removeUnassignedDefaultKeybinding(keybindingItem: ResolvedKeybindingItem, model: ITextModel): void { @@ -162,7 +165,8 @@ export class KeybindingsEditingService extends Disposable implements IKeybinding return index; } if (keybinding.when && keybindingItem.when) { - if (ContextKeyExpr.deserialize(keybinding.when).serialize() === keybindingItem.when.serialize()) { + const contextKeyExpr = ContextKeyExpr.deserialize(keybinding.when); + if (contextKeyExpr && contextKeyExpr.serialize() === keybindingItem.when.serialize()) { return index; } } @@ -181,9 +185,11 @@ export class KeybindingsEditingService extends Disposable implements IKeybinding return indices; } - private asObject(key: string, command: string, when: string, negate: boolean): any { + private asObject(key: string, command: string | null, when: string | undefined, negate: boolean): any { const object = { key }; - object['command'] = negate ? `-${command}` : command; + if (command) { + object['command'] = negate ? `-${command}` : command; + } if (when) { object['when'] = when; } diff --git a/src/vs/workbench/services/part/common/partService.ts b/src/vs/workbench/services/part/common/partService.ts index c779417568b..6ccd362ba74 100644 --- a/src/vs/workbench/services/part/common/partService.ts +++ b/src/vs/workbench/services/part/common/partService.ts @@ -21,13 +21,6 @@ export const enum Position { RIGHT, BOTTOM } -export function PositionToString(position: Position): string { - switch (position) { - case Position.LEFT: return 'LEFT'; - case Position.RIGHT: return 'RIGHT'; - case Position.BOTTOM: return 'BOTTOM'; - } -} export interface ILayoutOptions { toggleMaximizedPanel?: boolean; @@ -50,9 +43,9 @@ export interface IPartService { onTitleBarVisibilityChange: Event; /** - * Emits when the editor part's layout changes. + * Emits when the zen mode is enabled or disabled. */ - onEditorLayout: Event; + onZenModeChange: Event; /** * Asks the part service if all parts have been fully restored. For editor part diff --git a/src/vs/workbench/services/preferences/browser/preferencesService.ts b/src/vs/workbench/services/preferences/browser/preferencesService.ts index ee6c999089f..7879f21bc17 100644 --- a/src/vs/workbench/services/preferences/browser/preferencesService.ts +++ b/src/vs/workbench/services/preferences/browser/preferencesService.ts @@ -18,7 +18,7 @@ import { IModelService } from 'vs/editor/common/services/modelService'; import { IModeService } from 'vs/editor/common/services/modeService'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import * as nls from 'vs/nls'; -import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; +import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IEditorOptions } from 'vs/platform/editor/common/editor'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { FileOperationError, FileOperationResult, IFileService } from 'vs/platform/files/common/files'; @@ -29,13 +29,13 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { EditorInput, IEditor } from 'vs/workbench/common/editor'; -import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { GroupDirection, IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { DEFAULT_SETTINGS_EDITOR_SETTING, FOLDER_SETTINGS_PATH, getSettingsTargetName, IPreferencesEditorModel, IPreferencesService, ISetting, ISettingsEditorOptions, SettingsEditorOptions, USE_SPLIT_JSON_SETTING } from 'vs/workbench/services/preferences/common/preferences'; import { DefaultPreferencesEditorInput, KeybindingsEditorInput, PreferencesEditorInput, SettingsEditor2Input } from 'vs/workbench/services/preferences/common/preferencesEditorInput'; import { defaultKeybindingsContents, DefaultKeybindingsEditorModel, DefaultSettings, DefaultSettingsEditorModel, Settings2EditorModel, SettingsEditorModel, WorkspaceConfigurationEditorModel, DefaultRawSettingsEditorModel } from 'vs/workbench/services/preferences/common/preferencesModels'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; const emptyEditableSettingsContent = '{\n}'; @@ -45,7 +45,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic private lastOpenedSettingsInput: PreferencesEditorInput | null = null; - private readonly _onDispose = new Emitter(); + private readonly _onDispose = this._register(new Emitter()); private _defaultUserSettingsUriCounter = 0; private _defaultUserSettingsContentModel: DefaultSettings; @@ -58,7 +58,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic @IEditorService private readonly editorService: IEditorService, @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService, @IFileService private readonly fileService: IFileService, - @IWorkspaceConfigurationService private readonly configurationService: IWorkspaceConfigurationService, + @IConfigurationService private readonly configurationService: IConfigurationService, @INotificationService private readonly notificationService: INotificationService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @IInstantiationService private readonly instantiationService: IInstantiationService, @@ -74,24 +74,24 @@ export class PreferencesService extends Disposable implements IPreferencesServic super(); // The default keybindings.json updates based on keyboard layouts, so here we make sure // if a model has been given out we update it accordingly. - keybindingService.onDidUpdateKeybindings(() => { + this._register(keybindingService.onDidUpdateKeybindings(() => { const model = modelService.getModel(this.defaultKeybindingsResource); if (!model) { // model has not been given out => nothing to do return; } modelService.updateModel(model, defaultKeybindingsContents(keybindingService)); - }); + })); } readonly defaultKeybindingsResource = URI.from({ scheme: network.Schemas.vscode, authority: 'defaultsettings', path: '/keybindings.json' }); private readonly defaultSettingsRawResource = URI.from({ scheme: network.Schemas.vscode, authority: 'defaultsettings', path: '/defaultSettings.json' }); get userSettingsResource(): URI { - return this.getEditableSettingsURI(ConfigurationTarget.USER); + return this.getEditableSettingsURI(ConfigurationTarget.USER)!; } - get workspaceSettingsResource(): URI { + get workspaceSettingsResource(): URI | null { return this.getEditableSettingsURI(ConfigurationTarget.WORKSPACE); } @@ -99,11 +99,11 @@ export class PreferencesService extends Disposable implements IPreferencesServic return this.instantiationService.createInstance(SettingsEditor2Input); } - getFolderSettingsResource(resource: URI): URI { + getFolderSettingsResource(resource: URI): URI | null { return this.getEditableSettingsURI(ConfigurationTarget.WORKSPACE_FOLDER, resource); } - resolveModel(uri: URI): Promise { + resolveModel(uri: URI): Promise { if (this.isDefaultSettingsResource(uri)) { const target = this.getConfigurationTargetFromDefaultSettingsResource(uri); @@ -155,7 +155,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic return this.createDefaultSettingsEditorModel(uri); } - if (this.getEditableSettingsURI(ConfigurationTarget.USER).toString() === uri.toString()) { + if (this.userSettingsResource.toString() === uri.toString()) { return this.createEditableSettingsEditorModel(ConfigurationTarget.USER, uri); } @@ -168,18 +168,18 @@ export class PreferencesService extends Disposable implements IPreferencesServic return this.createEditableSettingsEditorModel(ConfigurationTarget.WORKSPACE_FOLDER, uri); } - return Promise.resolve>(null); + return Promise.reject(`unknown resource: ${uri.toString()}`); } - openRawDefaultSettings(): Promise { + openRawDefaultSettings(): Promise { return this.editorService.openEditor({ resource: this.defaultSettingsRawResource }); } - openRawUserSettings(): Promise { + openRawUserSettings(): Promise { return this.editorService.openEditor({ resource: this.userSettingsResource }); } - openSettings(jsonEditor?: boolean): Promise { + openSettings(jsonEditor?: boolean): Promise { jsonEditor = typeof jsonEditor === 'undefined' ? this.configurationService.getValue('workbench.settings.editor') === 'json' : jsonEditor; @@ -189,7 +189,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic } const editorInput = this.getActiveSettingsEditorInput() || this.lastOpenedSettingsInput; - const resource = editorInput ? editorInput.master.getResource() : this.userSettingsResource; + const resource = editorInput ? editorInput.master.getResource()! : this.userSettingsResource; const target = this.getConfigurationTargetFromSettingsResource(resource); return this.openOrSwitchSettings(target, resource); } @@ -197,10 +197,10 @@ export class PreferencesService extends Disposable implements IPreferencesServic private openSettings2(): Promise { const input = this.settingsEditor2Input; return this.editorGroupService.activeGroup.openEditor(input) - .then(() => this.editorGroupService.activeGroup.activeControl); + .then(() => this.editorGroupService.activeGroup.activeControl!); } - openGlobalSettings(jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise { + openGlobalSettings(jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise { jsonEditor = typeof jsonEditor === 'undefined' ? this.configurationService.getValue('workbench.settings.editor') === 'json' : jsonEditor; @@ -215,9 +215,9 @@ export class PreferencesService extends Disposable implements IPreferencesServic this.configurationService.getValue('workbench.settings.editor') === 'json' : jsonEditor; - if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) { + if (!this.workspaceSettingsResource) { this.notificationService.info(nls.localize('openFolderFirst', "Open a folder first to create workspace settings")); - return Promise.resolve(null); + return Promise.reject(null); } return jsonEditor ? @@ -225,26 +225,30 @@ export class PreferencesService extends Disposable implements IPreferencesServic this.openOrSwitchSettings2(ConfigurationTarget.WORKSPACE, undefined, options, group); } - openFolderSettings(folder: URI, jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise { + openFolderSettings(folder: URI, jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise { jsonEditor = typeof jsonEditor === 'undefined' ? this.configurationService.getValue('workbench.settings.editor') === 'json' : jsonEditor; - - return jsonEditor ? - this.openOrSwitchSettings(ConfigurationTarget.WORKSPACE_FOLDER, this.getEditableSettingsURI(ConfigurationTarget.WORKSPACE_FOLDER, folder), options, group) : - this.openOrSwitchSettings2(ConfigurationTarget.WORKSPACE_FOLDER, folder, options, group); + const folderSettingsUri = this.getEditableSettingsURI(ConfigurationTarget.WORKSPACE_FOLDER, folder); + if (jsonEditor) { + if (folderSettingsUri) { + return this.openOrSwitchSettings(ConfigurationTarget.WORKSPACE_FOLDER, folderSettingsUri, options, group); + } + return Promise.reject(`Invalid folder URI - ${folder.toString()}`); + } + return this.openOrSwitchSettings2(ConfigurationTarget.WORKSPACE_FOLDER, folder, options, group); } switchSettings(target: ConfigurationTarget, resource: URI, jsonEditor?: boolean): Promise { if (!jsonEditor) { - return this.doOpenSettings2(target, resource).then(() => null); + return this.doOpenSettings2(target, resource).then(() => undefined); } const activeControl = this.editorService.activeControl; if (activeControl && activeControl.input instanceof PreferencesEditorInput) { - return this.doSwitchSettings(target, resource, activeControl.input, activeControl.group).then(() => null); + return this.doSwitchSettings(target, resource, activeControl.input, activeControl.group).then(() => undefined); } else { - return this.doOpenSettings(target, resource).then(() => null); + return this.doOpenSettings(target, resource).then(() => undefined); } } @@ -275,10 +279,10 @@ export class PreferencesService extends Disposable implements IPreferencesServic }); } - return this.editorService.openEditor(this.instantiationService.createInstance(KeybindingsEditorInput), { pinned: true, revealIfOpened: true }).then(() => null); + return this.editorService.openEditor(this.instantiationService.createInstance(KeybindingsEditorInput), { pinned: true, revealIfOpened: true }).then(() => undefined); } - openDefaultKeybindingsFile(): Promise { + openDefaultKeybindingsFile(): Promise { return this.editorService.openEditor({ resource: this.defaultKeybindingsResource, label: nls.localize('defaultKeybindings', "Default Keybindings") }); } @@ -286,11 +290,11 @@ export class PreferencesService extends Disposable implements IPreferencesServic this.openGlobalSettings(true) .then(editor => this.createPreferencesEditorModel(this.userSettingsResource) .then((settingsModel: IPreferencesEditorModel) => { - const codeEditor = getCodeEditor(editor.getControl()); + const codeEditor = editor ? getCodeEditor(editor.getControl()) : null; if (codeEditor) { this.addLanguageOverrideEntry(language, settingsModel, codeEditor) .then(position => { - if (codeEditor) { + if (codeEditor && position) { codeEditor.setPosition(position); codeEditor.revealLine(position.lineNumber); codeEditor.focus(); @@ -300,19 +304,22 @@ export class PreferencesService extends Disposable implements IPreferencesServic })); } - private openOrSwitchSettings(configurationTarget: ConfigurationTarget, resource: URI, options?: ISettingsEditorOptions, group: IEditorGroup = this.editorGroupService.activeGroup): Promise { + private openOrSwitchSettings(configurationTarget: ConfigurationTarget, resource: URI, options?: ISettingsEditorOptions, group: IEditorGroup = this.editorGroupService.activeGroup): Promise { const editorInput = this.getActiveSettingsEditorInput(group); - if (editorInput && editorInput.master.getResource().fsPath !== resource.fsPath) { - return this.doSwitchSettings(configurationTarget, resource, editorInput, group, options); + if (editorInput) { + const editorInputResource = editorInput.master.getResource(); + if (editorInputResource && editorInputResource.fsPath !== resource.fsPath) { + return this.doSwitchSettings(configurationTarget, resource, editorInput, group, options); + } } return this.doOpenSettings(configurationTarget, resource, options, group); } - private openOrSwitchSettings2(configurationTarget: ConfigurationTarget, folderUri?: URI, options?: ISettingsEditorOptions, group: IEditorGroup = this.editorGroupService.activeGroup): Promise { + private openOrSwitchSettings2(configurationTarget: ConfigurationTarget, folderUri?: URI, options?: ISettingsEditorOptions, group: IEditorGroup = this.editorGroupService.activeGroup): Promise { return this.doOpenSettings2(configurationTarget, folderUri, options, group); } - private doOpenSettings(configurationTarget: ConfigurationTarget, resource: URI, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise { + private doOpenSettings(configurationTarget: ConfigurationTarget, resource: URI, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise { const openSplitJSON = !!this.configurationService.getValue(USE_SPLIT_JSON_SETTING); if (openSplitJSON) { return this.doOpenSplitJSON(configurationTarget, resource, options, group); @@ -334,14 +341,14 @@ export class PreferencesService extends Disposable implements IPreferencesServic return Promise.all([ this.editorService.openEditor({ resource: this.defaultSettingsRawResource, options: { pinned: true, preserveFocus: true, revealIfOpened: true }, label: nls.localize('defaultSettings', "Default Settings"), description: '' }), this.editorService.openEditor(editableSettingsEditorInput, { pinned: true, revealIfOpened: true }, sideEditorGroup.id) - ]).then(() => null); + ]).then(([defaultEditor, editor]) => editor); } else { return this.editorService.openEditor(editableSettingsEditorInput, SettingsEditorOptions.create(options), group); } }); } - private doOpenSplitJSON(configurationTarget: ConfigurationTarget, resource: URI, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise { + private doOpenSplitJSON(configurationTarget: ConfigurationTarget, resource: URI, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise { return this.getOrCreateEditableSettingsEditorInput(configurationTarget, resource) .then(editableSettingsEditorInput => { if (!options) { @@ -361,7 +368,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic return this.instantiationService.createInstance(Settings2EditorModel, this.getDefaultSettings(ConfigurationTarget.USER)); } - private doOpenSettings2(target: ConfigurationTarget, folderUri: URI | undefined, options?: IEditorOptions, group?: IEditorGroup): Promise { + private doOpenSettings2(target: ConfigurationTarget, folderUri: URI | undefined, options?: IEditorOptions, group?: IEditorGroup): Promise { const input = this.settingsEditor2Input; const settingsOptions: ISettingsEditorOptions = { ...options, @@ -373,7 +380,11 @@ export class PreferencesService extends Disposable implements IPreferencesServic } private doSwitchSettings(target: ConfigurationTarget, resource: URI, input: PreferencesEditorInput, group: IEditorGroup, options?: ISettingsEditorOptions): Promise { - return this.getOrCreateEditableSettingsEditorInput(target, this.getEditableSettingsURI(target, resource)) + const settingsURI = this.getEditableSettingsURI(target, resource); + if (!settingsURI) { + return Promise.reject(`Invalid settings URI - ${resource.toString()}`); + } + return this.getOrCreateEditableSettingsEditorInput(target, settingsURI) .then(toInput => { return group.openEditor(input).then(() => { const replaceWith = new PreferencesEditorInput(this.getPreferencesEditorInputName(target, resource), toInput.getDescription(), this.instantiationService.createInstance(DefaultPreferencesEditorInput, this.getDefaultSettingsResource(target)), toInput); @@ -381,10 +392,10 @@ export class PreferencesService extends Disposable implements IPreferencesServic return group.replaceEditors([{ editor: input, replacement: replaceWith, - options: SettingsEditorOptions.create(options) + options: options ? SettingsEditorOptions.create(options) : undefined }]).then(() => { this.lastOpenedSettingsInput = replaceWith; - return group.activeControl; + return group.activeControl!; }); }); }); @@ -463,7 +474,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic return this.textModelResolverService.createModelReference(settingsUri) .then(reference => this.instantiationService.createInstance(SettingsEditorModel, reference, configurationTarget)); } - return Promise.resolve(null); + return Promise.reject(`unknown target: ${configurationTarget} and resource: ${resource.toString()}`); } private createDefaultSettingsEditorModel(defaultSettingsUri: URI): Promise { @@ -493,7 +504,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic return this._defaultUserSettingsContentModel; } - private getEditableSettingsURI(configurationTarget: ConfigurationTarget, resource?: URI): URI { + private getEditableSettingsURI(configurationTarget: ConfigurationTarget, resource?: URI): URI | null { switch (configurationTarget) { case ConfigurationTarget.USER: return URI.file(this.environmentService.appSettingsPath); @@ -504,8 +515,10 @@ export class PreferencesService extends Disposable implements IPreferencesServic const workspace = this.contextService.getWorkspace(); return workspace.configuration || workspace.folders[0].toResource(FOLDER_SETTINGS_PATH); case ConfigurationTarget.WORKSPACE_FOLDER: - const folder = this.contextService.getWorkspaceFolder(resource); - return folder ? folder.toResource(FOLDER_SETTINGS_PATH) : null; + if (resource) { + const folder = this.contextService.getWorkspaceFolder(resource); + return folder ? folder.toResource(FOLDER_SETTINGS_PATH) : null; + } } return null; } @@ -522,7 +535,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic if (Object.keys(parse(content.value)).indexOf('settings') === -1) { return this.jsonEditingService.write(resource, { key: 'settings', value: {} }, true).then(undefined, () => { }); } - return null; + return undefined; }); } return this.createIfNotExists(resource, emptyEditableSettingsContent).then(() => { }); @@ -556,29 +569,35 @@ export class PreferencesService extends Disposable implements IPreferencesServic ]; } - private addLanguageOverrideEntry(language: string, settingsModel: IPreferencesEditorModel, codeEditor: ICodeEditor): Promise { + private addLanguageOverrideEntry(language: string, settingsModel: IPreferencesEditorModel, codeEditor: ICodeEditor): Promise { const languageKey = `[${language}]`; let setting = settingsModel.getPreference(languageKey); const model = codeEditor.getModel(); - const configuration = this.configurationService.getValue<{ editor: { tabSize: number; insertSpaces: boolean } }>(); - const eol = model.getEOL(); - if (setting) { - if (setting.overrides.length) { - const lastSetting = setting.overrides[setting.overrides.length - 1]; - return Promise.resolve({ lineNumber: lastSetting.valueRange.endLineNumber, column: model.getLineMaxColumn(lastSetting.valueRange.endLineNumber) }); + if (model) { + const configuration = this.configurationService.getValue<{ editor: { tabSize: number; insertSpaces: boolean } }>(); + const eol = model.getEOL(); + if (setting) { + if (setting.overrides && setting.overrides.length) { + const lastSetting = setting.overrides[setting.overrides.length - 1]; + return Promise.resolve({ lineNumber: lastSetting.valueRange.endLineNumber, column: model.getLineMaxColumn(lastSetting.valueRange.endLineNumber) }); + } + return Promise.resolve({ lineNumber: setting.valueRange.startLineNumber, column: setting.valueRange.startColumn + 1 }); } - return Promise.resolve({ lineNumber: setting.valueRange.startLineNumber, column: setting.valueRange.startColumn + 1 }); + return this.configurationService.updateValue(languageKey, {}, ConfigurationTarget.USER) + .then(() => { + setting = settingsModel.getPreference(languageKey); + if (setting) { + let content = eol + this.spaces(2, configuration.editor) + eol + this.spaces(1, configuration.editor); + let editOperation = EditOperation.insert(new Position(setting.valueRange.endLineNumber, setting.valueRange.endColumn - 1), content); + model.pushEditOperations([], [editOperation], () => []); + let lineNumber = setting.valueRange.endLineNumber + 1; + settingsModel.dispose(); + return { lineNumber, column: model.getLineMaxColumn(lineNumber) }; + } + return null; + }); } - return this.configurationService.updateValue(languageKey, {}, ConfigurationTarget.USER) - .then(() => { - setting = settingsModel.getPreference(languageKey); - let content = eol + this.spaces(2, configuration.editor) + eol + this.spaces(1, configuration.editor); - let editOperation = EditOperation.insert(new Position(setting.valueRange.endLineNumber, setting.valueRange.endColumn - 1), content); - model.pushEditOperations([], [editOperation], () => []); - let lineNumber = setting.valueRange.endLineNumber + 1; - settingsModel.dispose(); - return { lineNumber, column: model.getLineMaxColumn(lineNumber) }; - }); + return Promise.resolve(null); } private spaces(count: number, { tabSize, insertSpaces }: { tabSize: number; insertSpaces: boolean }): string { @@ -590,3 +609,5 @@ export class PreferencesService extends Disposable implements IPreferencesServic super.dispose(); } } + +registerSingleton(IPreferencesService, PreferencesService); \ No newline at end of file diff --git a/src/vs/workbench/services/preferences/common/preferences.ts b/src/vs/workbench/services/preferences/common/preferences.ts index c69cfc9d412..06b0a00d28e 100644 --- a/src/vs/workbench/services/preferences/common/preferences.ts +++ b/src/vs/workbench/services/preferences/common/preferences.ts @@ -12,7 +12,6 @@ import { localize } from 'vs/nls'; import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; import { IEditorOptions } from 'vs/platform/editor/common/editor'; -import { ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { EditorOptions, IEditor } from 'vs/workbench/common/editor'; @@ -54,7 +53,7 @@ export interface ISetting { value: any; valueRange: IRange; description: string[]; - descriptionIsMarkdown: boolean; + descriptionIsMarkdown?: boolean; descriptionRanges: IRange[]; overrides?: ISetting[]; overrideOf?: ISetting; @@ -66,12 +65,12 @@ export interface ISetting { enumDescriptions?: string[]; enumDescriptionsAreMarkdown?: boolean; tags?: string[]; - validator?: (value: any) => string; + validator?: (value: any) => string | null; } export interface IExtensionSetting extends ISetting { - extensionName: string; - extensionPublisher: string; + extensionName?: string; + extensionPublisher?: string; } export interface ISearchResult { @@ -98,7 +97,7 @@ export interface IFilterResult { export interface ISettingMatch { setting: ISetting; - matches: IRange[]; + matches: IRange[] | null; score: number; } @@ -123,7 +122,6 @@ export interface IFilterMetadata { timestamp: number; duration: number; scoredResults: IScoredResults; - extensions?: ILocalExtension[]; /** The number of requests made, since requests are split by number of filters */ requestCount?: number; @@ -138,8 +136,8 @@ export interface IPreferencesEditorModel { dispose(): void; } -export type IGroupFilter = (group: ISettingsGroup) => boolean; -export type ISettingMatcher = (setting: ISetting, group: ISettingsGroup) => { matches: IRange[], score: number }; +export type IGroupFilter = (group: ISettingsGroup) => boolean | null; +export type ISettingMatcher = (setting: ISetting, group: ISettingsGroup) => { matches: IRange[], score: number } | null; export interface ISettingsEditorModel extends IPreferencesEditorModel { readonly onDidChangeGroups: Event; @@ -164,11 +162,7 @@ export class SettingsEditorOptions extends EditorOptions implements ISettingsEdi folderUri?: URI; query?: string; - static create(settings: ISettingsEditorOptions): SettingsEditorOptions | null { - if (!settings) { - return null; - } - + static create(settings: ISettingsEditorOptions): SettingsEditorOptions { const options = new SettingsEditorOptions(); options.target = settings.target; @@ -197,23 +191,23 @@ export interface IPreferencesService { _serviceBrand: any; userSettingsResource: URI; - workspaceSettingsResource: URI; - getFolderSettingsResource(resource: URI): URI; + workspaceSettingsResource: URI | null; + getFolderSettingsResource(resource: URI): URI | null; - resolveModel(uri: URI): Promise; + resolveModel(uri: URI): Promise; createPreferencesEditorModel(uri: URI): Promise>; createSettings2EditorModel(): Settings2EditorModel; // TODO - openRawDefaultSettings(): Promise; - openSettings(jsonEditor?: boolean): Promise; - openGlobalSettings(jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise; - openWorkspaceSettings(jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise; - openFolderSettings(folder: URI, jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise; + openRawDefaultSettings(): Promise; + openSettings(jsonEditor?: boolean): Promise; + openGlobalSettings(jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise; + openWorkspaceSettings(jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise; + openFolderSettings(folder: URI, jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise; switchSettings(target: ConfigurationTarget, resource: URI, jsonEditor?: boolean): Promise; openGlobalKeybindingSettings(textual: boolean): Promise; - openDefaultKeybindingsFile(): Promise; + openDefaultKeybindingsFile(): Promise; - configureSettingsForLanguage(language: string): void; + configureSettingsForLanguage(language: string | null): void; } export function getSettingsTargetName(target: ConfigurationTarget, resource: URI, workspaceContextService: IWorkspaceContextService): string { diff --git a/src/vs/workbench/services/preferences/common/preferencesEditorInput.ts b/src/vs/workbench/services/preferences/common/preferencesEditorInput.ts index 4929b15f8f9..e1db37ca23b 100644 --- a/src/vs/workbench/services/preferences/common/preferencesEditorInput.ts +++ b/src/vs/workbench/services/preferences/common/preferencesEditorInput.ts @@ -22,7 +22,7 @@ export class PreferencesEditorInput extends SideBySideEditorInput { return PreferencesEditorInput.ID; } - getTitle(verbosity: Verbosity): string { + getTitle(verbosity: Verbosity): string | null { return this.master.getTitle(verbosity); } } diff --git a/src/vs/workbench/services/preferences/common/preferencesModels.ts b/src/vs/workbench/services/preferences/common/preferencesModels.ts index f61324585e4..e49a53b39c8 100644 --- a/src/vs/workbench/services/preferences/common/preferencesModels.ts +++ b/src/vs/workbench/services/preferences/common/preferencesModels.ts @@ -23,6 +23,9 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { EditorModel } from 'vs/workbench/common/editor'; import { IFilterMetadata, IFilterResult, IGroupFilter, IKeybindingsEditorModel, ISearchResultGroup, ISetting, ISettingMatch, ISettingMatcher, ISettingsEditorModel, ISettingsGroup } from 'vs/workbench/services/preferences/common/preferences'; +export const nullRange: IRange = { startLineNumber: -1, startColumn: -1, endLineNumber: -1, endColumn: -1 }; +export function isNullRange(range: IRange): boolean { return range.startLineNumber === -1 && range.startColumn === -1 && range.endLineNumber === -1 && range.endColumn === -1; } + export abstract class AbstractSettingsModel extends EditorModel { protected _currentResultGroups = new Map(); @@ -124,7 +127,7 @@ export class SettingsEditorModel extends AbstractSettingsModel implements ISetti constructor(reference: IReference, private _configurationTarget: ConfigurationTarget) { super(); - this.settingsModel = reference.object.textEditorModel; + this.settingsModel = reference.object.textEditorModel!; this._register(this.onDispose(() => reference.dispose())); this._register(this.settingsModel.onDidChangeContent(() => { this._settingsGroups = null; @@ -175,7 +178,9 @@ export class SettingsEditorModel extends AbstractSettingsModel implements ISetti resultGroups.forEach(group => { group.result.filterMatches.forEach(filterMatch => { filteredSettings.push(filterMatch.setting); - matches.push(...filterMatch.matches); + if (filterMatch.matches) { + matches.push(...filterMatch.matches); + } }); }); @@ -268,7 +273,7 @@ function parse(model: ITextModel, isSettingsProperty: (currentProperty: string, } if (previousParents.length === settingsPropertyIndex + 1 || (previousParents.length === settingsPropertyIndex + 2 && overrideSetting !== null)) { // settings value started - const setting = previousParents.length === settingsPropertyIndex + 1 ? settings[settings.length - 1] : overrideSetting.overrides[overrideSetting.overrides.length - 1]; + const setting = previousParents.length === settingsPropertyIndex + 1 ? settings[settings.length - 1] : overrideSetting!.overrides![overrideSetting!.overrides!.length - 1]; if (setting) { const valueStartPosition = model.getPositionAt(offset); const valueEndPosition = model.getPositionAt(offset + length); @@ -288,7 +293,7 @@ function parse(model: ITextModel, isSettingsProperty: (currentProperty: string, } const visitor: JSONVisitor = { onObjectBegin: (offset: number, length: number) => { - if (isSettingsProperty(currentProperty, previousParents)) { + if (isSettingsProperty(currentProperty!, previousParents)) { // Settings started settingsPropertyIndex = previousParents.length; const position = model.getPositionAt(offset); @@ -323,10 +328,10 @@ function parse(model: ITextModel, isSettingsProperty: (currentProperty: string, endColumn: 0 }, value: null, - valueRange: null, - descriptionRanges: null, + valueRange: nullRange, + descriptionRanges: [], overrides: [], - overrideOf: overrideSetting + overrideOf: overrideSetting || undefined }; if (previousParents.length === settingsPropertyIndex + 1) { settings.push(setting); @@ -334,7 +339,7 @@ function parse(model: ITextModel, isSettingsProperty: (currentProperty: string, overrideSetting = setting; } } else { - overrideSetting.overrides.push(setting); + overrideSetting!.overrides!.push(setting); } } }, @@ -342,7 +347,7 @@ function parse(model: ITextModel, isSettingsProperty: (currentProperty: string, currentParent = previousParents.pop(); if (previousParents.length === settingsPropertyIndex + 1 || (previousParents.length === settingsPropertyIndex + 2 && overrideSetting !== null)) { // setting ended - const setting = previousParents.length === settingsPropertyIndex + 1 ? settings[settings.length - 1] : overrideSetting.overrides[overrideSetting.overrides.length - 1]; + const setting = previousParents.length === settingsPropertyIndex + 1 ? settings[settings.length - 1] : overrideSetting!.overrides![overrideSetting!.overrides!.length - 1]; if (setting) { const valueEndPosition = model.getPositionAt(offset + length); setting.valueRange = assign(setting.valueRange, { @@ -377,7 +382,7 @@ function parse(model: ITextModel, isSettingsProperty: (currentProperty: string, currentParent = previousParents.pop(); if (previousParents.length === settingsPropertyIndex + 1 || (previousParents.length === settingsPropertyIndex + 2 && overrideSetting !== null)) { // setting value ended - const setting = previousParents.length === settingsPropertyIndex + 1 ? settings[settings.length - 1] : overrideSetting.overrides[overrideSetting.overrides.length - 1]; + const setting = previousParents.length === settingsPropertyIndex + 1 ? settings[settings.length - 1] : overrideSetting!.overrides![overrideSetting!.overrides!.length - 1]; if (setting) { const valueEndPosition = model.getPositionAt(offset + length); setting.valueRange = assign(setting.valueRange, { @@ -394,7 +399,7 @@ function parse(model: ITextModel, isSettingsProperty: (currentProperty: string, onLiteralValue: onValue, onError: (error) => { const setting = settings[settings.length - 1]; - if (setting && (!setting.range || !setting.keyRange || !setting.valueRange)) { + if (setting && (isNullRange(setting.range) || isNullRange(setting.keyRange) || isNullRange(setting.valueRange))) { settings.pop(); } } @@ -408,8 +413,8 @@ function parse(model: ITextModel, isSettingsProperty: (currentProperty: string, settings } ], - title: null, - titleRange: null, + title: '', + titleRange: nullRange, range }] : []; } @@ -514,13 +519,15 @@ export class DefaultSettings extends Disposable { description: setting.description, key: setting.key, value: setting.value, - range: null, - valueRange: null, + keyRange: nullRange, + range: nullRange, + valueRange: nullRange, overrides: [], scope: ConfigurationScope.RESOURCE, type: setting.type, enum: setting.enum, - enumDescriptions: setting.enumDescriptions + enumDescriptions: setting.enumDescriptions, + descriptionRanges: [] }; } return null; @@ -528,9 +535,9 @@ export class DefaultSettings extends Disposable { return { id: 'mostCommonlyUsed', - range: null, + range: nullRange, title: nls.localize('commonlyUsed', "Commonly Used"), - titleRange: null, + titleRange: nullRange, sections: [ { settings @@ -552,7 +559,7 @@ export class DefaultSettings extends Disposable { if (!settingsGroup) { settingsGroup = find(result, g => g.title === title); if (!settingsGroup) { - settingsGroup = { sections: [{ settings: [] }], id: config.id, title: title, titleRange: null, range: null, contributedByExtension: !!config.contributedByExtension }; + settingsGroup = { sections: [{ settings: [] }], id: config.id || '', title: title || '', titleRange: nullRange, range: nullRange, contributedByExtension: !!config.contributedByExtension }; result.push(settingsGroup); } } else { @@ -561,7 +568,7 @@ export class DefaultSettings extends Disposable { } if (config.properties) { if (!settingsGroup) { - settingsGroup = { sections: [{ settings: [] }], id: config.id, title: config.id, titleRange: null, range: null, contributedByExtension: !!config.contributedByExtension }; + settingsGroup = { sections: [{ settings: [] }], id: config.id || '', title: config.id || '', titleRange: nullRange, range: nullRange, contributedByExtension: !!config.contributedByExtension }; result.push(settingsGroup); } const configurationSettings: ISetting[] = []; @@ -605,9 +612,9 @@ export class DefaultSettings extends Disposable { value, description, descriptionIsMarkdown: !prop.description, - range: null, - keyRange: null, - valueRange: null, + range: nullRange, + keyRange: nullRange, + valueRange: nullRange, descriptionRanges: [], overrides, scope: prop.scope, @@ -630,9 +637,9 @@ export class DefaultSettings extends Disposable { value: overrideSettings[key], description: [], descriptionIsMarkdown: false, - range: null, - keyRange: null, - valueRange: null, + range: nullRange, + keyRange: nullRange, + valueRange: nullRange, descriptionRanges: [], overrides: [] })); @@ -691,7 +698,7 @@ export class DefaultSettingsEditorModel extends AbstractSettingsModel implements super(); this._register(defaultSettings.onDidChange(() => this._onDidChangeGroups.fire())); - this._model = reference.object.textEditorModel; + this._model = reference.object.textEditorModel!; this._register(this.onDispose(() => reference.dispose())); } @@ -712,7 +719,7 @@ export class DefaultSettingsEditorModel extends AbstractSettingsModel implements return this.settingsGroups.slice(1); } - protected update(): IFilterResult { + protected update(): IFilterResult | null { if (this._model.isDisposed()) { return null; } @@ -825,10 +832,10 @@ export class DefaultSettingsEditorModel extends AbstractSettingsModel implements overrideOf: setting.overrideOf, tags: setting.tags, deprecationMessage: setting.deprecationMessage, - keyRange: undefined, - valueRange: undefined, + keyRange: nullRange, + valueRange: nullRange, descriptionIsMarkdown: undefined, - descriptionRanges: undefined + descriptionRanges: [] }; } @@ -852,9 +859,9 @@ export class DefaultSettingsEditorModel extends AbstractSettingsModel implements private getGroup(resultGroup: ISearchResultGroup): ISettingsGroup { return { id: resultGroup.id, - range: null, + range: nullRange, title: resultGroup.label, - titleRange: null, + titleRange: nullRange, sections: [ { settings: resultGroup.result.filterMatches.map(m => this.copySetting(m.setting)) @@ -899,7 +906,7 @@ class SettingsContentBuilder { this._contentByLines.push('}'); } - protected _pushGroup(group: ISettingsGroup, indent: string): ISetting { + protected _pushGroup(group: ISettingsGroup, indent: string): ISetting | null { let lastSetting: ISetting | null = null; const groupStart = this.lineCountWithOffset + 1; for (const section of group.sections) { @@ -959,7 +966,7 @@ class SettingsContentBuilder { if (setting.enumDescriptions && setting.enumDescriptions.some(desc => !!desc)) { setting.enumDescriptions.forEach((desc, i) => { - const displayEnum = escapeInvisibleChars(String(setting.enum[i])); + const displayEnum = escapeInvisibleChars(String(setting.enum![i])); const line = desc ? `${displayEnum}: ${fixSettingLink(desc)}` : displayEnum; @@ -974,7 +981,7 @@ class SettingsContentBuilder { private pushValue(setting: ISetting, preValueConent: string, indent: string): void { const valueString = JSON.stringify(setting.value, null, indent); if (valueString && (typeof setting.value === 'object')) { - if (setting.overrides.length) { + if (setting.overrides && setting.overrides.length) { this._contentByLines.push(preValueConent + ' {'); for (const subSetting of setting.overrides) { this.pushSetting(subSetting, indent + indent); diff --git a/src/vs/workbench/services/preferences/test/common/preferencesModel.test.ts b/src/vs/workbench/services/preferences/test/common/preferencesModel.test.ts index d4dae17cbc3..c428209859a 100644 --- a/src/vs/workbench/services/preferences/test/common/preferencesModel.test.ts +++ b/src/vs/workbench/services/preferences/test/common/preferencesModel.test.ts @@ -10,7 +10,7 @@ import { IConfigurationPropertySchema } from 'vs/platform/configuration/common/c suite('Preferences Model test', () => { class Tester { - private validator: (value: any) => string; + private validator: (value: any) => string | null; constructor(private settings: IConfigurationPropertySchema) { this.validator = createValidator(settings)!; @@ -24,8 +24,12 @@ suite('Preferences Model test', () => { assert.notEqual(this.validator(input), '', `Expected ${JSON.stringify(this.settings)} to reject \`${input}\`.`); return { withMessage: - (message) => assert(this.validator(input).indexOf(message) > -1, - `Expected error of ${JSON.stringify(this.settings)} on \`${input}\` to contain ${message}. Got ${this.validator(input)}.`) + (message) => { + const actual = this.validator(input); + assert.ok(actual); + assert(actual!.indexOf(message) > -1, + `Expected error of ${JSON.stringify(this.settings)} on \`${input}\` to contain ${message}. Got ${this.validator(input)}.`); + } }; } diff --git a/src/vs/workbench/services/progress/browser/progressService2.ts b/src/vs/workbench/services/progress/browser/progressService2.ts index da77ec66b8d..d267ae05770 100644 --- a/src/vs/workbench/services/progress/browser/progressService2.ts +++ b/src/vs/workbench/services/progress/browser/progressService2.ts @@ -15,6 +15,7 @@ import { ProgressBadge, IActivityService } from 'vs/workbench/services/activity/ import { INotificationService, Severity, INotificationHandle, INotificationActions } from 'vs/platform/notification/common/notification'; import { Action } from 'vs/base/common/actions'; import { Event } from 'vs/base/common/event'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; export class ProgressService2 implements IProgressService2 { @@ -38,8 +39,7 @@ export class ProgressService2 implements IProgressService2 { if (viewlet) { return this._withViewletProgress(location, task); } - console.warn(`Bad progress location: ${location}`); - return undefined; + return Promise.reject(new Error(`Bad progress location: ${location}`)); } switch (location) { @@ -54,8 +54,7 @@ export class ProgressService2 implements IProgressService2 { case ProgressLocation.Extensions: return this._withViewletProgress('workbench.view.extensions', task); default: - console.warn(`Bad progress location: ${location}`); - return undefined; + return Promise.reject(new Error(`Bad progress location: ${location}`)); } } @@ -267,3 +266,5 @@ export class ProgressService2 implements IProgressService2 { return promise; } } + +registerSingleton(IProgressService2, ProgressService2, true); diff --git a/src/vs/workbench/services/search/common/search.ts b/src/vs/workbench/services/search/common/search.ts index 16f1f7af4d9..a109834cc1a 100644 --- a/src/vs/workbench/services/search/common/search.ts +++ b/src/vs/workbench/services/search/common/search.ts @@ -71,9 +71,6 @@ export interface IFileQueryProps extends ICommonQueryPr type: QueryType.File; filePattern?: string; - // TODO: Remove this! - disregardExcludeSettings?: boolean; - /** * If true no results will be returned. Instead `limitHit` will indicate if at least one result exists or not. * Currently does not work with queries including a 'siblings clause'. diff --git a/src/vs/workbench/services/search/node/searchService.ts b/src/vs/workbench/services/search/node/searchService.ts index 3eaafa4f31e..a61f1d7a986 100644 --- a/src/vs/workbench/services/search/node/searchService.ts +++ b/src/vs/workbench/services/search/node/searchService.ts @@ -29,6 +29,7 @@ import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/un import { IRawSearchService, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, isSerializedSearchComplete, isSerializedSearchSuccess } from './search'; import { SearchChannelClient } from './searchIpc'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; export class SearchService extends Disposable implements ISearchService { _serviceBrand: any; @@ -586,3 +587,5 @@ export class DiskSearch implements ISearchResultProvider { return this.raw.clearCache(cacheKey); } } + +registerSingleton(ISearchService, SearchService, true); \ No newline at end of file diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index e7f9af79cf3..d056cc34156 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -12,7 +12,7 @@ import { URI } from 'vs/base/common/uri'; import { isUndefinedOrNull } from 'vs/base/common/types'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { ITextFileService, IAutoSaveConfiguration, ModelState, ITextFileEditorModel, ISaveOptions, ISaveErrorHandler, ISaveParticipant, StateChange, SaveReason, IRawTextContent, ILoadOptions, LoadReason } from 'vs/workbench/services/textfile/common/textfiles'; +import { ITextFileService, IAutoSaveConfiguration, ModelState, ITextFileEditorModel, ISaveOptions, ISaveErrorHandler, ISaveParticipant, StateChange, SaveReason, IRawTextContent, ILoadOptions, LoadReason, IResolvedTextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles'; import { EncodingMode } from 'vs/workbench/common/editor'; import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; @@ -70,7 +70,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil private saveSequentializer: SaveSequentializer; private disposed: boolean; private lastSaveAttemptTime: number; - private createTextEditorModelPromise: Promise; + private createTextEditorModelPromise: Promise | null; private inConflictMode: boolean; private inOrphanMode: boolean; private inErrorMode: boolean; @@ -435,7 +435,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil this.updateSavedVersionId(); } - private doCreateTextModel(resource: URI, value: ITextBufferFactory, backup: URI): Promise { + private doCreateTextModel(resource: URI, value: ITextBufferFactory, backup: URI | undefined): Promise { this.logService.trace('load() - created text editor model', this.resource); this.createTextEditorModelPromise = this.doLoadBackup(backup).then(backupContent => { @@ -443,7 +443,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Create model const hasBackupContent = !!backupContent; - this.createTextEditorModel(hasBackupContent ? backupContent : value, resource); + this.createTextEditorModel(backupContent ? backupContent : value, resource); // We restored a backup so we have to set the model as being dirty // We also want to trigger auto save if it is enabled to simulate the exact same behaviour @@ -480,15 +480,17 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // where `value` was captured in the content change listener closure scope. // Content Change - this._register(this.textEditorModel.onDidChangeContent(() => this.onModelContentChanged())); + if (this.textEditorModel) { + this._register(this.textEditorModel.onDidChangeContent(() => this.onModelContentChanged())); + } } - private doLoadBackup(backup: URI): Promise { + private doLoadBackup(backup: URI | undefined): Promise { if (!backup) { return Promise.resolve(null); } - return this.backupFileService.resolveBackupContent(backup).then(backupContent => backupContent, error => null /* ignore errors */); + return this.backupFileService.resolveBackupContent(backup).then(backupContent => backupContent || null, error => null /* ignore errors */); } protected getOrCreateMode(modeService: IModeService, preferredModeIds: string | undefined, firstLineText?: string): ILanguageSelection { @@ -511,7 +513,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // In this case we clear the dirty flag and emit a SAVED event to indicate this state. // Note: we currently only do this check when auto-save is turned off because there you see // a dirty indicator that you want to get rid of when undoing to the saved version. - if (!this.autoSaveAfterMilliesEnabled && this.textEditorModel.getAlternativeVersionId() === this.bufferSavedVersionId) { + if (!this.autoSaveAfterMilliesEnabled && this.textEditorModel && this.textEditorModel.getAlternativeVersionId() === this.bufferSavedVersionId) { this.logService.trace('onModelContentChanged() - model content changed back to last saved version', this.resource); // Clear flags @@ -609,7 +611,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil if (this.saveSequentializer.hasPendingSave(versionId)) { this.logService.trace(`doSave(${versionId}) - exit - found a pending save for versionId ${versionId}`, this.resource); - return this.saveSequentializer.pendingSave; + return this.saveSequentializer.pendingSave || Promise.resolve(undefined); } // Return early if not dirty (unless forced) or version changed meanwhile @@ -642,7 +644,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Push all edit operations to the undo stack so that the user has a chance to // Ctrl+Z back to the saved version. We only do this when auto-save is turned off - if (!this.autoSaveAfterMilliesEnabled) { + if (!this.autoSaveAfterMilliesEnabled && this.textEditorModel) { this.textEditorModel.pushStackElement(); } @@ -659,7 +661,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil }; this.blockModelContentChange = true; - saveParticipantPromise = TextFileEditorModel.saveParticipant.participate(this, { reason: options.reason }).then(onCompleteOrError, onCompleteOrError); + saveParticipantPromise = TextFileEditorModel.saveParticipant.participate(this as IResolvedTextFileEditorModel, { reason: options.reason }).then(onCompleteOrError, onCompleteOrError); } // mark the save participant as current pending save operation @@ -698,7 +700,11 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Save to Disk // mark the save operation as currently pending with the versionId (it might have changed from a save participant triggering) this.logService.trace(`doSave(${versionId}) - before updateContent()`, this.resource); - return this.saveSequentializer.setPending(newVersionId, this.fileService.updateContent(this.lastResolvedDiskStat.resource, this.createSnapshot(), { + const snapshot = this.createSnapshot(); + if (!snapshot) { + throw new Error('Invalid snapshot'); + } + return this.saveSequentializer.setPending(newVersionId, this.fileService.updateContent(this.lastResolvedDiskStat.resource, snapshot, { overwriteReadonly: options.overwriteReadonly, overwriteEncoding: options.overwriteEncoding, mtime: this.lastResolvedDiskStat.mtime, @@ -807,7 +813,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil return ''; } - private getTelemetryData(reason: number): Object { + private getTelemetryData(reason: number | undefined): Object { const ext = extname(this.resource); const fileName = basename(this.resource); const telemetryData = { @@ -834,7 +840,11 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } private doTouch(versionId: number): Promise { - return this.saveSequentializer.setPending(versionId, this.fileService.updateContent(this.lastResolvedDiskStat.resource, this.createSnapshot(), { + const snapshot = this.createSnapshot(); + if (!snapshot) { + throw new Error('invalid snapshot'); + } + return this.saveSequentializer.setPending(versionId, this.fileService.updateContent(this.lastResolvedDiskStat.resource, snapshot, { mtime: this.lastResolvedDiskStat.mtime, encoding: this.getEncoding(), etag: this.lastResolvedDiskStat.etag @@ -915,7 +925,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } getETag(): string | null { - return this.lastResolvedDiskStat ? this.lastResolvedDiskStat.etag : null; + return this.lastResolvedDiskStat ? this.lastResolvedDiskStat.etag || null : null; } hasState(state: ModelState): boolean { @@ -1100,8 +1110,8 @@ export class SaveSequentializer { // so that we can return a promise that completes when the save operation // has completed. if (!this._nextSave) { - let promiseResolve: (() => void) | undefined; - let promiseReject: ((error: Error) => void) | undefined; + let promiseResolve: () => void; + let promiseReject: (error: Error) => void; const promise = new Promise((resolve, reject) => { promiseResolve = resolve; promiseReject = reject; @@ -1110,8 +1120,8 @@ export class SaveSequentializer { this._nextSave = { run, promise, - promiseResolve: promiseResolve, - promiseReject: promiseReject + promiseResolve: promiseResolve!, + promiseReject: promiseReject! }; } diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts b/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts index b8f80e3f6b6..33dc731d7bb 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts @@ -117,7 +117,7 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE return 250; } - get(resource: URI): ITextFileEditorModel { + get(resource: URI): ITextFileEditorModel | undefined { return this.mapResourceToModel.get(resource); } @@ -153,12 +153,12 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE // Model does not exist else { - model = this.instantiationService.createInstance(TextFileEditorModel, resource, options ? options.encoding : undefined); + const newModel = model = this.instantiationService.createInstance(TextFileEditorModel, resource, options ? options.encoding : undefined); modelPromise = model.load(options); // Install state change listener this.mapResourceToStateChangeListener.set(resource, model.onDidStateChange(state => { - const event = new TextFileModelChangeEvent(model, state); + const event = new TextFileModelChangeEvent(newModel, state); switch (state) { case StateChange.DIRTY: this._onModelDirty.fire(event); @@ -183,7 +183,7 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE // Install model content change listener this.mapResourceToModelContentChangeListener.set(resource, model.onDidContentChange(e => { - this._onModelContentChanged.fire(new TextFileModelChangeEvent(model, e)); + this._onModelContentChanged.fire(new TextFileModelChangeEvent(newModel, e)); })); } @@ -207,7 +207,9 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE }, error => { // Free resources of this invalid model - model.dispose(); + if (model) { + model.dispose(); + } // Remove from pending loads this.mapResourceToPendingModelLoaders.delete(resource); diff --git a/src/vs/workbench/services/textfile/common/textFileService.ts b/src/vs/workbench/services/textfile/common/textFileService.ts index 5d98f0e093a..6504cd43b20 100644 --- a/src/vs/workbench/services/textfile/common/textFileService.ts +++ b/src/vs/workbench/services/textfile/common/textFileService.ts @@ -37,6 +37,7 @@ import { IModeService } from 'vs/editor/common/services/modeService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { coalesce } from 'vs/base/common/arrays'; import { trim } from 'vs/base/common/strings'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; export interface IBackupResult { didBackup: boolean; @@ -978,3 +979,5 @@ export class TextFileService extends Disposable implements ITextFileService { super.dispose(); } } + +registerSingleton(ITextFileService, TextFileService); \ No newline at end of file diff --git a/src/vs/workbench/services/textfile/common/textfiles.ts b/src/vs/workbench/services/textfile/common/textfiles.ts index 43d88b808ae..0e249fb61f7 100644 --- a/src/vs/workbench/services/textfile/common/textfiles.ts +++ b/src/vs/workbench/services/textfile/common/textfiles.ts @@ -10,7 +10,7 @@ import { IEncodingSupport, ConfirmResult, IRevertOptions } from 'vs/workbench/co import { IBaseStat, IResolveContentOptions, ITextSnapshot } from 'vs/platform/files/common/files'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { ITextEditorModel } from 'vs/editor/common/services/resolverService'; -import { ITextBufferFactory } from 'vs/editor/common/model'; +import { ITextBufferFactory, ITextModel } from 'vs/editor/common/model'; import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; /** @@ -29,7 +29,7 @@ export interface ISaveParticipant { /** * Participate in a save of a model. Allows to change the model before it is being saved to disk. */ - participate(model: ITextFileEditorModel, env: { reason: SaveReason }): Promise; + participate(model: IResolvedTextFileEditorModel, env: { reason: SaveReason }): Promise; } /** @@ -189,7 +189,7 @@ export interface ITextFileEditorModelManager { onModelsSaved: Event; onModelsReverted: Event; - get(resource: URI): ITextFileEditorModel; + get(resource: URI): ITextFileEditorModel | undefined; getAll(resource?: URI): ITextFileEditorModel[]; @@ -240,7 +240,7 @@ export interface ITextFileEditorModel extends ITextEditorModel, IEncodingSupport updatePreferredEncoding(encoding: string): void; - save(options?: ISaveOptions): Promise; + save(options?: ISaveOptions): Promise | undefined; load(options?: ILoadOptions): Promise; @@ -255,6 +255,10 @@ export interface ITextFileEditorModel extends ITextEditorModel, IEncodingSupport isDisposed(): boolean; } +export interface IResolvedTextFileEditorModel extends ITextFileEditorModel { + readonly textEditorModel: ITextModel; +} + export interface IWillMoveEvent { oldResource: URI; diff --git a/src/vs/workbench/services/textfile/node/textResourcePropertiesService.ts b/src/vs/workbench/services/textfile/node/textResourcePropertiesService.ts index 86503bc02d5..2d54b0c9e43 100644 --- a/src/vs/workbench/services/textfile/node/textResourcePropertiesService.ts +++ b/src/vs/workbench/services/textfile/node/textResourcePropertiesService.ts @@ -45,7 +45,7 @@ export class TextResourcePropertiesService implements ITextResourcePropertiesSer if (remoteAuthority) { if (resource.scheme !== Schemas.file) { const osCacheKey = `resource.authority.os.${remoteAuthority}`; - os = this.remoteEnvironment ? this.remoteEnvironment.os : /* Get it from cache */ this.storageService.getInteger(osCacheKey, StorageScope.WORKSPACE, OS); + os = this.remoteEnvironment ? this.remoteEnvironment.os : /* Get it from cache */ this.storageService.getNumber(osCacheKey, StorageScope.WORKSPACE, OS); this.storageService.store(osCacheKey, os, StorageScope.WORKSPACE); } } diff --git a/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts b/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts index 3b011295c08..bc4a2084711 100644 --- a/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts +++ b/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts @@ -12,10 +12,11 @@ import { IModelService } from 'vs/editor/common/services/modelService'; import { ResourceEditorModel } from 'vs/workbench/common/editor/resourceEditorModel'; import { ITextFileService, LoadReason } from 'vs/workbench/services/textfile/common/textfiles'; import * as network from 'vs/base/common/network'; -import { ITextModelService, ITextModelContentProvider, ITextEditorModel } from 'vs/editor/common/services/resolverService'; +import { ITextModelService, ITextModelContentProvider, ITextEditorModel, IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService'; import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; import { IFileService } from 'vs/platform/files/common/files'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; class ResourceModelCollection extends ReferenceCollection> { @@ -103,7 +104,7 @@ class ResourceModelCollection extends ReferenceCollection { const resource = URI.parse(key); const providers = this.providers[resource.scheme] || []; - const factories = providers.map(p => () => Promise.resolve(p.provideTextContent(resource))); + const factories = providers.map(p => () => Promise.resolve(p.provideTextContent(resource))); return first(factories).then(model => { if (!model) { @@ -129,15 +130,15 @@ export class TextModelResolverService implements ITextModelService { this.resourceModelCollection = instantiationService.createInstance(ResourceModelCollection); } - createModelReference(resource: URI): Promise> { + createModelReference(resource: URI): Promise> { return this._createModelReference(resource); } - private _createModelReference(resource: URI): Promise> { + private _createModelReference(resource: URI): Promise> { // Untitled Schema: go through cached input if (resource.scheme === network.Schemas.untitled) { - return this.untitledEditorService.loadOrCreate({ resource }).then(model => new ImmortalReference(model)); + return this.untitledEditorService.loadOrCreate({ resource }).then(model => new ImmortalReference(model as IResolvedTextEditorModel)); } // InMemory Schema: go through model service cache @@ -148,7 +149,7 @@ export class TextModelResolverService implements ITextModelService { return Promise.reject(new Error('Cant resolve inmemory resource')); } - return Promise.resolve(new ImmortalReference(this.instantiationService.createInstance(ResourceEditorModel, resource))); + return Promise.resolve(new ImmortalReference(this.instantiationService.createInstance(ResourceEditorModel, resource) as IResolvedTextEditorModel)); } const ref = this.resourceModelCollection.acquire(resource.toString()); @@ -171,3 +172,5 @@ export class TextModelResolverService implements ITextModelService { return this.resourceModelCollection.hasTextModelContentProvider(scheme); } } + +registerSingleton(ITextModelService, TextModelResolverService, true); \ No newline at end of file diff --git a/src/vs/workbench/services/viewlet/browser/viewlet.ts b/src/vs/workbench/services/viewlet/browser/viewlet.ts index b87357e7630..88410fceb95 100644 --- a/src/vs/workbench/services/viewlet/browser/viewlet.ts +++ b/src/vs/workbench/services/viewlet/browser/viewlet.ts @@ -22,7 +22,7 @@ export interface IViewletService { /** * Opens a viewlet with the given identifier and pass keyboard focus to it if specified. */ - openViewlet(id: string, focus?: boolean): Promise; + openViewlet(id: string | undefined, focus?: boolean): Promise; /** * Returns the current active viewlet or null if none. diff --git a/src/vs/workbench/services/workspace/node/workspaceEditingService.ts b/src/vs/workbench/services/workspace/node/workspaceEditingService.ts index 1003e3f040f..c79cd34347b 100644 --- a/src/vs/workbench/services/workspace/node/workspaceEditingService.ts +++ b/src/vs/workbench/services/workspace/node/workspaceEditingService.ts @@ -10,7 +10,6 @@ import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/ import { IWindowService, MessageBoxOptions, IWindowsService } from 'vs/platform/windows/common/windows'; import { IJSONEditingService, JSONEditingError, JSONEditingErrorCode } from 'vs/workbench/services/configuration/common/jsonEditing'; import { IWorkspaceIdentifier, IWorkspaceFolderCreationData, IWorkspacesService, rewriteWorkspaceFileForNewLocation, WORKSPACE_FILTER } from 'vs/platform/workspaces/common/workspaces'; -import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { WorkspaceService } from 'vs/workbench/services/configuration/node/configurationService'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { StorageService } from 'vs/platform/storage/node/storageService'; @@ -29,6 +28,8 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' import { ILifecycleService, ShutdownReason } from 'vs/platform/lifecycle/common/lifecycle'; import { IFileDialogService, IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { mnemonicButtonLabel } from 'vs/base/common/labels'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; export class WorkspaceEditingService implements IWorkspaceEditingService { @@ -38,7 +39,7 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { @IJSONEditingService private readonly jsonEditingService: IJSONEditingService, @IWorkspaceContextService private readonly contextService: WorkspaceService, @IWindowService private readonly windowService: IWindowService, - @IWorkspaceConfigurationService private readonly workspaceConfigurationService: IWorkspaceConfigurationService, + @IConfigurationService private readonly workspaceConfigurationService: IConfigurationService, @IStorageService private readonly storageService: IStorageService, @IExtensionService private readonly extensionService: IExtensionService, @IBackupFileService private readonly backupFileService: IBackupFileService, @@ -426,3 +427,5 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { return undefined; } } + +registerSingleton(IWorkspaceEditingService, WorkspaceEditingService, true); \ No newline at end of file diff --git a/src/vs/workbench/test/browser/parts/editor/baseEditor.test.ts b/src/vs/workbench/test/browser/parts/editor/baseEditor.test.ts index b959ede3928..4f7400dbc91 100644 --- a/src/vs/workbench/test/browser/parts/editor/baseEditor.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/baseEditor.test.ts @@ -180,7 +180,7 @@ suite('Workbench base editor', () => { }); test('Editor Input Factory', function () { - EditorInputRegistry.setInstantiationService(workbenchInstantiationService()); + workbenchInstantiationService().invokeFunction(accessor => EditorInputRegistry.start(accessor)); EditorInputRegistry.registerEditorInputFactory('myInputId', MyInputFactory); let factory = EditorInputRegistry.getEditorInputFactory('myInputId'); diff --git a/src/vs/workbench/test/browser/parts/views/views.test.ts b/src/vs/workbench/test/browser/parts/views/views.test.ts index 11fd665875f..5b0dd4cc9af 100644 --- a/src/vs/workbench/test/browser/parts/views/views.test.ts +++ b/src/vs/workbench/test/browser/parts/views/views.test.ts @@ -63,7 +63,7 @@ suite('ContributableViewsModel', () => { const viewDescriptor: IViewDescriptor = { id: 'view1', - ctor: null, + ctorDescriptor: null, name: 'Test View 1' }; @@ -89,7 +89,7 @@ suite('ContributableViewsModel', () => { const viewDescriptor: IViewDescriptor = { id: 'view1', - ctor: null, + ctorDescriptor: null, name: 'Test View 1', when: ContextKeyExpr.equals('showview1', true) }; @@ -128,8 +128,8 @@ suite('ContributableViewsModel', () => { const model = new ContributableViewsModel(container, viewsService); const seq = new ViewDescriptorSequence(model); - const view1: IViewDescriptor = { id: 'view1', ctor: null, name: 'Test View 1' }; - const view2: IViewDescriptor = { id: 'view2', ctor: null, name: 'Test View 2', when: ContextKeyExpr.equals('showview2', true) }; + const view1: IViewDescriptor = { id: 'view1', ctorDescriptor: null, name: 'Test View 1' }; + const view2: IViewDescriptor = { id: 'view2', ctorDescriptor: null, name: 'Test View 2', when: ContextKeyExpr.equals('showview2', true) }; ViewsRegistry.registerViews([view1, view2], container); assert.deepEqual(model.visibleViewDescriptors, [view1], 'only view1 should be visible'); @@ -151,8 +151,8 @@ suite('ContributableViewsModel', () => { const model = new ContributableViewsModel(container, viewsService); const seq = new ViewDescriptorSequence(model); - const view1: IViewDescriptor = { id: 'view1', ctor: null, name: 'Test View 1', when: ContextKeyExpr.equals('showview1', true) }; - const view2: IViewDescriptor = { id: 'view2', ctor: null, name: 'Test View 2' }; + const view1: IViewDescriptor = { id: 'view1', ctorDescriptor: null, name: 'Test View 1', when: ContextKeyExpr.equals('showview1', true) }; + const view2: IViewDescriptor = { id: 'view2', ctorDescriptor: null, name: 'Test View 2' }; ViewsRegistry.registerViews([view1, view2], container); assert.deepEqual(model.visibleViewDescriptors, [view2], 'only view2 should be visible'); @@ -174,9 +174,9 @@ suite('ContributableViewsModel', () => { const model = new ContributableViewsModel(container, viewsService); const seq = new ViewDescriptorSequence(model); - const view1: IViewDescriptor = { id: 'view1', ctor: null, name: 'Test View 1', canToggleVisibility: true }; - const view2: IViewDescriptor = { id: 'view2', ctor: null, name: 'Test View 2', canToggleVisibility: true }; - const view3: IViewDescriptor = { id: 'view3', ctor: null, name: 'Test View 3', canToggleVisibility: true }; + const view1: IViewDescriptor = { id: 'view1', ctorDescriptor: null, name: 'Test View 1', canToggleVisibility: true }; + const view2: IViewDescriptor = { id: 'view2', ctorDescriptor: null, name: 'Test View 2', canToggleVisibility: true }; + const view3: IViewDescriptor = { id: 'view3', ctorDescriptor: null, name: 'Test View 3', canToggleVisibility: true }; ViewsRegistry.registerViews([view1, view2, view3], container); assert.deepEqual(model.visibleViewDescriptors, [view1, view2, view3]); @@ -219,9 +219,9 @@ suite('ContributableViewsModel', () => { const model = new ContributableViewsModel(container, viewsService); const seq = new ViewDescriptorSequence(model); - const view1: IViewDescriptor = { id: 'view1', ctor: null, name: 'Test View 1' }; - const view2: IViewDescriptor = { id: 'view2', ctor: null, name: 'Test View 2' }; - const view3: IViewDescriptor = { id: 'view3', ctor: null, name: 'Test View 3' }; + const view1: IViewDescriptor = { id: 'view1', ctorDescriptor: null, name: 'Test View 1' }; + const view2: IViewDescriptor = { id: 'view2', ctorDescriptor: null, name: 'Test View 2' }; + const view3: IViewDescriptor = { id: 'view3', ctorDescriptor: null, name: 'Test View 3' }; ViewsRegistry.registerViews([view1, view2, view3], container); assert.deepEqual(model.visibleViewDescriptors, [view1, view2, view3], 'model views should be OK'); diff --git a/src/vs/workbench/test/common/editor/editorGroups.test.ts b/src/vs/workbench/test/common/editor/editorGroups.test.ts index bc186f8d331..fde39c097be 100644 --- a/src/vs/workbench/test/common/editor/editorGroups.test.ts +++ b/src/vs/workbench/test/common/editor/editorGroups.test.ts @@ -215,7 +215,7 @@ suite('Workbench editor groups', () => { }); test('group serialization', function () { - Registry.as(EditorExtensions.EditorInputFactories).setInstantiationService(inst()); + inst().invokeFunction(accessor => Registry.as(EditorExtensions.EditorInputFactories).start(accessor)); const group = createGroup(); const input1 = input(); @@ -1003,7 +1003,7 @@ suite('Workbench editor groups', () => { config.setUserConfiguration('workbench', { editor: { openPositioning: 'right' } }); inst.stub(IConfigurationService, config); - (Registry.as(EditorExtensions.EditorInputFactories)).setInstantiationService(inst); + inst.invokeFunction(accessor => Registry.as(EditorExtensions.EditorInputFactories).start(accessor)); let group = createGroup(); @@ -1037,7 +1037,7 @@ suite('Workbench editor groups', () => { config.setUserConfiguration('workbench', { editor: { openPositioning: 'right' } }); inst.stub(IConfigurationService, config); - (Registry.as(EditorExtensions.EditorInputFactories)).setInstantiationService(inst); + inst.invokeFunction(accessor => Registry.as(EditorExtensions.EditorInputFactories).start(accessor)); let group1 = createGroup(); @@ -1107,7 +1107,7 @@ suite('Workbench editor groups', () => { config.setUserConfiguration('workbench', { editor: { openPositioning: 'right' } }); inst.stub(IConfigurationService, config); - (Registry.as(EditorExtensions.EditorInputFactories)).setInstantiationService(inst); + inst.invokeFunction(accessor => Registry.as(EditorExtensions.EditorInputFactories).start(accessor)); let group = createGroup(); @@ -1151,7 +1151,7 @@ suite('Workbench editor groups', () => { config.setUserConfiguration('workbench', { editor: { openPositioning: 'right' } }); inst.stub(IConfigurationService, config); - (Registry.as(EditorExtensions.EditorInputFactories)).setInstantiationService(inst); + inst.invokeFunction(accessor => Registry.as(EditorExtensions.EditorInputFactories).start(accessor)); let group1 = createGroup(); let group2 = createGroup(); diff --git a/src/vs/workbench/test/common/editor/untitledEditor.test.ts b/src/vs/workbench/test/common/editor/untitledEditor.test.ts index 5ce1d30ace9..62403c94f5c 100644 --- a/src/vs/workbench/test/common/editor/untitledEditor.test.ts +++ b/src/vs/workbench/test/common/editor/untitledEditor.test.ts @@ -68,7 +68,7 @@ suite('Workbench untitled editors', () => { assert.equal(service.getAll().length, 1); // dirty - input2.resolve().then((model: UntitledEditorModel) => { + input2.resolve().then(model => { assert.ok(!service.isDirty(input2.getResource())); const listener = service.onDidChangeDirty(resource => { @@ -112,7 +112,7 @@ suite('Workbench untitled editors', () => { const input = service.createOrGet(); // dirty - return input.resolve().then((model: UntitledEditorModel) => { + return input.resolve().then(model => { model.textEditorModel.setValue('foo bar'); assert.ok(model.isDirty()); @@ -126,14 +126,14 @@ suite('Workbench untitled editors', () => { test('Untitled via loadOrCreate', function () { const service = accessor.untitledEditorService; service.loadOrCreate().then(model1 => { - model1.textEditorModel.setValue('foo bar'); + model1.textEditorModel!.setValue('foo bar'); assert.ok(model1.isDirty()); - model1.textEditorModel.setValue(''); + model1.textEditorModel!.setValue(''); assert.ok(!model1.isDirty()); return service.loadOrCreate({ initialValue: 'Hello World' }).then(model2 => { - assert.equal(snapshotToString(model2.createSnapshot()), 'Hello World'); + assert.equal(snapshotToString(model2.createSnapshot()!), 'Hello World'); const input = service.createOrGet(); @@ -169,7 +169,7 @@ suite('Workbench untitled editors', () => { const input = service.createOrGet(file); // dirty - return input.resolve().then((model: UntitledEditorModel) => { + return input.resolve().then(model => { model.textEditorModel.setValue('foo bar'); assert.ok(model.isDirty()); @@ -223,7 +223,7 @@ suite('Workbench untitled editors', () => { }); // dirty - return input.resolve().then((model: UntitledEditorModel) => { + return input.resolve().then(model => { model.setEncoding('utf16'); assert.equal(counter, 1); @@ -245,7 +245,7 @@ suite('Workbench untitled editors', () => { assert.equal(r.toString(), input.getResource().toString()); }); - return input.resolve().then((model: UntitledEditorModel) => { + return input.resolve().then(model => { model.textEditorModel.setValue('foo'); assert.equal(counter, 0, 'Dirty model should not trigger event immediately'); @@ -288,7 +288,7 @@ suite('Workbench untitled editors', () => { assert.equal(r.toString(), input.getResource().toString()); }); - return input.resolve().then((model: UntitledEditorModel) => { + return input.resolve().then(model => { assert.equal(counter, 0); input.dispose(); assert.equal(counter, 1); diff --git a/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts b/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts index 49c0a50275a..f7c066436d5 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts @@ -87,15 +87,15 @@ suite('ExtHostLanguageFeatureCommands', function () { instantiationService.stub(IModelService, { _serviceBrand: IModelService, getModel(): any { return model; }, - createModel(): any { throw new Error(); }, - updateModel(): any { throw new Error(); }, - setMode(): any { throw new Error(); }, - destroyModel(): any { throw new Error(); }, - getModels(): any { throw new Error(); }, - onModelAdded: undefined, - onModelModeChanged: undefined, - onModelRemoved: undefined, - getCreationOptions(): any { throw new Error(); } + createModel() { throw new Error(); }, + updateModel() { throw new Error(); }, + setMode() { throw new Error(); }, + destroyModel() { throw new Error(); }, + getModels() { throw new Error(); }, + onModelAdded: undefined!, + onModelModeChanged: undefined!, + onModelRemoved: undefined!, + getCreationOptions() { throw new Error(); } }); inst = instantiationService; } @@ -190,8 +190,8 @@ suite('ExtHostLanguageFeatureCommands', function () { test('executeWorkspaceSymbolProvider should accept empty string, #39522', async function () { disposables.push(extHost.registerWorkspaceSymbolProvider(nullExtensionDescription, { - provideWorkspaceSymbols(query) { - return [new types.SymbolInformation('hello', types.SymbolKind.Array, new types.Range(0, 0, 0, 0), URI.parse('foo:bar'))]; + provideWorkspaceSymbols(query): vscode.SymbolInformation[] { + return [new types.SymbolInformation('hello', types.SymbolKind.Array, new types.Range(0, 0, 0, 0), URI.parse('foo:bar')) as vscode.SymbolInformation]; } })); diff --git a/src/vs/workbench/test/electron-browser/api/extHostDiagnostics.test.ts b/src/vs/workbench/test/electron-browser/api/extHostDiagnostics.test.ts index de772e5d024..02419a4d34e 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostDiagnostics.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostDiagnostics.test.ts @@ -188,7 +188,7 @@ suite('ExtHostDiagnostics', () => { lastEntries = undefined!; }); - test('don\'t send message when not making a change', function () { + test('do send message when not making a change', function () { let changeCount = 0; let eventCount = 0; @@ -209,7 +209,7 @@ suite('ExtHostDiagnostics', () => { assert.equal(eventCount, 1); collection.set(uri, [diag]); - assert.equal(changeCount, 1); + assert.equal(changeCount, 2); assert.equal(eventCount, 2); }); @@ -418,10 +418,10 @@ suite('ExtHostDiagnostics', () => { assert.equal(callCount, 1); collection.set(URI.parse('test:me'), array); - assert.equal(callCount, 1); // equal array + assert.equal(callCount, 2); // equal array array.push(diag2); collection.set(URI.parse('test:me'), array); - assert.equal(callCount, 2); // same but un-equal array + assert.equal(callCount, 3); // same but un-equal array }); }); diff --git a/src/vs/workbench/test/electron-browser/api/extHostTextEditor.test.ts b/src/vs/workbench/test/electron-browser/api/extHostTextEditor.test.ts index b7cdcfcf6e9..a9bcfbeca01 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostTextEditor.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostTextEditor.test.ts @@ -292,7 +292,7 @@ suite('ExtHostTextEditorOptions', () => { }); test('ignores invalid indentSize 1', () => { - opts.indentSize = null; + opts.indentSize = null!; assertState(opts, { tabSize: 4, indentSize: 4, diff --git a/src/vs/workbench/test/electron-browser/api/extHostTreeViews.test.ts b/src/vs/workbench/test/electron-browser/api/extHostTreeViews.test.ts index f371dd2f5d9..7e626d3985b 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostTreeViews.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostTreeViews.test.ts @@ -625,7 +625,7 @@ suite('ExtHostTreeView', function () { getTreeItem: (element: { key: string }): TreeItem => { return getTreeItem(element.key); }, - getParent: ({ key }: { key: string }): { key: string } => { + getParent: ({ key }: { key: string }): { key: string } | undefined => { const parentKey = key.substring(0, key.length - 1); return parentKey ? new Key(parentKey) : undefined; }, @@ -672,7 +672,7 @@ suite('ExtHostTreeView', function () { return parent; } - function getChildren(key: string): string[] { + function getChildren(key: string | undefined): string[] { if (!key) { return Object.keys(tree); } 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 40de7dbe2b3..5974b4713f5 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostWorkspace.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostWorkspace.test.ts @@ -4,16 +4,20 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { URI } from 'vs/base/common/uri'; -import { basename } from 'vs/base/common/path'; -import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace'; -import { TestRPCProtocol } from './testRPCProtocol'; -import { IWorkspaceFolderData } from 'vs/platform/workspace/common/workspace'; -import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; -import { NullLogService, ILogService } from 'vs/platform/log/common/log'; -import { IMainContext, IWorkspaceData } from 'vs/workbench/api/node/extHost.protocol'; +import { CancellationToken } from 'vs/base/common/cancellation'; import { Counter } from 'vs/base/common/numbers'; +import { basename } from 'vs/base/common/path'; +import { URI, UriComponents } from 'vs/base/common/uri'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { ILogService, NullLogService } from 'vs/platform/log/common/log'; +import { IWorkspaceFolderData } from 'vs/platform/workspace/common/workspace'; +import { MainThreadWorkspace } from 'vs/workbench/api/electron-browser/mainThreadWorkspace'; +import { IMainContext, IWorkspaceData, MainContext } from 'vs/workbench/api/node/extHost.protocol'; +import { RelativePattern } from 'vs/workbench/api/node/extHostTypes'; +import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace'; +import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; +import { mock } from 'vs/workbench/test/electron-browser/api/mock'; +import { TestRPCProtocol } from './testRPCProtocol'; function createExtHostWorkspace(mainContext: IMainContext, data: IWorkspaceData, logService: ILogService, requestIdProvider: Counter): ExtHostWorkspace { const result = new ExtHostWorkspace(mainContext, logService, requestIdProvider); @@ -563,4 +567,107 @@ suite('ExtHostWorkspace', function () { function asUpdateWorkspaceFolderData(uri: URI, name?: string): { uri: URI, name?: string } { return { uri, name }; } + + test('findFiles - string include', () => { + const root = '/project/foo'; + const rpcProtocol = new TestRPCProtocol(); + + let mainThreadCalled = false; + rpcProtocol.set(MainContext.MainThreadWorkspace, new class extends mock() { + $startFileSearch(includePattern: string, _includeFolder: UriComponents | undefined, excludePatternOrDisregardExcludes: string | false, maxResults: number, token: CancellationToken): Promise { + mainThreadCalled = true; + assert.equal(includePattern, 'foo'); + assert.equal(_includeFolder, undefined); + assert.equal(excludePatternOrDisregardExcludes, undefined); + assert.equal(maxResults, 10); + return Promise.resolve(undefined); + } + }); + + const ws = createExtHostWorkspace(rpcProtocol, { id: 'foo', folders: [aWorkspaceFolderData(URI.file(root), 0)], name: 'Test' }, new NullLogService(), new Counter()); + return ws.findFiles('foo', undefined!, 10, new ExtensionIdentifier('test')).then(() => { + assert(mainThreadCalled, 'mainThreadCalled'); + }); + }); + + test('findFiles - RelativePattern include', () => { + const root = '/project/foo'; + const rpcProtocol = new TestRPCProtocol(); + + let mainThreadCalled = false; + rpcProtocol.set(MainContext.MainThreadWorkspace, new class extends mock() { + $startFileSearch(includePattern: string, _includeFolder: UriComponents | undefined, excludePatternOrDisregardExcludes: string | false, maxResults: number, token: CancellationToken): Promise { + mainThreadCalled = true; + assert.equal(includePattern, 'glob/**'); + assert.deepEqual(_includeFolder, URI.file('/other/folder').toJSON()); + assert.equal(excludePatternOrDisregardExcludes, undefined); + return Promise.resolve(undefined); + } + }); + + const ws = createExtHostWorkspace(rpcProtocol, { id: 'foo', folders: [aWorkspaceFolderData(URI.file(root), 0)], name: 'Test' }, new NullLogService(), new Counter()); + return ws.findFiles(new RelativePattern('/other/folder', 'glob/**'), undefined!, 10, new ExtensionIdentifier('test')).then(() => { + assert(mainThreadCalled, 'mainThreadCalled'); + }); + }); + + test('findFiles - no excludes', () => { + const root = '/project/foo'; + const rpcProtocol = new TestRPCProtocol(); + + let mainThreadCalled = false; + rpcProtocol.set(MainContext.MainThreadWorkspace, new class extends mock() { + $startFileSearch(includePattern: string, _includeFolder: UriComponents | undefined, excludePatternOrDisregardExcludes: string | false, maxResults: number, token: CancellationToken): Promise { + mainThreadCalled = true; + assert.equal(includePattern, 'glob/**'); + assert.deepEqual(_includeFolder, URI.file('/other/folder').toJSON()); + assert.equal(excludePatternOrDisregardExcludes, false); + return Promise.resolve(undefined); + } + }); + + const ws = createExtHostWorkspace(rpcProtocol, { id: 'foo', folders: [aWorkspaceFolderData(URI.file(root), 0)], name: 'Test' }, new NullLogService(), new Counter()); + return ws.findFiles(new RelativePattern('/other/folder', 'glob/**'), null!, 10, new ExtensionIdentifier('test')).then(() => { + assert(mainThreadCalled, 'mainThreadCalled'); + }); + }); + + test('findFiles - with cancelled token', () => { + const root = '/project/foo'; + const rpcProtocol = new TestRPCProtocol(); + + let mainThreadCalled = false; + rpcProtocol.set(MainContext.MainThreadWorkspace, new class extends mock() { + $startFileSearch(includePattern: string, _includeFolder: UriComponents | undefined, excludePatternOrDisregardExcludes: string | false, maxResults: number, token: CancellationToken): Promise { + mainThreadCalled = true; + return Promise.resolve(undefined); + } + }); + + const ws = createExtHostWorkspace(rpcProtocol, { id: 'foo', folders: [aWorkspaceFolderData(URI.file(root), 0)], name: 'Test' }, new NullLogService(), new Counter()); + + const token = CancellationToken.Cancelled; + return ws.findFiles(new RelativePattern('/other/folder', 'glob/**'), null!, 10, new ExtensionIdentifier('test'), token).then(() => { + assert(!mainThreadCalled, '!mainThreadCalled'); + }); + }); + + test('findFiles - RelativePattern exclude', () => { + const root = '/project/foo'; + const rpcProtocol = new TestRPCProtocol(); + + let mainThreadCalled = false; + rpcProtocol.set(MainContext.MainThreadWorkspace, new class extends mock() { + $startFileSearch(includePattern: string, _includeFolder: UriComponents | undefined, excludePatternOrDisregardExcludes: string | false, maxResults: number, token: CancellationToken): Promise { + mainThreadCalled = true; + assert(excludePatternOrDisregardExcludes, 'glob/**'); // Note that the base portion is ignored, see #52651 + return Promise.resolve(undefined); + } + }); + + const ws = createExtHostWorkspace(rpcProtocol, { id: 'foo', folders: [aWorkspaceFolderData(URI.file(root), 0)], name: 'Test' }, new NullLogService(), new Counter()); + return ws.findFiles('', new RelativePattern(root, 'glob/**'), 10, new ExtensionIdentifier('test')).then(() => { + assert(mainThreadCalled, 'mainThreadCalled'); + }); + }); }); diff --git a/src/vs/workbench/test/electron-browser/api/mainThreadConfiguration.test.ts b/src/vs/workbench/test/electron-browser/api/mainThreadConfiguration.test.ts index 4be29bb9ecd..01d75b06fd5 100644 --- a/src/vs/workbench/test/electron-browser/api/mainThreadConfiguration.test.ts +++ b/src/vs/workbench/test/electron-browser/api/mainThreadConfiguration.test.ts @@ -63,7 +63,7 @@ suite('MainThreadConfiguration', function () { instantiationService.stub(IWorkspaceContextService, { getWorkbenchState: () => WorkbenchState.WORKSPACE }); const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(proxy)); - testObject.$updateConfigurationOption(null, 'extHostConfiguration.resource', 'value', null); + testObject.$updateConfigurationOption(null, 'extHostConfiguration.resource', 'value', undefined); assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][3]); }); @@ -81,7 +81,7 @@ suite('MainThreadConfiguration', function () { instantiationService.stub(IWorkspaceContextService, { getWorkbenchState: () => WorkbenchState.FOLDER }); const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(proxy)); - testObject.$updateConfigurationOption(null, 'extHostConfiguration.resource', 'value', null); + testObject.$updateConfigurationOption(null, 'extHostConfiguration.resource', 'value', undefined); assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][3]); }); @@ -90,7 +90,7 @@ suite('MainThreadConfiguration', function () { instantiationService.stub(IWorkspaceContextService, { getWorkbenchState: () => WorkbenchState.WORKSPACE }); const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(proxy)); - testObject.$updateConfigurationOption(null, 'extHostConfiguration.window', 'value', null); + testObject.$updateConfigurationOption(null, 'extHostConfiguration.window', 'value', undefined); assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][3]); }); @@ -117,7 +117,7 @@ suite('MainThreadConfiguration', function () { instantiationService.stub(IWorkspaceContextService, { getWorkbenchState: () => WorkbenchState.FOLDER }); const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(proxy)); - testObject.$updateConfigurationOption(null, 'extHostConfiguration.window', 'value', null); + testObject.$updateConfigurationOption(null, 'extHostConfiguration.window', 'value', undefined); assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][3]); }); @@ -162,7 +162,7 @@ suite('MainThreadConfiguration', function () { instantiationService.stub(IWorkspaceContextService, { getWorkbenchState: () => WorkbenchState.WORKSPACE }); const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(proxy)); - testObject.$removeConfigurationOption(null, 'extHostConfiguration.resource', null); + testObject.$removeConfigurationOption(null, 'extHostConfiguration.resource', undefined); assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][3]); }); @@ -180,7 +180,7 @@ suite('MainThreadConfiguration', function () { instantiationService.stub(IWorkspaceContextService, { getWorkbenchState: () => WorkbenchState.FOLDER }); const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(proxy)); - testObject.$removeConfigurationOption(null, 'extHostConfiguration.resource', null); + testObject.$removeConfigurationOption(null, 'extHostConfiguration.resource', undefined); assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][3]); }); @@ -189,7 +189,7 @@ suite('MainThreadConfiguration', function () { instantiationService.stub(IWorkspaceContextService, { getWorkbenchState: () => WorkbenchState.WORKSPACE }); const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(proxy)); - testObject.$removeConfigurationOption(null, 'extHostConfiguration.window', null); + testObject.$removeConfigurationOption(null, 'extHostConfiguration.window', undefined); assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][3]); }); @@ -216,7 +216,7 @@ suite('MainThreadConfiguration', function () { instantiationService.stub(IWorkspaceContextService, { getWorkbenchState: () => WorkbenchState.FOLDER }); const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(proxy)); - testObject.$removeConfigurationOption(null, 'extHostConfiguration.window', null); + testObject.$removeConfigurationOption(null, 'extHostConfiguration.window', undefined); assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][3]); }); diff --git a/src/vs/workbench/test/electron-browser/api/mainThreadEditors.test.ts b/src/vs/workbench/test/electron-browser/api/mainThreadEditors.test.ts index 049ba68c009..74633e4d5ac 100644 --- a/src/vs/workbench/test/electron-browser/api/mainThreadEditors.test.ts +++ b/src/vs/workbench/test/electron-browser/api/mainThreadEditors.test.ts @@ -23,7 +23,7 @@ import { TestFileService, TestEditorService, TestEditorGroupsService, TestEnviro import { ResourceTextEdit } from 'vs/editor/common/modes'; import { BulkEditService } from 'vs/workbench/services/bulkEdit/browser/bulkEditService'; import { NullLogService } from 'vs/platform/log/common/log'; -import { ITextModelService, ITextEditorModel } from 'vs/editor/common/services/resolverService'; +import { ITextModelService, IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService'; import { IReference, ImmortalReference } from 'vs/base/common/lifecycle'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { LabelService } from 'vs/workbench/services/label/common/labelService'; @@ -73,9 +73,9 @@ suite('MainThreadEditors', () => { const workbenchEditorService = new TestEditorService(); const editorGroupService = new TestEditorGroupsService(); const textModelService = new class extends mock() { - createModelReference(resource: URI): Promise> { - const textEditorModel: ITextEditorModel = new class extends mock() { - textEditorModel = modelService.getModel(resource); + createModelReference(resource: URI): Promise> { + const textEditorModel = new class extends mock() { + textEditorModel = modelService.getModel(resource)!; }; textEditorModel.isReadonly = () => false; return Promise.resolve(new ImmortalReference(textEditorModel)); @@ -100,10 +100,10 @@ suite('MainThreadEditors', () => { textFileService, workbenchEditorService, codeEditorService, - null, + null!, fileService, - null, - null, + null!, + null!, editorGroupService, bulkEditService, new class extends mock() implements IPanelService { diff --git a/src/vs/workbench/test/electron-browser/api/mainThreadSaveParticipant.test.ts b/src/vs/workbench/test/electron-browser/api/mainThreadSaveParticipant.test.ts index 2cc91becc3c..eb437ee4f29 100644 --- a/src/vs/workbench/test/electron-browser/api/mainThreadSaveParticipant.test.ts +++ b/src/vs/workbench/test/electron-browser/api/mainThreadSaveParticipant.test.ts @@ -13,7 +13,7 @@ import { IModelService } from 'vs/editor/common/services/modelService'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; -import { ITextFileService, SaveReason } from 'vs/workbench/services/textfile/common/textfiles'; +import { ITextFileService, SaveReason, IResolvedTextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles'; import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager'; import { snapshotToString } from 'vs/platform/files/common/files'; @@ -38,7 +38,7 @@ suite('MainThreadSaveParticipant', function () { }); test('insert final new line', async function () { - const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/final_new_line.txt'), 'utf8'); + const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/final_new_line.txt'), 'utf8') as IResolvedTextFileEditorModel; await model.load(); const configService = new TestConfigurationService(); @@ -71,7 +71,7 @@ suite('MainThreadSaveParticipant', function () { }); test('trim final new lines', async function () { - const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/trim_final_new_line.txt'), 'utf8'); + const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/trim_final_new_line.txt'), 'utf8') as IResolvedTextFileEditorModel; await model.load(); const configService = new TestConfigurationService(); @@ -106,7 +106,7 @@ suite('MainThreadSaveParticipant', function () { }); test('trim final new lines bug#39750', async function () { - const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/trim_final_new_line.txt'), 'utf8'); + const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/trim_final_new_line.txt'), 'utf8') as IResolvedTextFileEditorModel; await model.load(); const configService = new TestConfigurationService(); @@ -133,7 +133,7 @@ suite('MainThreadSaveParticipant', function () { }); test('trim final new lines bug#46075', async function () { - const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/trim_final_new_line.txt'), 'utf8'); + const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/trim_final_new_line.txt'), 'utf8') as IResolvedTextFileEditorModel; await model.load(); const configService = new TestConfigurationService(); diff --git a/src/vs/workbench/test/electron-browser/api/mainThreadWorkspace.test.ts b/src/vs/workbench/test/electron-browser/api/mainThreadWorkspace.test.ts new file mode 100644 index 00000000000..fc2e5cd50b5 --- /dev/null +++ b/src/vs/workbench/test/electron-browser/api/mainThreadWorkspace.test.ts @@ -0,0 +1,101 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { workbenchInstantiationService } from 'vs/workbench/test/workbenchTestServices'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import { ISearchService, IFileQuery } from 'vs/workbench/services/search/common/search'; +import { MainThreadWorkspace } from 'vs/workbench/api/electron-browser/mainThreadWorkspace'; +import * as assert from 'assert'; +import { SingleProxyRPCProtocol } from 'vs/workbench/test/electron-browser/api/testRPCProtocol'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; + +suite('MainThreadWorkspace', () => { + + let configService: TestConfigurationService; + let instantiationService: TestInstantiationService; + + setup(() => { + instantiationService = workbenchInstantiationService() as TestInstantiationService; + + configService = instantiationService.get(IConfigurationService) as TestConfigurationService; + configService.setUserConfiguration('search', {}); + }); + + test('simple', () => { + instantiationService.stub(ISearchService, { + fileSearch(query: IFileQuery) { + assert.equal(query.folderQueries.length, 1); + assert.equal(query.folderQueries[0].disregardIgnoreFiles, true); + + assert.deepEqual(query.includePattern, { 'foo': true }); + assert.equal(query.maxResults, 10); + + return Promise.resolve({ results: [] }); + } + }); + + const mtw: MainThreadWorkspace = instantiationService.createInstance(MainThreadWorkspace, SingleProxyRPCProtocol({ $initializeWorkspace: () => { } })); + return mtw.$startFileSearch('foo', undefined, undefined, 10, new CancellationTokenSource().token); + }); + + test('exclude defaults', () => { + configService.setUserConfiguration('search', { + 'exclude': { 'searchExclude': true } + }); + configService.setUserConfiguration('files', { + 'exclude': { 'filesExclude': true } + }); + + instantiationService.stub(ISearchService, { + fileSearch(query: IFileQuery) { + assert.equal(query.folderQueries.length, 1); + assert.equal(query.folderQueries[0].disregardIgnoreFiles, true); + assert.deepEqual(query.folderQueries[0].excludePattern, { 'filesExclude': true }); + + return Promise.resolve({ results: [] }); + } + }); + + const mtw: MainThreadWorkspace = instantiationService.createInstance(MainThreadWorkspace, SingleProxyRPCProtocol({ $initializeWorkspace: () => { } })); + return mtw.$startFileSearch('', undefined, undefined, 10, new CancellationTokenSource().token); + }); + + test('disregard excludes', () => { + configService.setUserConfiguration('search', { + 'exclude': { 'searchExclude': true } + }); + configService.setUserConfiguration('files', { + 'exclude': { 'filesExclude': true } + }); + + instantiationService.stub(ISearchService, { + fileSearch(query: IFileQuery) { + assert.equal(query.folderQueries[0].excludePattern, undefined); + assert.deepEqual(query.excludePattern, undefined); + + return Promise.resolve({ results: [] }); + } + }); + + const mtw: MainThreadWorkspace = instantiationService.createInstance(MainThreadWorkspace, SingleProxyRPCProtocol({ $initializeWorkspace: () => { } })); + return mtw.$startFileSearch('', undefined, false, 10, new CancellationTokenSource().token); + }); + + test('exclude string', () => { + instantiationService.stub(ISearchService, { + fileSearch(query: IFileQuery) { + assert.equal(query.folderQueries[0].excludePattern, undefined); + assert.deepEqual(query.excludePattern, { 'exclude/**': true }); + + return Promise.resolve({ results: [] }); + } + }); + + const mtw: MainThreadWorkspace = instantiationService.createInstance(MainThreadWorkspace, SingleProxyRPCProtocol({ $initializeWorkspace: () => { } })); + return mtw.$startFileSearch('', undefined, 'exclude/**', 10, new CancellationTokenSource().token); + }); +}); diff --git a/src/vs/workbench/test/electron-browser/colorRegistry.releaseTest.ts b/src/vs/workbench/test/electron-browser/colorRegistry.releaseTest.ts index 32f092e1c11..1da752ea1f9 100644 --- a/src/vs/workbench/test/electron-browser/colorRegistry.releaseTest.ts +++ b/src/vs/workbench/test/electron-browser/colorRegistry.releaseTest.ts @@ -6,12 +6,12 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { IColorRegistry, Extensions, ColorContribution } from 'vs/platform/theme/common/colorRegistry'; import { editorMarkerNavigationError } from 'vs/editor/contrib/gotoError/gotoErrorWidget'; -import { overviewRulerModifiedForeground } from 'vs/workbench/contrib/scm/electron-browser/dirtydiffDecorator'; +import { overviewRulerModifiedForeground } from 'vs/workbench/contrib/scm/browser/dirtydiffDecorator'; import { STATUS_BAR_DEBUGGING_BACKGROUND } from 'vs/workbench/contrib/debug/browser/statusbarColorProvider'; import { debugExceptionWidgetBackground } from 'vs/workbench/contrib/debug/browser/exceptionWidget'; import { debugToolBarBackground } from 'vs/workbench/contrib/debug/browser/debugToolbar'; -import { buttonBackground } from 'vs/workbench/contrib/welcome/page/electron-browser/welcomePage'; -import { embeddedEditorBackground } from 'vs/workbench/contrib/welcome/walkThrough/electron-browser/walkThroughPart'; +import { buttonBackground } from 'vs/workbench/contrib/welcome/page/browser/welcomePage'; +import { embeddedEditorBackground } from 'vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart'; import { request, asText } from 'vs/base/node/request'; import * as pfs from 'vs/base/node/pfs'; import * as path from 'vs/base/common/path'; diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index 2b7d37ffe8c..ccd4fc6df40 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -444,9 +444,10 @@ export class TestPartService implements IPartService { public _serviceBrand: any; + onZenModeChange: Event = Event.None; + private _onTitleBarVisibilityChange = new Emitter(); private _onMenubarVisibilityChange = new Emitter(); - private _onEditorLayout = new Emitter(); public get onTitleBarVisibilityChange(): Event { return this._onTitleBarVisibilityChange.event; @@ -456,10 +457,6 @@ export class TestPartService implements IPartService { return this._onMenubarVisibilityChange.event; } - public get onEditorLayout(): Event { - return this._onEditorLayout.event; - } - public isRestored(): boolean { return true; } @@ -554,6 +551,7 @@ export class TestEditorGroupsService implements EditorGroupsServiceImpl { onDidAddGroup: Event = Event.None; onDidRemoveGroup: Event = Event.None; onDidMoveGroup: Event = Event.None; + onDidLayout: Event = Event.None; orientation: any; whenRestored: Promise = Promise.resolve(undefined); @@ -1334,7 +1332,7 @@ export class TestWindowsService implements IWindowsService { return Promise.resolve(); } - showItemInFolder(_path: string): Promise { + showItemInFolder(_path: URI): Promise { return Promise.resolve(); } diff --git a/src/vs/workbench/workbench.main.ts b/src/vs/workbench/workbench.main.ts index 368fffe6423..e06ec70c0b0 100644 --- a/src/vs/workbench/workbench.main.ts +++ b/src/vs/workbench/workbench.main.ts @@ -38,18 +38,60 @@ import 'vs/workbench/api/browser/viewsExtensionPoint'; //#region --- workbench services +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IMenuService } from 'vs/platform/actions/common/actions'; +import { MenuService } from 'vs/platform/actions/common/menuService'; +import { IListService, ListService } from 'vs/platform/list/browser/listService'; +import { OpenerService } from 'vs/editor/browser/services/openerService'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; +import { EditorWorkerServiceImpl } from 'vs/editor/common/services/editorWorkerServiceImpl'; +import { MarkerDecorationsService } from 'vs/editor/common/services/markerDecorationsServiceImpl'; +import { IMarkerDecorationsService } from 'vs/editor/common/services/markersDecorationService'; +import { IMarkerService } from 'vs/platform/markers/common/markers'; +import { MarkerService } from 'vs/platform/markers/common/markerService'; +import { IDownloadService } from 'vs/platform/download/common/download'; +import { DownloadService } from 'vs/platform/download/node/downloadService'; +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { ClipboardService } from 'vs/platform/clipboard/electron-browser/clipboardService'; import 'vs/workbench/services/bulkEdit/browser/bulkEditService'; import 'vs/workbench/services/integrity/node/integrityService'; import 'vs/workbench/services/keybinding/common/keybindingEditing'; import 'vs/workbench/services/hash/node/hashService'; import 'vs/workbench/services/textMate/electron-browser/textMateService'; +import 'vs/workbench/services/configurationResolver/browser/configurationResolverService'; +import 'vs/workbench/services/workspace/node/workspaceEditingService'; +import 'vs/workbench/services/extensions/electron-browser/inactiveExtensionUrlHandler'; +import 'vs/workbench/services/decorations/browser/decorationsService'; +import 'vs/workbench/services/search/node/searchService'; +import 'vs/workbench/services/progress/browser/progressService2'; +import 'vs/workbench/services/editor/browser/codeEditorService'; +import 'vs/workbench/services/broadcast/electron-browser/broadcastService'; +import 'vs/workbench/services/preferences/browser/preferencesService'; +import 'vs/workbench/services/configuration/node/jsonEditingService'; +import 'vs/workbench/services/textmodelResolver/common/textModelResolverService'; +import 'vs/workbench/services/textfile/common/textFileService'; +import 'vs/workbench/services/dialogs/electron-browser/dialogService'; +import 'vs/workbench/services/backup/node/backupFileService'; + +registerSingleton(IMenuService, MenuService, true); +registerSingleton(IListService, ListService, true); +registerSingleton(IOpenerService, OpenerService, true); +registerSingleton(IEditorWorkerService, EditorWorkerServiceImpl); +registerSingleton(IMarkerDecorationsService, MarkerDecorationsService); +registerSingleton(IMarkerService, MarkerService, true); +registerSingleton(IDownloadService, DownloadService, true); +registerSingleton(IClipboardService, ClipboardService, true); //#endregion //#region --- workbench contributions +// Telemetry +import 'vs/workbench/contrib/telemetry/browser/telemetry.contribution'; + // Localizations import 'vs/workbench/contrib/localizations/browser/localizations.contribution'; @@ -83,8 +125,8 @@ import 'vs/workbench/contrib/search/browser/searchView'; import 'vs/workbench/contrib/search/browser/openAnythingHandler'; // SCM -import 'vs/workbench/contrib/scm/electron-browser/scm.contribution'; -import 'vs/workbench/contrib/scm/electron-browser/scmViewlet'; +import 'vs/workbench/contrib/scm/browser/scm.contribution'; +import 'vs/workbench/contrib/scm/browser/scmViewlet'; // Debug import 'vs/workbench/contrib/debug/electron-browser/debug.contribution'; @@ -98,9 +140,6 @@ import 'vs/workbench/contrib/markers/browser/markers.contribution'; // Comments import 'vs/workbench/contrib/comments/electron-browser/comments.contribution'; -// HTML Preview -import 'vs/workbench/contrib/html/electron-browser/html.contribution'; - // URL Support import 'vs/workbench/contrib/url/common/url.contribution'; @@ -117,9 +156,10 @@ import 'vs/workbench/contrib/output/electron-browser/output.contribution'; import 'vs/workbench/contrib/output/browser/outputPanel'; // Terminal +import 'vs/workbench/contrib/terminal/browser/terminal.contribution'; import 'vs/workbench/contrib/terminal/electron-browser/terminal.contribution'; import 'vs/workbench/contrib/terminal/browser/terminalQuickOpen'; -import 'vs/workbench/contrib/terminal/electron-browser/terminalPanel'; +import 'vs/workbench/contrib/terminal/browser/terminalPanel'; // Relauncher import 'vs/workbench/contrib/relauncher/electron-browser/relauncher.contribution'; @@ -136,7 +176,7 @@ import 'vs/workbench/contrib/codeEditor/browser/codeEditor.contribution'; import 'vs/workbench/contrib/codeEditor/electron-browser/codeEditor.contribution'; // Execution -import 'vs/workbench/contrib/execution/electron-browser/execution.contribution'; +import 'vs/workbench/contrib/externalTerminal/electron-browser/externalTerminal.contribution'; // Snippets import 'vs/workbench/contrib/snippets/browser/snippets.contribution'; @@ -169,13 +209,13 @@ import 'vs/workbench/contrib/themes/browser/themes.contribution'; import 'vs/workbench/contrib/themes/test/electron-browser/themes.test.contribution'; // Watermark -import 'vs/workbench/contrib/watermark/electron-browser/watermark'; +import 'vs/workbench/contrib/watermark/browser/watermark'; // Welcome -import 'vs/workbench/contrib/welcome/walkThrough/electron-browser/walkThrough.contribution'; +import 'vs/workbench/contrib/welcome/walkThrough/browser/walkThrough.contribution'; import 'vs/workbench/contrib/welcome/gettingStarted/electron-browser/gettingStarted.contribution'; import 'vs/workbench/contrib/welcome/overlay/browser/welcomeOverlay'; -import 'vs/workbench/contrib/welcome/page/electron-browser/welcomePage.contribution'; +import 'vs/workbench/contrib/welcome/page/browser/welcomePage.contribution'; // Outline import 'vs/workbench/contrib/outline/browser/outline.contribution'; diff --git a/test/smoke/README.md b/test/smoke/README.md index c61944fee9e..4154212a0f5 100644 --- a/test/smoke/README.md +++ b/test/smoke/README.md @@ -1,5 +1,7 @@ # VS Code Smoke Test +Make sure you are on **Node v10.x**. + ### Run ```bash diff --git a/tslint.json b/tslint.json index 0383001a61b..5365738399a 100644 --- a/tslint.json +++ b/tslint.json @@ -485,6 +485,13 @@ "assert" ] }, + { + "target": "**/vs/workbench/contrib/terminal/browser/**", + "restrictions": [ + "vscode-xterm", + "**/vs/**" + ] + }, { "target": "**/vs/code/node/**", "restrictions": [ diff --git a/yarn.lock b/yarn.lock index ee5b960a7e4..53dc4477514 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3482,10 +3482,10 @@ gauge@~2.7.3: strip-ansi "^3.0.1" wide-align "^1.1.0" -gc-signals@^0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/gc-signals/-/gc-signals-0.0.1.tgz#91e3b7904168b58aa3dc78b619b7b4495b4038ab" - integrity sha1-keO3kEFotYqj3Hi2Gbe0SVtAOKs= +gc-signals@^0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/gc-signals/-/gc-signals-0.0.2.tgz#1cfa8a00adecaeeb93ea0dda72dad9e9f333e62f" + integrity sha512-Ghj4Co6x5bd3dvbAFuiDc6gN+BVK8ic8CBn70dXjzrtbC5hq4a+s4S6acEvftMP7LcQuHKN5v+30PGXhkCLoCQ== generate-function@^2.0.0: version "2.0.0" @@ -3944,10 +3944,10 @@ gulp-symdest@^1.1.1: queue "^3.1.0" vinyl-fs "^2.4.3" -gulp-tsb@2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/gulp-tsb/-/gulp-tsb-2.0.6.tgz#2f8fbee9a81cf0da088744d6da2bca457f2893d1" - integrity sha512-tSIAPfU9rhbtwMgRWopipWFx3NZg943bCLPcP3LxQJ4KUkSdPVc2ZivCibMojw/7HwJUMjLWQ2NCN0yStTQP0g== +gulp-tsb@2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/gulp-tsb/-/gulp-tsb-2.0.7.tgz#0e8c5cc20643d304979a59e90a6448260b314eba" + integrity sha512-9cllqseEkotum/aWHnCTQX/ATD3kyEi1aVzkGp0AEe768uNuU1DqPzRxvyVrfuIPcdYUUvtvmKvEvgIMffNmNA== dependencies: ansi-colors "^1.0.1" fancy-log "^1.3.2"