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/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.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/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/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/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 97a6a41997f..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, 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/typescript-language-features/src/languageProvider.ts b/extensions/typescript-language-features/src/languageProvider.ts index 6f122fcb633..84dbb8e946d 100644 --- a/extensions/typescript-language-features/src/languageProvider.ts +++ b/extensions/typescript-language-features/src/languageProvider.ts @@ -90,7 +90,7 @@ export default class LanguageProvider extends Disposable { } const base = basename(resource.fsPath); - return !!base && base === this.description.configFile; + return !!base && (!!this.description.configFilePattern && this.description.configFilePattern.test(base)); } private get id(): string { 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/languageDescription.ts b/extensions/typescript-language-features/src/utils/languageDescription.ts index 32cec300ebd..b9f279dc509 100644 --- a/extensions/typescript-language-features/src/utils/languageDescription.ts +++ b/extensions/typescript-language-features/src/utils/languageDescription.ts @@ -17,7 +17,7 @@ export interface LanguageDescription { readonly diagnosticSource: string; readonly diagnosticLanguage: DiagnosticLanguage; readonly modeIds: string[]; - readonly configFile?: string; + readonly configFilePattern?: RegExp; readonly isExternal?: boolean; } @@ -28,13 +28,13 @@ export const standardLanguageDescriptions: LanguageDescription[] = [ diagnosticSource: 'ts', diagnosticLanguage: DiagnosticLanguage.TypeScript, modeIds: [languageModeIds.typescript, languageModeIds.typescriptreact], - configFile: 'tsconfig.json' + configFilePattern: /^tsconfig(\..*)?\.json$/gi }, { id: 'javascript', diagnosticOwner: 'typescript', diagnosticSource: 'ts', diagnosticLanguage: DiagnosticLanguage.JavaScript, modeIds: [languageModeIds.javascript, languageModeIds.javascriptreact], - configFile: 'jsconfig.json' + configFilePattern: /^jsconfig(\..*)?\.json$/gi } ]; 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/package.json b/package.json index 606c1365c3b..8eddb0f8231 100644 --- a/package.json +++ b/package.json @@ -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 cc45a82aa66..a674809e8b0 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -15,13 +15,26 @@ "./vs/workbench/electron-browser/actions/**/*", "./vs/workbench/contrib/emmet/**/*", "./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/output/**/*.ts", + "./vs/workbench/services/progress/**/*.ts", + "./vs/workbench/services/preferences/**/*.ts", + "./vs/workbench/services/timer/**/*.ts", + "./vs/workbench/contrib/webview/**/*.ts", + "./vs/workbench/contrib/debug/common/**/*.ts", + "./vs/workbench/contrib/preferences/common/**/*.ts", + "./vs/workbench/contrib/preferences/**/settings*.ts", ], "files": [ "./vs/monaco.d.ts", @@ -29,6 +42,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", @@ -46,6 +60,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", @@ -53,6 +68,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", @@ -62,50 +78,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", @@ -115,6 +168,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", @@ -131,12 +187,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", @@ -144,6 +203,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", @@ -185,6 +246,7 @@ "./vs/workbench/contrib/debug/common/debugViewModel.ts", "./vs/workbench/contrib/debug/electron-browser/rawDebugSession.ts", "./vs/workbench/contrib/debug/node/debugAdapter.ts", + "./vs/workbench/contrib/debug/node/debugger.ts", "./vs/workbench/contrib/debug/node/telemetryApp.ts", "./vs/workbench/contrib/debug/test/common/debugSource.test.ts", "./vs/workbench/contrib/debug/test/common/debugUtils.test.ts", @@ -198,12 +260,19 @@ "./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/common/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", @@ -211,8 +280,12 @@ "./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", @@ -225,22 +298,32 @@ "./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/node/outputAppender.ts", - "./vs/workbench/contrib/performance/electron-browser/perfviewEditor.ts", - "./vs/workbench/contrib/performance/electron-browser/startupProfiler.ts", - "./vs/workbench/contrib/performance/electron-browser/startupTimings.ts", + "./vs/workbench/contrib/output/browser/output.contribution.ts", + "./vs/workbench/contrib/output/browser/outputServices.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", @@ -249,12 +332,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", @@ -271,53 +354,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/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/terminalPanel.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", @@ -338,14 +429,16 @@ "./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/extensionHostProtocol.ts", "./vs/workbench/services/extensions/node/lazyPromise.ts", "./vs/workbench/services/extensions/node/proxyIdentifier.ts", "./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", @@ -373,8 +466,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", @@ -409,8 +500,10 @@ "./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", @@ -427,7 +520,8 @@ "./vs/workbench/test/electron-browser/api/mainThreadDiagnostics.test.ts", "./vs/workbench/test/electron-browser/api/mainThreadDocumentContentProviders.test.ts", "./vs/workbench/test/electron-browser/api/mock.ts", - "./vs/workbench/test/electron-browser/api/testRPCProtocol.ts" + "./vs/workbench/test/electron-browser/api/testRPCProtocol.ts", + "./vs/workbench/contrib/debug/node/terminals.ts" ], "exclude": [ "./typings/require-monaco.d.ts", 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 80dea11ee4a..42097113808 100644 --- a/src/vs/base/browser/ui/list/list.ts +++ b/src/vs/base/browser/ui/list/list.ts @@ -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/listView.ts b/src/vs/base/browser/ui/list/listView.ts index ac85245b2fe..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; } diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index cd7312809c6..197fcb2ab98 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -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/menubar.ts b/src/vs/base/browser/ui/menu/menubar.ts index 4506ec9f488..a4283053ab0 100644 --- a/src/vs/base/browser/ui/menu/menubar.ts +++ b/src/vs/base/browser/ui/menu/menubar.ts @@ -22,7 +22,7 @@ const $ = DOM.$; export interface IMenuBarOptions { enableMnemonics?: boolean; visibility?: string; - getKeybinding?: (action: IAction) => ResolvedKeybinding; + getKeybinding?: (action: IAction) => ResolvedKeybinding | undefined; alwaysOnMnemonics?: boolean; } @@ -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/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index 20e0bdf0bd4..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; @@ -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 }; @@ -318,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 = { @@ -426,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); } @@ -477,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); } @@ -651,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; @@ -704,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; @@ -748,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 { @@ -758,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) { @@ -769,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 ab554f756bf..984a1c8afd1 100644 --- a/src/vs/base/common/extpath.ts +++ b/src/vs/base/common/extpath.ts @@ -32,7 +32,7 @@ export function getRoot(path: string, sep: string = posix.sep): string { 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 = posix.sep): 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 844cdd246a9..640c033ae01 100644 --- a/src/vs/base/common/path.ts +++ b/src/vs/base/common/path.ts @@ -231,7 +231,7 @@ export const win32: IPath = { continue; } - let len = path.length; + const len = path.length; let rootEnd = 0; let device = ''; let isAbsolute = false; @@ -503,7 +503,7 @@ export 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) { @@ -582,8 +582,8 @@ export 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 ''; @@ -610,7 +610,7 @@ export const win32: IPath = { break; } } - let fromLen = (fromEnd - fromStart); + const fromLen = (fromEnd - fromStart); // Trim any leading backslashes let toStart = 0; @@ -626,10 +626,10 @@ export 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) { @@ -658,8 +658,8 @@ export 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; } @@ -1015,12 +1015,12 @@ export 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); @@ -1268,7 +1268,7 @@ export 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) { @@ -1307,8 +1307,8 @@ export 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; @@ -1317,11 +1317,11 @@ export 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) { @@ -1349,8 +1349,8 @@ export 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; } @@ -1568,11 +1568,11 @@ export 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 = '/'; 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 index 386ad29df56..a8447d58eea 100644 --- a/src/vs/base/common/process.ts +++ b/src/vs/base/common/process.ts @@ -13,7 +13,7 @@ interface IProcess { nextTick(callback: (...args: any[]) => void): number; } -declare let process: IProcess; +declare const process: IProcess; const safeProcess: IProcess = (typeof process === 'undefined') ? { cwd(): string { return '/'; }, env: Object.create(null), 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 54f46ad9468..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 } } 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 8fbecabfd80..79560531ae8 100644 --- a/src/vs/base/common/uri.ts +++ b/src/vs/base/common/uri.ts @@ -329,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; @@ -379,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; @@ -488,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 ( @@ -518,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 @@ -547,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); @@ -637,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/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 dfaa9042499..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,33 +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, ...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; - } - } - }); -} - export abstract class AbstractProcess { private cmd: string; private args: string[]; @@ -140,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); @@ -151,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]!; }); @@ -164,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); } @@ -181,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; }); @@ -193,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. @@ -205,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)) { @@ -218,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; @@ -234,7 +207,7 @@ export abstract class AbstractProcess { quotedArg = quotedArg && quoted.quoted; }); } - let args: string[] = [ + const args: string[] = [ '/s', '/c', ]; @@ -314,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; } @@ -329,7 +302,7 @@ export abstract class AbstractProcess { if (!this.shell || !Platform.isWindows) { return c(false); } - let cmdShell = cp.spawn(getWindowsShell(), ['/s', '/c']); + const cmdShell = cp.spawn(getWindowsShell(), ['/s', '/c']); cmdShell.on('error', (error: Error) => { return c(true); }); @@ -353,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 }); } @@ -370,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 })); }); } @@ -407,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 } @@ -439,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. 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/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/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/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/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/code/electron-browser/issue/issueReporterMain.ts b/src/vs/code/electron-browser/issue/issueReporterMain.ts index ca5ed69d0af..c4d6e66aaa8 100644 --- a/src/vs/code/electron-browser/issue/issueReporterMain.ts +++ b/src/vs/code/electron-browser/issue/issueReporterMain.ts @@ -64,14 +64,14 @@ export class IssueReporter extends Disposable { private environmentService: IEnvironmentService; private telemetryService: ITelemetryService; private logService: ILogService; - private issueReporterModel: IssueReporterModel; + private readonly issueReporterModel: IssueReporterModel; private numberOfSearchResultsDisplayed = 0; private receivedSystemInfo = false; private receivedPerformanceInfo = false; private shouldQueueSearch = false; private hasBeenSubmitted = false; - private previewButton: Button; + private readonly previewButton: Button; constructor(configuration: IssueReporterConfiguration) { super(); diff --git a/src/vs/code/electron-browser/issue/issueReporterModel.ts b/src/vs/code/electron-browser/issue/issueReporterModel.ts index b918ceff961..8ad189c44b4 100644 --- a/src/vs/code/electron-browser/issue/issueReporterModel.ts +++ b/src/vs/code/electron-browser/issue/issueReporterModel.ts @@ -34,7 +34,7 @@ export interface IssueReporterData { } export class IssueReporterModel { - private _data: IssueReporterData; + private readonly _data: IssueReporterData; constructor(initialData?: Partial) { const defaultData = { @@ -196,7 +196,7 @@ ${this._data.workspaceInfo}; return 'Extensions: none' + themeExclusionStr; } - let tableHeader = `Extension|Author (truncated)|Version + const tableHeader = `Extension|Author (truncated)|Version ---|---|---`; const table = this._data.enabledNonThemeExtesions.map(e => { return `${e.name}|${e.publisher.substr(0, 3)}|${e.version}`; @@ -226,7 +226,7 @@ Literal matches: ${this._data.filterResultCount}`; return `No fuzzy results`; } - let tableHeader = `Setting|Extension|Score + const tableHeader = `Setting|Extension|Score ---|---|---`; const table = this._data.actualSearchResults.map(setting => { return `${setting.key}|${setting.extensionId}|${String(setting.score).slice(0, 5)}`; diff --git a/src/vs/code/electron-browser/sharedProcess/contrib/languagePackCachedDataCleaner.ts b/src/vs/code/electron-browser/sharedProcess/contrib/languagePackCachedDataCleaner.ts index 19c73491115..e7f1eec7742 100644 --- a/src/vs/code/electron-browser/sharedProcess/contrib/languagePackCachedDataCleaner.ts +++ b/src/vs/code/electron-browser/sharedProcess/contrib/languagePackCachedDataCleaner.ts @@ -57,15 +57,15 @@ export class LanguagePackCachedDataCleaner { ? 1000 * 60 * 60 * 24 * 7 // roughly 1 week : 1000 * 60 * 60 * 24 * 30 * 3; // roughly 3 months try { - let installed: IStringDictionary = Object.create(null); + const installed: IStringDictionary = Object.create(null); const metaData: LanguagePackFile = JSON.parse(await pfs.readFile(path.join(this._environmentService.userDataPath, 'languagepacks.json'), 'utf8')); for (let locale of Object.keys(metaData)) { - let entry = metaData[locale]; + const entry = metaData[locale]; installed[`${entry.hash}.${locale}`] = true; } // Cleanup entries for language packs that aren't installed anymore const cacheDir = path.join(this._environmentService.userDataPath, 'clp'); - let exists = await pfs.exists(cacheDir); + const exists = await pfs.exists(cacheDir); if (!exists) { return; } diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index a97534eb331..d0e144be9fe 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -91,8 +91,8 @@ export class CodeApplication extends Disposable { private sharedProcessClient: Promise; constructor( - private mainIpcServer: Server, - private userEnv: IProcessEnvironment, + private readonly mainIpcServer: Server, + private readonly userEnv: IProcessEnvironment, @IInstantiationService private readonly instantiationService: IInstantiationService, @ILogService private readonly logService: ILogService, @IEnvironmentService private readonly environmentService: IEnvironmentService, @@ -648,9 +648,9 @@ export class CodeApplication extends Disposable { const isBuilt = this.environmentService.isBuilt; class ActiveConnection { - private _authority: string; - private _client: Promise>; - private _disposeRunner: RunOnceScheduler; + private readonly _authority: string; + private readonly _client: Promise>; + private readonly _disposeRunner: RunOnceScheduler; constructor(authority: string, host: string, port: number) { this._authority = authority; @@ -707,7 +707,7 @@ export class CodeApplication extends Disposable { if (connectionPool.has(uri.authority)) { activeConnection = connectionPool.get(uri.authority); } else { - let resolvedAuthority = resolveAuthority(uri.authority); + const resolvedAuthority = resolveAuthority(uri.authority); if (!resolvedAuthority) { callback(undefined); return; diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index f89af11d2aa..d3d8f3fa86d 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -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) ); } } diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index b63e759f3a9..234ed41eeb1 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -69,14 +69,14 @@ export class CodeWindow extends Disposable implements ICodeWindow { private currentMenuBarVisibility: MenuBarVisibility; private representedFilename: string; - private whenReadyCallbacks: { (window: ICodeWindow): void }[]; + private readonly whenReadyCallbacks: { (window: ICodeWindow): void }[]; private currentConfig: IWindowConfiguration; private pendingLoadConfig?: IWindowConfiguration; private marketplaceHeadersPromise: Promise; - private touchBarGroups: Electron.TouchBarSegmentedControl[]; + private readonly touchBarGroups: Electron.TouchBarSegmentedControl[]; constructor( config: IWindowCreationOptions, diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 8f012a9315c..1bd75d43c0c 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -15,7 +15,7 @@ import { CodeWindow, defaultWindowState } from 'vs/code/electron-main/window'; import { hasArgs, asArray } from 'vs/platform/environment/node/argv'; import { ipcMain as ipc, screen, BrowserWindow, dialog, systemPreferences } from 'electron'; import { parseLineAndColumnAware } from 'vs/code/node/paths'; -import { ILifecycleService, UnloadReason, IWindowUnloadEvent, LifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain'; +import { ILifecycleService, UnloadReason, LifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ILogService } from 'vs/platform/log/common/log'; import { IWindowSettings, OpenContext, IPath, IWindowConfiguration, INativeOpenDialogOptions, IPathsToWaitFor, IEnterWorkspaceResult, IMessageBoxResult, INewWindowOptions, IURIToOpen, URIType, OpenDialogOptions } from 'vs/platform/windows/common/windows'; @@ -25,14 +25,14 @@ import product from 'vs/platform/product/node/product'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IWindowsMainService, IOpenConfiguration, IWindowsCountChangedEvent, ICodeWindow, IWindowState as ISingleWindowState, WindowMode } from 'vs/platform/windows/electron-main/windows'; import { IHistoryMainService } from 'vs/platform/history/common/history'; -import { IProcessEnvironment, isLinux, isMacintosh, isWindows } from 'vs/base/common/platform'; +import { IProcessEnvironment, isMacintosh, isWindows } from 'vs/base/common/platform'; import { IWorkspacesMainService, IWorkspaceIdentifier, WORKSPACE_FILTER, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { mnemonicButtonLabel } from 'vs/base/common/labels'; import { Schemas } from 'vs/base/common/network'; import { normalizeNFC } from 'vs/base/common/normalization'; import { URI } from 'vs/base/common/uri'; -import { Queue, timeout } from 'vs/base/common/async'; +import { Queue } from 'vs/base/common/async'; import { exists } from 'vs/base/node/pfs'; import { getComparisonKey, isEqual, normalizePath, basename as resourcesBasename, originalFSPath, hasTrailingPathSeparator, removeTrailingPathSeparator } from 'vs/base/common/resources'; import { getRemoteAuthority } from 'vs/platform/remote/common/remoteHosts'; @@ -158,11 +158,11 @@ export class WindowsManager implements IWindowsMainService { private initialUserEnv: IProcessEnvironment; - private windowsState: IWindowsState; + private readonly windowsState: IWindowsState; private lastClosedWindowState?: IWindowState; - private dialogs: Dialogs; - private workspacesManager: WorkspacesManager; + private readonly dialogs: Dialogs; + private readonly workspacesManager: WorkspacesManager; private _onWindowReady = new Emitter(); onWindowReady: CommonEvent = this._onWindowReady.event; @@ -197,7 +197,7 @@ export class WindowsManager implements IWindowsMainService { } this.dialogs = new Dialogs(environmentService, telemetryService, stateService, this); - this.workspacesManager = new WorkspacesManager(workspacesMainService, backupMainService, environmentService, historyMainService, this); + this.workspacesManager = new WorkspacesManager(workspacesMainService, backupMainService, this); } ready(initialUserEnv: IProcessEnvironment): void { @@ -233,7 +233,6 @@ export class WindowsManager implements IWindowsMainService { } // Handle various lifecycle events around windows - this.lifecycleService.onBeforeWindowUnload(e => this.onBeforeWindowUnload(e)); this.lifecycleService.onBeforeWindowClose(window => this.onBeforeWindowClose(window)); this.lifecycleService.onBeforeShutdown(() => this.onBeforeShutdown()); this.onWindowsCountChanged(e => { @@ -368,7 +367,7 @@ export class WindowsManager implements IWindowsMainService { this.logService.trace('windowsManager#open'); openConfig = this.validateOpenConfig(openConfig); - let pathsToOpen = this.getPathsToOpen(openConfig); + const pathsToOpen = this.getPathsToOpen(openConfig); const foldersToAdd: IFolderPathToOpen[] = []; const foldersToOpen: IFolderPathToOpen[] = []; @@ -440,7 +439,7 @@ export class WindowsManager implements IWindowsMainService { // Make sure to pass focus to the most relevant of the windows if we open multiple if (usedWindows.length > 1) { - let focusLastActive = this.windowsState.lastActiveWindow && !openConfig.forceEmpty && !hasArgs(openConfig.cli._) && !hasArgs(openConfig.cli['file-uri']) && !hasArgs(openConfig.cli['folder-uri']) && !(openConfig.urisToOpen && openConfig.urisToOpen.length); + const focusLastActive = this.windowsState.lastActiveWindow && !openConfig.forceEmpty && !hasArgs(openConfig.cli._) && !hasArgs(openConfig.cli['file-uri']) && !hasArgs(openConfig.cli['folder-uri']) && !(openConfig.urisToOpen && openConfig.urisToOpen.length); let focusLastOpened = true; let focusLastWindow = true; @@ -551,7 +550,7 @@ export class WindowsManager implements IWindowsMainService { // only look at the windows with correct authority const windows = WindowsManager.WINDOWS.filter(w => w.remoteAuthority === fileInputs!.remoteAuthority); - let bestWindowOrFolder = findBestWindowOrFolderForFile({ + const bestWindowOrFolder = findBestWindowOrFolderForFile({ windows, newWindow: openFilesInNewWindow, context: openConfig.context, @@ -820,7 +819,7 @@ export class WindowsManager implements IWindowsMainService { if (!openConfig.addMode && isCommandLineOrAPICall) { const foldersToOpen = windowsToOpen.filter(path => !!path.folderUri); if (foldersToOpen.length > 1) { - let remoteAuthority = foldersToOpen[0].remoteAuthority; + const remoteAuthority = foldersToOpen[0].remoteAuthority; if (foldersToOpen.every(f => f.remoteAuthority === remoteAuthority)) { // only if all folder have the same authority const workspace = this.workspacesMainService.createUntitledWorkspaceSync(foldersToOpen.map(folder => ({ uri: folder.folderUri! }))); @@ -837,7 +836,7 @@ export class WindowsManager implements IWindowsMainService { private doExtractPathsFromAPI(openConfig: IOpenConfiguration): IPathToOpen[] { const pathsToOpen: IPathToOpen[] = []; const cli = openConfig.cli; - let parseOptions: IPathParseOptions = { gotoLineMode: cli && cli.goto, forceOpenWorkspaceAsFile: openConfig.forceOpenWorkspaceAsFile }; + const parseOptions: IPathParseOptions = { gotoLineMode: cli && cli.goto, forceOpenWorkspaceAsFile: openConfig.forceOpenWorkspaceAsFile }; for (const pathToOpen of openConfig.urisToOpen || []) { if (!pathToOpen) { continue; @@ -980,7 +979,7 @@ export class WindowsManager implements IWindowsMainService { private argToUri(arg: string): URI | undefined { try { - let uri = URI.parse(arg); + const uri = URI.parse(arg); if (!uri.scheme) { this.logService.error(`Invalid URI input string, scheme missing: ${arg}`); return undefined; @@ -1526,43 +1525,6 @@ export class WindowsManager implements IWindowsMainService { this.workspacesManager.pickWorkspaceAndOpen(options); } - private onBeforeWindowUnload(e: IWindowUnloadEvent): void { - const windowClosing = (e.reason === UnloadReason.CLOSE); - const windowLoading = (e.reason === UnloadReason.LOAD); - if (!windowClosing && !windowLoading) { - return; // only interested when window is closing or loading - } - - const workspace = e.window.openedWorkspace; - if (!workspace || !this.workspacesMainService.isUntitledWorkspace(workspace)) { - return; // only care about untitled workspaces to ask for saving - } - - if (e.window.config && !!e.window.config.extensionDevelopmentPath) { - // do not ask to save workspace when doing extension development - // but still delete it. - this.workspacesMainService.deleteUntitledWorkspaceSync(workspace); - return; - } - - if (windowClosing && !isMacintosh && this.getWindowCount() === 1) { - return; // Windows/Linux: quits when last window is closed, so do not ask then - } - - // Handle untitled workspaces with prompt as needed - e.veto(this.workspacesManager.promptToSaveUntitledWorkspace(this.getWindowById(e.window.id), workspace).then((veto): boolean | Promise => { - if (veto) { - return veto; - } - - // Bug in electron: somehow we need this timeout so that the window closes properly. That - // might be related to the fact that the untitled workspace prompt shows up async and this - // code can execute before the dialog is fully closed which then blocks the window from closing. - // Issue: https://github.com/Microsoft/vscode/issues/41989 - return timeout(0).then(() => veto); - })); - } - focusLastActive(cli: ParsedArgs, context: OpenContext): ICodeWindow { const lastActive = this.getLastActiveWindow(); if (lastActive) { @@ -1585,7 +1547,7 @@ export class WindowsManager implements IWindowsMainService { openNewWindow(context: OpenContext, options?: INewWindowOptions): ICodeWindow[] { let cli = this.environmentService.args; - let remote = options && options.remoteAuthority || undefined; + const remote = options && options.remoteAuthority || undefined; if (cli && (cli.remote !== remote)) { cli = { ...cli, remote }; } @@ -1822,14 +1784,14 @@ class Dialogs { private static readonly workingDirPickerStorageKey = 'pickerWorkingDir'; - private mapWindowToDialogQueue: Map>; - private noWindowDialogQueue: Queue; + private readonly mapWindowToDialogQueue: Map>; + private readonly noWindowDialogQueue: Queue; constructor( - private environmentService: IEnvironmentService, - private telemetryService: ITelemetryService, - private stateService: IStateService, - private windowsMainService: IWindowsMainService, + private readonly environmentService: IEnvironmentService, + private readonly telemetryService: ITelemetryService, + private readonly stateService: IStateService, + private readonly windowsMainService: IWindowsMainService, ) { this.mapWindowToDialogQueue = new Map>(); this.noWindowDialogQueue = new Queue(); @@ -1994,8 +1956,6 @@ class WorkspacesManager { constructor( private readonly workspacesMainService: IWorkspacesMainService, private readonly backupMainService: IBackupMainService, - private readonly environmentService: IEnvironmentService, - private readonly historyMainService: IHistoryMainService, private readonly windowsMainService: IWindowsMainService, ) { } @@ -2079,92 +2039,4 @@ class WorkspacesManager { telemetryExtraData: options.telemetryExtraData }); } - - promptToSaveUntitledWorkspace(window: ICodeWindow | undefined, workspace: IWorkspaceIdentifier): Promise { - enum ConfirmResult { - SAVE, - DONT_SAVE, - CANCEL - } - - const save = { label: mnemonicButtonLabel(localize({ key: 'save', comment: ['&& denotes a mnemonic'] }, "&&Save")), result: ConfirmResult.SAVE }; - const dontSave = { label: mnemonicButtonLabel(localize({ key: 'doNotSave', comment: ['&& denotes a mnemonic'] }, "Do&&n't Save")), result: ConfirmResult.DONT_SAVE }; - const cancel = { label: localize('cancel', "Cancel"), result: ConfirmResult.CANCEL }; - - const buttons: { label: string; result: ConfirmResult; }[] = []; - if (isWindows) { - buttons.push(save, dontSave, cancel); - } else if (isLinux) { - buttons.push(dontSave, cancel, save); - } else { - buttons.push(save, cancel, dontSave); - } - - const options: Electron.MessageBoxOptions = { - title: this.environmentService.appNameLong, - message: localize('saveWorkspaceMessage', "Do you want to save your workspace configuration as a file?"), - detail: localize('saveWorkspaceDetail', "Save your workspace if you plan to open it again."), - noLink: true, - type: 'warning', - buttons: buttons.map(button => button.label), - cancelId: buttons.indexOf(cancel) - }; - - if (isLinux) { - options.defaultId = 2; - } - - return this.windowsMainService.showMessageBox(options, window).then(res => { - switch (buttons[res.button].result) { - - // Cancel: veto unload - case ConfirmResult.CANCEL: - return true; - - // Don't Save: delete workspace - case ConfirmResult.DONT_SAVE: - this.workspacesMainService.deleteUntitledWorkspaceSync(workspace); - return false; - - // Save: save workspace, but do not veto unload - case ConfirmResult.SAVE: { - return this.windowsMainService.showSaveDialog({ - buttonLabel: mnemonicButtonLabel(localize({ key: 'save', comment: ['&& denotes a mnemonic'] }, "&&Save")), - title: localize('saveWorkspace', "Save Workspace"), - filters: WORKSPACE_FILTER, - defaultPath: this.getUntitledWorkspaceSaveDialogDefaultPath(workspace) - }, window).then(target => { - if (target) { - return this.workspacesMainService.saveWorkspaceAs(workspace, target).then(savedWorkspace => { - this.historyMainService.addRecentlyOpened([savedWorkspace], []); - this.workspacesMainService.deleteUntitledWorkspaceSync(workspace); - return false; - }, () => false); - } - - return true; // keep veto if no target was provided - }); - } - } - }); - } - - private getUntitledWorkspaceSaveDialogDefaultPath(workspace?: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier): string | undefined { - if (workspace) { - if (isSingleFolderWorkspaceIdentifier(workspace)) { - return workspace.scheme === Schemas.file ? dirname(workspace.fsPath) : undefined; - } - - const resolvedWorkspace = workspace.configPath.scheme === Schemas.file && this.workspacesMainService.resolveLocalWorkspaceSync(workspace.configPath); - if (resolvedWorkspace && resolvedWorkspace.folders.length > 0) { - for (const folder of resolvedWorkspace.folders) { - if (folder.uri.scheme === Schemas.file) { - return dirname(folder.uri.fsPath); - } - } - } - } - - return undefined; - } } diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index 3f07a1e6365..566448022a8 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -104,7 +104,7 @@ export class Main { } private async installExtensions(extensions: string[], force: boolean): Promise { - let failed: string[] = []; + const failed: string[] = []; for (const extension of extensions) { try { await this.installExtension(extension, force); diff --git a/src/vs/editor/browser/config/charWidthReader.ts b/src/vs/editor/browser/config/charWidthReader.ts index d24f7b5cf9b..a578ce29186 100644 --- a/src/vs/editor/browser/config/charWidthReader.ts +++ b/src/vs/editor/browser/config/charWidthReader.ts @@ -62,12 +62,12 @@ class DomCharWidthReader { } private _createDomElements(): void { - let container = document.createElement('div'); + const container = document.createElement('div'); container.style.position = 'absolute'; container.style.top = '-50000px'; container.style.width = '50000px'; - let regularDomNode = document.createElement('div'); + const regularDomNode = document.createElement('div'); regularDomNode.style.fontFamily = this._bareFontInfo.getMassagedFontFamily(); regularDomNode.style.fontWeight = this._bareFontInfo.fontWeight; regularDomNode.style.fontSize = this._bareFontInfo.fontSize + 'px'; @@ -75,7 +75,7 @@ class DomCharWidthReader { regularDomNode.style.letterSpacing = this._bareFontInfo.letterSpacing + 'px'; container.appendChild(regularDomNode); - let boldDomNode = document.createElement('div'); + const boldDomNode = document.createElement('div'); boldDomNode.style.fontFamily = this._bareFontInfo.getMassagedFontFamily(); boldDomNode.style.fontWeight = 'bold'; boldDomNode.style.fontSize = this._bareFontInfo.fontSize + 'px'; @@ -83,7 +83,7 @@ class DomCharWidthReader { boldDomNode.style.letterSpacing = this._bareFontInfo.letterSpacing + 'px'; container.appendChild(boldDomNode); - let italicDomNode = document.createElement('div'); + const italicDomNode = document.createElement('div'); italicDomNode.style.fontFamily = this._bareFontInfo.getMassagedFontFamily(); italicDomNode.style.fontWeight = this._bareFontInfo.fontWeight; italicDomNode.style.fontSize = this._bareFontInfo.fontSize + 'px'; @@ -92,7 +92,7 @@ class DomCharWidthReader { italicDomNode.style.fontStyle = 'italic'; container.appendChild(italicDomNode); - let testElements: HTMLSpanElement[] = []; + const testElements: HTMLSpanElement[] = []; for (let i = 0, len = this._requests.length; i < len; i++) { const request = this._requests[i]; @@ -109,7 +109,7 @@ class DomCharWidthReader { parent!.appendChild(document.createElement('br')); - let testElement = document.createElement('span'); + const testElement = document.createElement('span'); DomCharWidthReader._render(testElement, request); parent!.appendChild(testElement); @@ -149,6 +149,6 @@ class DomCharWidthReader { } export function readCharWidths(bareFontInfo: BareFontInfo, requests: CharWidthRequest[]): void { - let reader = new DomCharWidthReader(bareFontInfo, requests); + const reader = new DomCharWidthReader(bareFontInfo, requests); reader.read(); } diff --git a/src/vs/editor/browser/config/configuration.ts b/src/vs/editor/browser/config/configuration.ts index 32e87b2a794..c7fa9fb94c6 100644 --- a/src/vs/editor/browser/config/configuration.ts +++ b/src/vs/editor/browser/config/configuration.ts @@ -19,8 +19,8 @@ import { IAccessibilityService } from 'vs/platform/accessibility/common/accessib class CSSBasedConfigurationCache { - private _keys: { [key: string]: BareFontInfo; }; - private _values: { [key: string]: FontInfo; }; + private readonly _keys: { [key: string]: BareFontInfo; }; + private readonly _values: { [key: string]: FontInfo; }; constructor() { this._keys = Object.create(null); @@ -28,23 +28,23 @@ class CSSBasedConfigurationCache { } public has(item: BareFontInfo): boolean { - let itemId = item.getId(); + const itemId = item.getId(); return !!this._values[itemId]; } public get(item: BareFontInfo): FontInfo { - let itemId = item.getId(); + const itemId = item.getId(); return this._values[itemId]; } public put(item: BareFontInfo, value: FontInfo): void { - let itemId = item.getId(); + const itemId = item.getId(); this._keys[itemId] = item; this._values[itemId] = value; } public remove(item: BareFontInfo): void { - let itemId = item.getId(); + const itemId = item.getId(); delete this._keys[itemId]; delete this._values[itemId]; } @@ -63,7 +63,7 @@ export function readFontInfo(bareFontInfo: BareFontInfo): FontInfo { } export function restoreFontInfo(storageService: IStorageService): void { - let strStoredFontInfo = storageService.get('editorFontInfo', StorageScope.GLOBAL); + const strStoredFontInfo = storageService.get('editorFontInfo', StorageScope.GLOBAL); if (typeof strStoredFontInfo !== 'string') { return; } @@ -80,7 +80,7 @@ export function restoreFontInfo(storageService: IStorageService): void { } export function saveFontInfo(storageService: IStorageService): void { - let knownFontInfo = CSSBasedConfiguration.INSTANCE.saveFontInfo(); + const knownFontInfo = CSSBasedConfiguration.INSTANCE.saveFontInfo(); if (knownFontInfo.length > 0) { storageService.store('editorFontInfo', JSON.stringify(knownFontInfo), StorageScope.GLOBAL); } @@ -144,10 +144,10 @@ class CSSBasedConfiguration extends Disposable { } private _evictUntrustedReadings(): void { - let values = this._cache.getValues(); + const values = this._cache.getValues(); let somethingRemoved = false; for (let i = 0, len = values.length; i < len; i++) { - let item = values[i]; + const item = values[i]; if (!item.isTrusted) { somethingRemoved = true; this._cache.remove(item); @@ -167,7 +167,7 @@ class CSSBasedConfiguration extends Disposable { // Take all the saved font info and insert them in the cache without the trusted flag. // The reason for this is that a font might have been installed on the OS in the meantime. for (let i = 0, len = savedFontInfo.length; i < len; i++) { - let fontInfo = new FontInfo(savedFontInfo[i], false); + const fontInfo = new FontInfo(savedFontInfo[i], false); this._writeToCache(fontInfo, fontInfo); } } @@ -200,7 +200,7 @@ class CSSBasedConfiguration extends Disposable { } private static createRequest(chr: string, type: CharWidthRequestType, all: CharWidthRequest[], monospace: CharWidthRequest[] | null): CharWidthRequest { - let result = new CharWidthRequest(chr, type); + const result = new CharWidthRequest(chr, type); all.push(result); if (monospace) { monospace.push(result); @@ -209,8 +209,8 @@ class CSSBasedConfiguration extends Disposable { } private static _actualReadConfiguration(bareFontInfo: BareFontInfo): FontInfo { - let all: CharWidthRequest[] = []; - let monospace: CharWidthRequest[] = []; + const all: CharWidthRequest[] = []; + const monospace: CharWidthRequest[] = []; const typicalHalfwidthCharacter = this.createRequest('n', CharWidthRequestType.Regular, all, monospace); const typicalFullwidthCharacter = this.createRequest('\uff4d', CharWidthRequestType.Regular, all, null); @@ -262,7 +262,7 @@ class CSSBasedConfiguration extends Disposable { const maxDigitWidth = Math.max(digit0.width, digit1.width, digit2.width, digit3.width, digit4.width, digit5.width, digit6.width, digit7.width, digit8.width, digit9.width); let isMonospace = true; - let referenceWidth = monospace[0].width; + const referenceWidth = monospace[0].width; for (let i = 1, len = monospace.length; i < len; i++) { const diff = referenceWidth - monospace[i].width; if (diff < -0.001 || diff > 0.001) { diff --git a/src/vs/editor/browser/config/elementSizeObserver.ts b/src/vs/editor/browser/config/elementSizeObserver.ts index 77a74232b60..325741e7293 100644 --- a/src/vs/editor/browser/config/elementSizeObserver.ts +++ b/src/vs/editor/browser/config/elementSizeObserver.ts @@ -8,9 +8,9 @@ import { IDimension } from 'vs/editor/common/editorCommon'; export class ElementSizeObserver extends Disposable { - private referenceDomElement: HTMLElement | null; + private readonly referenceDomElement: HTMLElement | null; private measureReferenceDomElementToken: any; - private changeCallback: () => void; + private readonly changeCallback: () => void; private width: number; private height: number; diff --git a/src/vs/editor/browser/controller/coreCommands.ts b/src/vs/editor/browser/controller/coreCommands.ts index b6629ad43a7..f1abe319f58 100644 --- a/src/vs/editor/browser/controller/coreCommands.ts +++ b/src/vs/editor/browser/controller/coreCommands.ts @@ -48,7 +48,7 @@ export namespace EditorScroll_ { return false; } - let scrollArg: RawArguments = arg; + const scrollArg: RawArguments = arg; if (!types.isString(scrollArg.to)) { return false; @@ -213,7 +213,7 @@ export namespace RevealLine_ { return false; } - let reveaLineArg: RawArguments = arg; + const reveaLineArg: RawArguments = arg; if (!types.isNumber(reveaLineArg.lineNumber)) { return false; @@ -800,7 +800,7 @@ export namespace CoreNavigationCommands { const lastAddedCursorIndex = cursors.getLastAddedCursorIndex(); const states = cursors.getAll(); - let newStates: PartialCursorState[] = states.slice(0); + const newStates: PartialCursorState[] = states.slice(0); newStates[lastAddedCursorIndex] = CursorMoveCommands.moveTo(context, states[lastAddedCursorIndex], true, args.position, args.viewPosition); cursors.context.model.pushStackElement(); @@ -881,7 +881,7 @@ export namespace CoreNavigationCommands { } private _exec(context: CursorContext, cursors: CursorState[]): PartialCursorState[] { - let result: PartialCursorState[] = []; + const result: PartialCursorState[] = []; for (let i = 0, len = cursors.length; i < len; i++) { const cursor = cursors[i]; const lineNumber = cursor.modelState.position.lineNumber; @@ -960,7 +960,7 @@ export namespace CoreNavigationCommands { } private _exec(context: CursorContext, cursors: CursorState[]): PartialCursorState[] { - let result: PartialCursorState[] = []; + const result: PartialCursorState[] = []; for (let i = 0, len = cursors.length; i < len; i++) { const cursor = cursors[i]; const lineNumber = cursor.modelState.position.lineNumber; @@ -1280,8 +1280,8 @@ export namespace CoreNavigationCommands { const lastAddedCursorIndex = cursors.getLastAddedCursorIndex(); const states = cursors.getAll(); - let newStates: PartialCursorState[] = states.slice(0); - let lastAddedState = states[lastAddedCursorIndex]; + const newStates: PartialCursorState[] = states.slice(0); + const lastAddedState = states[lastAddedCursorIndex]; newStates[lastAddedCursorIndex] = CursorMoveCommands.word(context, lastAddedState, lastAddedState.modelState.hasSelection(), args.position); context.model.pushStackElement(); @@ -1338,7 +1338,7 @@ export namespace CoreNavigationCommands { const lastAddedCursorIndex = cursors.getLastAddedCursorIndex(); const states = cursors.getAll(); - let newStates: PartialCursorState[] = states.slice(0); + const newStates: PartialCursorState[] = states.slice(0); newStates[lastAddedCursorIndex] = CursorMoveCommands.line(cursors.context, states[lastAddedCursorIndex], this._inSelectionMode, args.position, args.viewPosition); cursors.context.model.pushStackElement(); @@ -1685,21 +1685,21 @@ class EditorOrNativeTextInputCommand extends Command { public runCommand(accessor: ServicesAccessor, args: any): void { - let focusedEditor = accessor.get(ICodeEditorService).getFocusedCodeEditor(); + const focusedEditor = accessor.get(ICodeEditorService).getFocusedCodeEditor(); // Only if editor text focus (i.e. not if editor has widget focus). if (focusedEditor && focusedEditor.hasTextFocus()) { return this._runEditorHandler(accessor, focusedEditor, args); } // Ignore this action when user is focused on an element that allows for entering text - let activeElement = document.activeElement; + const activeElement = document.activeElement; if (activeElement && ['input', 'textarea'].indexOf(activeElement.tagName.toLowerCase()) >= 0) { document.execCommand(this._inputHandler); return; } // Redirecting to active editor - let activeEditor = accessor.get(ICodeEditorService).getActiveCodeEditor(); + const activeEditor = accessor.get(ICodeEditorService).getActiveCodeEditor(); if (activeEditor) { activeEditor.focus(); return this._runEditorHandler(accessor, activeEditor, args); @@ -1707,7 +1707,7 @@ class EditorOrNativeTextInputCommand extends Command { } private _runEditorHandler(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void { - let HANDLER = this._editorHandler; + const HANDLER = this._editorHandler; if (typeof HANDLER === 'string') { editor.trigger('keyboard', HANDLER, args); } else { diff --git a/src/vs/editor/browser/controller/mouseHandler.ts b/src/vs/editor/browser/controller/mouseHandler.ts index 1a8af35373f..b3b447201c6 100644 --- a/src/vs/editor/browser/controller/mouseHandler.ts +++ b/src/vs/editor/browser/controller/mouseHandler.ts @@ -69,9 +69,9 @@ export class MouseHandler extends ViewEventHandler { protected viewController: ViewController; protected viewHelper: IPointerHandlerHelper; protected mouseTargetFactory: MouseTargetFactory; - private _asyncFocus: RunOnceScheduler; + private readonly _asyncFocus: RunOnceScheduler; - private _mouseDownOperation: MouseDownOperation; + private readonly _mouseDownOperation: MouseDownOperation; private lastMouseLeaveTime: number; constructor(context: ViewContext, viewController: ViewController, viewHelper: IPointerHandlerHelper) { @@ -94,7 +94,7 @@ export class MouseHandler extends ViewEventHandler { this.lastMouseLeaveTime = -1; - let mouseEvents = new EditorMouseEventFactory(this.viewHelper.viewDomNode); + const mouseEvents = new EditorMouseEventFactory(this.viewHelper.viewDomNode); this._register(mouseEvents.onContextMenu(this.viewHelper.viewDomNode, (e) => this._onContextMenu(e, true))); @@ -108,14 +108,14 @@ export class MouseHandler extends ViewEventHandler { this._register(mouseEvents.onMouseDown(this.viewHelper.viewDomNode, (e) => this._onMouseDown(e))); - let onMouseWheel = (browserEvent: IMouseWheelEvent) => { + const onMouseWheel = (browserEvent: IMouseWheelEvent) => { if (!this._context.configuration.editor.viewInfo.mouseWheelZoom) { return; } - let e = new StandardWheelEvent(browserEvent); + const e = new StandardWheelEvent(browserEvent); if (e.browserEvent!.ctrlKey || e.browserEvent!.metaKey) { - let zoomLevel: number = EditorZoom.getZoomLevel(); - let delta = e.deltaY > 0 ? 1 : -1; + const zoomLevel: number = EditorZoom.getZoomLevel(); + const delta = e.deltaY > 0 ? 1 : -1; EditorZoom.setZoomLevel(zoomLevel + delta); e.preventDefault(); e.stopPropagation(); @@ -148,20 +148,20 @@ export class MouseHandler extends ViewEventHandler { // --- end event handlers public getTargetAtClientPoint(clientX: number, clientY: number): editorBrowser.IMouseTarget | null { - let clientPos = new ClientCoordinates(clientX, clientY); - let pos = clientPos.toPageCoordinates(); - let editorPos = createEditorPagePosition(this.viewHelper.viewDomNode); + const clientPos = new ClientCoordinates(clientX, clientY); + const pos = clientPos.toPageCoordinates(); + const editorPos = createEditorPagePosition(this.viewHelper.viewDomNode); if (pos.y < editorPos.y || pos.y > editorPos.y + editorPos.height || pos.x < editorPos.x || pos.x > editorPos.x + editorPos.width) { return null; } - let lastViewCursorsRenderData = this.viewHelper.getLastViewCursorsRenderData(); + const lastViewCursorsRenderData = this.viewHelper.getLastViewCursorsRenderData(); return this.mouseTargetFactory.createMouseTarget(lastViewCursorsRenderData, editorPos, pos, null); } protected _createMouseTarget(e: EditorMouseEvent, testEventTarget: boolean): editorBrowser.IMouseTarget { - let lastViewCursorsRenderData = this.viewHelper.getLastViewCursorsRenderData(); + const lastViewCursorsRenderData = this.viewHelper.getLastViewCursorsRenderData(); return this.mouseTargetFactory.createMouseTarget(lastViewCursorsRenderData, e.editorPos, e.pos, testEventTarget ? e.target : null); } @@ -181,7 +181,7 @@ export class MouseHandler extends ViewEventHandler { // In selection/drag operation return; } - let actualMouseMoveTime = e.timestamp; + const actualMouseMoveTime = e.timestamp; if (actualMouseMoveTime < this.lastMouseLeaveTime) { // Due to throttling, this event occurred before the mouse left the editor, therefore ignore it. return; @@ -209,21 +209,21 @@ export class MouseHandler extends ViewEventHandler { } public _onMouseDown(e: EditorMouseEvent): void { - let t = this._createMouseTarget(e, true); + const t = this._createMouseTarget(e, true); - let targetIsContent = (t.type === editorBrowser.MouseTargetType.CONTENT_TEXT || t.type === editorBrowser.MouseTargetType.CONTENT_EMPTY); - let targetIsGutter = (t.type === editorBrowser.MouseTargetType.GUTTER_GLYPH_MARGIN || t.type === editorBrowser.MouseTargetType.GUTTER_LINE_NUMBERS || t.type === editorBrowser.MouseTargetType.GUTTER_LINE_DECORATIONS); - let targetIsLineNumbers = (t.type === editorBrowser.MouseTargetType.GUTTER_LINE_NUMBERS); - let selectOnLineNumbers = this._context.configuration.editor.viewInfo.selectOnLineNumbers; - let targetIsViewZone = (t.type === editorBrowser.MouseTargetType.CONTENT_VIEW_ZONE || t.type === editorBrowser.MouseTargetType.GUTTER_VIEW_ZONE); - let targetIsWidget = (t.type === editorBrowser.MouseTargetType.CONTENT_WIDGET); + const targetIsContent = (t.type === editorBrowser.MouseTargetType.CONTENT_TEXT || t.type === editorBrowser.MouseTargetType.CONTENT_EMPTY); + const targetIsGutter = (t.type === editorBrowser.MouseTargetType.GUTTER_GLYPH_MARGIN || t.type === editorBrowser.MouseTargetType.GUTTER_LINE_NUMBERS || t.type === editorBrowser.MouseTargetType.GUTTER_LINE_DECORATIONS); + const targetIsLineNumbers = (t.type === editorBrowser.MouseTargetType.GUTTER_LINE_NUMBERS); + const selectOnLineNumbers = this._context.configuration.editor.viewInfo.selectOnLineNumbers; + const targetIsViewZone = (t.type === editorBrowser.MouseTargetType.CONTENT_VIEW_ZONE || t.type === editorBrowser.MouseTargetType.GUTTER_VIEW_ZONE); + const targetIsWidget = (t.type === editorBrowser.MouseTargetType.CONTENT_WIDGET); let shouldHandle = e.leftButton || e.middleButton; if (platform.isMacintosh && e.leftButton && e.ctrlKey) { shouldHandle = false; } - let focus = () => { + const focus = () => { // In IE11, if the focus is in the browser's address bar and // then you click in the editor, calling preventDefault() // will not move focus properly (focus remains the address bar) @@ -243,7 +243,7 @@ export class MouseHandler extends ViewEventHandler { // Do not steal focus e.preventDefault(); } else if (targetIsViewZone) { - let viewZoneData = t.detail; + const viewZoneData = t.detail; if (this.viewHelper.shouldSuppressMouseDownOnViewZone(viewZoneData.viewZoneId)) { focus(); this._mouseDownOperation.start(t.type, e); @@ -312,7 +312,7 @@ class MouseDownOperation extends Disposable { this._lastMouseEvent = e; this._mouseState.setModifiers(e); - let position = this._findMousePosition(e, true); + const position = this._findMousePosition(e, true); if (!position) { // Ignoring because position is unknown return; @@ -334,7 +334,7 @@ class MouseDownOperation extends Disposable { this._mouseState.setStartedOnLineNumbers(targetType === editorBrowser.MouseTargetType.GUTTER_LINE_NUMBERS); this._mouseState.setStartButtons(e); this._mouseState.setModifiers(e); - let position = this._findMousePosition(e, true); + const position = this._findMousePosition(e, true); if (!position || !position.position) { // Ignoring because position is unknown return; @@ -361,7 +361,7 @@ class MouseDownOperation extends Disposable { createMouseMoveEventMerger(null), (e) => this._onMouseDownThenMove(e), () => { - let position = this._findMousePosition(this._lastMouseEvent!, true); + const position = this._findMousePosition(this._lastMouseEvent!, true); this._viewController.emitMouseDrop({ event: this._lastMouseEvent!, @@ -401,7 +401,7 @@ class MouseDownOperation extends Disposable { if (!this._lastMouseEvent) { return; } - let position = this._findMousePosition(this._lastMouseEvent, false); + const position = this._findMousePosition(this._lastMouseEvent, false); if (!position) { // Ignoring because position is unknown return; @@ -435,7 +435,7 @@ class MouseDownOperation extends Disposable { } } - let aboveLineNumber = viewLayout.getLineNumberAtVerticalOffset(verticalOffset); + const aboveLineNumber = viewLayout.getLineNumberAtVerticalOffset(verticalOffset); return new MouseTarget(null, editorBrowser.MouseTargetType.OUTSIDE_EDITOR, mouseColumn, new Position(aboveLineNumber, 1)); } @@ -449,11 +449,11 @@ class MouseDownOperation extends Disposable { } } - let belowLineNumber = viewLayout.getLineNumberAtVerticalOffset(verticalOffset); + const belowLineNumber = viewLayout.getLineNumberAtVerticalOffset(verticalOffset); return new MouseTarget(null, editorBrowser.MouseTargetType.OUTSIDE_EDITOR, mouseColumn, new Position(belowLineNumber, model.getLineMaxColumn(belowLineNumber))); } - let possibleLineNumber = viewLayout.getLineNumberAtVerticalOffset(viewLayout.getCurrentScrollTop() + (e.posy - editorContent.y)); + const possibleLineNumber = viewLayout.getLineNumberAtVerticalOffset(viewLayout.getCurrentScrollTop() + (e.posy - editorContent.y)); if (e.posx < editorContent.x) { return new MouseTarget(null, editorBrowser.MouseTargetType.OUTSIDE_EDITOR, mouseColumn, new Position(possibleLineNumber, 1)); @@ -467,13 +467,13 @@ class MouseDownOperation extends Disposable { } private _findMousePosition(e: EditorMouseEvent, testEventTarget: boolean): MouseTarget | null { - let positionOutsideEditor = this._getPositionOutsideEditor(e); + const positionOutsideEditor = this._getPositionOutsideEditor(e); if (positionOutsideEditor) { return positionOutsideEditor; } - let t = this._createMouseTarget(e, testEventTarget); - let hintedPosition = t.position; + const t = this._createMouseTarget(e, testEventTarget); + const hintedPosition = t.position; if (!hintedPosition) { return null; } @@ -490,9 +490,9 @@ class MouseDownOperation extends Disposable { private _helpPositionJumpOverViewZone(viewZoneData: IViewZoneData): Position | null { // Force position on view zones to go above or below depending on where selection started from - let selectionStart = new Position(this._currentSelection.selectionStartLineNumber, this._currentSelection.selectionStartColumn); - let positionBefore = viewZoneData.positionBefore; - let positionAfter = viewZoneData.positionAfter; + const selectionStart = new Position(this._currentSelection.selectionStartLineNumber, this._currentSelection.selectionStartColumn); + const positionBefore = viewZoneData.positionBefore; + const positionAfter = viewZoneData.positionAfter; if (positionBefore && positionAfter) { if (positionBefore.isBefore(selectionStart)) { @@ -594,7 +594,7 @@ class MouseDownState { public trySetCount(setMouseDownCount: number, newMouseDownPosition: Position): void { // a. Invalidate multiple clicking if too much time has passed (will be hit by IE because the detail field of mouse events contains garbage in IE10) - let currentTime = (new Date()).getTime(); + const currentTime = (new Date()).getTime(); if (currentTime - this._lastSetMouseDownCountTime > MouseDownState.CLEAR_MOUSE_DOWN_COUNT_TIME) { setMouseDownCount = 1; } diff --git a/src/vs/editor/browser/controller/mouseTarget.ts b/src/vs/editor/browser/controller/mouseTarget.ts index 54127e9beb6..8d69b3dffbf 100644 --- a/src/vs/editor/browser/controller/mouseTarget.ts +++ b/src/vs/editor/browser/controller/mouseTarget.ts @@ -253,7 +253,7 @@ export class HitTestContext { public static getZoneAtCoord(context: ViewContext, mouseVerticalOffset: number): IViewZoneData | null { // The target is either a view zone or the empty space after the last view-line - let viewZoneWhitespace = context.viewLayout.getWhitespaceAtVerticalOffset(mouseVerticalOffset); + const viewZoneWhitespace = context.viewLayout.getWhitespaceAtVerticalOffset(mouseVerticalOffset); if (viewZoneWhitespace) { let viewZoneMiddle = viewZoneWhitespace.verticalOffset + viewZoneWhitespace.height / 2, @@ -295,16 +295,16 @@ export class HitTestContext { public getFullLineRangeAtCoord(mouseVerticalOffset: number): { range: EditorRange; isAfterLines: boolean; } { if (this._context.viewLayout.isAfterLines(mouseVerticalOffset)) { // Below the last line - let lineNumber = this._context.model.getLineCount(); - let maxLineColumn = this._context.model.getLineMaxColumn(lineNumber); + const lineNumber = this._context.model.getLineCount(); + const maxLineColumn = this._context.model.getLineMaxColumn(lineNumber); return { range: new EditorRange(lineNumber, maxLineColumn, lineNumber, maxLineColumn), isAfterLines: true }; } - let lineNumber = this._context.viewLayout.getLineNumberAtVerticalOffset(mouseVerticalOffset); - let maxLineColumn = this._context.model.getLineMaxColumn(lineNumber); + const lineNumber = this._context.viewLayout.getLineNumberAtVerticalOffset(mouseVerticalOffset); + const maxLineColumn = this._context.model.getLineMaxColumn(lineNumber); return { range: new EditorRange(lineNumber, 1, lineNumber, maxLineColumn), isAfterLines: false @@ -430,8 +430,8 @@ function createEmptyContentDataInLines(horizontalDistanceToText: number): IEmpty export class MouseTargetFactory { - private _context: ViewContext; - private _viewHelper: IPointerHandlerHelper; + private readonly _context: ViewContext; + private readonly _viewHelper: IPointerHandlerHelper; constructor(context: ViewContext, viewHelper: IPointerHandlerHelper) { this._context = context; @@ -439,8 +439,8 @@ export class MouseTargetFactory { } public mouseTargetIsWidget(e: EditorMouseEvent): boolean { - let t = e.target; - let path = PartFingerprints.collect(t, this._viewHelper.viewDomNode); + const t = e.target; + const path = PartFingerprints.collect(t, this._viewHelper.viewDomNode); // Is it a content widget? if (ElementPath.isChildOfContentWidgets(path) || ElementPath.isChildOfOverflowingContentWidgets(path)) { @@ -459,7 +459,7 @@ export class MouseTargetFactory { const ctx = new HitTestContext(this._context, this._viewHelper, lastViewCursorsRenderData); const request = new HitTestRequest(ctx, editorPos, pos, target); try { - let r = MouseTargetFactory._createMouseTarget(ctx, request, false); + const r = MouseTargetFactory._createMouseTarget(ctx, request, false); // console.log(r.toString()); return r; } catch (err) { @@ -510,7 +510,7 @@ export class MouseTargetFactory { private static _hitTestContentWidget(ctx: HitTestContext, request: ResolvedHitTestRequest): MouseTarget | null { // Is it a content widget? if (ElementPath.isChildOfContentWidgets(request.targetPath) || ElementPath.isChildOfOverflowingContentWidgets(request.targetPath)) { - let widgetId = ctx.findAttribute(request.target, 'widgetId'); + const widgetId = ctx.findAttribute(request.target, 'widgetId'); if (widgetId) { return request.fulfill(MouseTargetType.CONTENT_WIDGET, null, null, widgetId); } else { @@ -523,7 +523,7 @@ export class MouseTargetFactory { private static _hitTestOverlayWidget(ctx: HitTestContext, request: ResolvedHitTestRequest): MouseTarget | null { // Is it an overlay widget? if (ElementPath.isChildOfOverlayWidgets(request.targetPath)) { - let widgetId = ctx.findAttribute(request.target, 'widgetId'); + const widgetId = ctx.findAttribute(request.target, 'widgetId'); if (widgetId) { return request.fulfill(MouseTargetType.OVERLAY_WIDGET, null, null, widgetId); } else { @@ -583,9 +583,9 @@ export class MouseTargetFactory { } private static _hitTestViewZone(ctx: HitTestContext, request: ResolvedHitTestRequest): MouseTarget | null { - let viewZoneData = ctx.getZoneAtCoord(request.mouseVerticalOffset); + const viewZoneData = ctx.getZoneAtCoord(request.mouseVerticalOffset); if (viewZoneData) { - let mouseTargetType = (request.isInContentArea ? MouseTargetType.CONTENT_VIEW_ZONE : MouseTargetType.GUTTER_VIEW_ZONE); + const mouseTargetType = (request.isInContentArea ? MouseTargetType.CONTENT_VIEW_ZONE : MouseTargetType.GUTTER_VIEW_ZONE); return request.fulfill(mouseTargetType, viewZoneData.position, null, viewZoneData); } @@ -602,8 +602,8 @@ export class MouseTargetFactory { private static _hitTestMargin(ctx: HitTestContext, request: ResolvedHitTestRequest): MouseTarget | null { if (request.isInMarginArea) { - let res = ctx.getFullLineRangeAtCoord(request.mouseVerticalOffset); - let pos = res.range.getStartPosition(); + const res = ctx.getFullLineRangeAtCoord(request.mouseVerticalOffset); + const pos = res.range.getStartPosition(); let offset = Math.abs(request.pos.x - request.editorPos.x); const detail: IMarginData = { isAfterLines: res.isAfterLines, @@ -683,7 +683,7 @@ export class MouseTargetFactory { private static _hitTestScrollbarSlider(ctx: HitTestContext, request: ResolvedHitTestRequest): MouseTarget | null { if (ElementPath.isChildOfScrollableElement(request.targetPath)) { if (request.target && request.target.nodeType === 1) { - let className = request.target.className; + const className = request.target.className; if (className && /\b(slider|scrollbar)\b/.test(className)) { const possibleLineNumber = ctx.getLineNumberAtVerticalOffset(request.mouseVerticalOffset); const maxColumn = ctx.model.getLineMaxColumn(possibleLineNumber); @@ -707,8 +707,8 @@ export class MouseTargetFactory { } public getMouseColumn(editorPos: EditorPagePosition, pos: PageCoordinates): number { - let layoutInfo = this._context.configuration.editor.layoutInfo; - let mouseContentHorizontalOffset = this._context.viewLayout.getCurrentScrollLeft() + pos.x - editorPos.x - layoutInfo.contentLeft; + const layoutInfo = this._context.configuration.editor.layoutInfo; + const mouseContentHorizontalOffset = this._context.viewLayout.getCurrentScrollLeft() + pos.x - editorPos.x - layoutInfo.contentLeft; return MouseTargetFactory._getMouseColumn(mouseContentHorizontalOffset, this._context.configuration.editor.fontInfo.typicalHalfwidthCharacterWidth); } @@ -716,14 +716,14 @@ export class MouseTargetFactory { if (mouseContentHorizontalOffset < 0) { return 1; } - let chars = Math.round(mouseContentHorizontalOffset / typicalHalfwidthCharacterWidth); + const chars = Math.round(mouseContentHorizontalOffset / typicalHalfwidthCharacterWidth); return (chars + 1); } private static createMouseTargetFromHitTestPosition(ctx: HitTestContext, request: HitTestRequest, lineNumber: number, column: number): MouseTarget { - let pos = new Position(lineNumber, column); + const pos = new Position(lineNumber, column); - let lineWidth = ctx.getLineWidth(lineNumber); + const lineWidth = ctx.getLineWidth(lineNumber); if (request.mouseContentHorizontalOffset > lineWidth) { if (browser.isEdge && pos.column === 1) { @@ -741,7 +741,7 @@ export class MouseTargetFactory { return request.fulfill(MouseTargetType.UNKNOWN, pos); } - let columnHorizontalOffset = visibleRange.left; + const columnHorizontalOffset = visibleRange.left; if (request.mouseContentHorizontalOffset === columnHorizontalOffset) { return request.fulfill(MouseTargetType.CONTENT_TEXT, pos); @@ -750,7 +750,7 @@ export class MouseTargetFactory { // Let's define a, b, c and check if the offset is in between them... interface OffsetColumn { offset: number; column: number; } - let points: OffsetColumn[] = []; + const points: OffsetColumn[] = []; points.push({ offset: visibleRange.left, column: column }); if (column > 1) { const visibleRange = ctx.visibleRangeForPosition2(lineNumber, column - 1); @@ -786,9 +786,9 @@ export class MouseTargetFactory { // In Chrome, especially on Linux it is possible to click between lines, // so try to adjust the `hity` below so that it lands in the center of a line - let lineNumber = ctx.getLineNumberAtVerticalOffset(request.mouseVerticalOffset); - let lineVerticalOffset = ctx.getVerticalOffsetForLineNumber(lineNumber); - let lineCenteredVerticalOffset = lineVerticalOffset + Math.floor(ctx.lineHeight / 2); + const lineNumber = ctx.getLineNumberAtVerticalOffset(request.mouseVerticalOffset); + const lineVerticalOffset = ctx.getVerticalOffsetForLineNumber(lineNumber); + const lineCenteredVerticalOffset = lineVerticalOffset + Math.floor(ctx.lineHeight / 2); let adjustedPageY = request.pos.y + (lineCenteredVerticalOffset - request.mouseVerticalOffset); if (adjustedPageY <= request.editorPos.y) { @@ -798,9 +798,9 @@ export class MouseTargetFactory { adjustedPageY = request.editorPos.y + ctx.layoutInfo.height - 1; } - let adjustedPage = new PageCoordinates(request.pos.x, adjustedPageY); + const adjustedPage = new PageCoordinates(request.pos.x, adjustedPageY); - let r = this._actualDoHitTestWithCaretRangeFromPoint(ctx, adjustedPage.toClientCoordinates()); + const r = this._actualDoHitTestWithCaretRangeFromPoint(ctx, adjustedPage.toClientCoordinates()); if (r.position) { return r; } @@ -811,7 +811,7 @@ export class MouseTargetFactory { private static _actualDoHitTestWithCaretRangeFromPoint(ctx: HitTestContext, coords: ClientCoordinates): IHitTestResult { - let range: Range = document.caretRangeFromPoint(coords.clientX, coords.clientY); + const range: Range = document.caretRangeFromPoint(coords.clientX, coords.clientY); if (!range || !range.startContainer) { return { @@ -821,18 +821,18 @@ export class MouseTargetFactory { } // Chrome always hits a TEXT_NODE, while Edge sometimes hits a token span - let startContainer = range.startContainer; + const startContainer = range.startContainer; let hitTarget: HTMLElement | null = null; if (startContainer.nodeType === startContainer.TEXT_NODE) { // startContainer is expected to be the token text - let parent1 = startContainer.parentNode; // expected to be the token span - let parent2 = parent1 ? parent1.parentNode : null; // expected to be the view line container span - let parent3 = parent2 ? parent2.parentNode : null; // expected to be the view line div - let parent3ClassName = parent3 && parent3.nodeType === parent3.ELEMENT_NODE ? (parent3).className : null; + const parent1 = startContainer.parentNode; // expected to be the token span + const parent2 = parent1 ? parent1.parentNode : null; // expected to be the view line container span + const parent3 = parent2 ? parent2.parentNode : null; // expected to be the view line div + const parent3ClassName = parent3 && parent3.nodeType === parent3.ELEMENT_NODE ? (parent3).className : null; if (parent3ClassName === ViewLine.CLASS_NAME) { - let p = ctx.getPositionFromDOMInfo(parent1, range.startOffset); + const p = ctx.getPositionFromDOMInfo(parent1, range.startOffset); return { position: p, hitTarget: null @@ -842,12 +842,12 @@ export class MouseTargetFactory { } } else if (startContainer.nodeType === startContainer.ELEMENT_NODE) { // startContainer is expected to be the token span - let parent1 = startContainer.parentNode; // expected to be the view line container span - let parent2 = parent1 ? parent1.parentNode : null; // expected to be the view line div - let parent2ClassName = parent2 && parent2.nodeType === parent2.ELEMENT_NODE ? (parent2).className : null; + const parent1 = startContainer.parentNode; // expected to be the view line container span + const parent2 = parent1 ? parent1.parentNode : null; // expected to be the view line div + const parent2ClassName = parent2 && parent2.nodeType === parent2.ELEMENT_NODE ? (parent2).className : null; if (parent2ClassName === ViewLine.CLASS_NAME) { - let p = ctx.getPositionFromDOMInfo(startContainer, (startContainer).textContent!.length); + const p = ctx.getPositionFromDOMInfo(startContainer, (startContainer).textContent!.length); return { position: p, hitTarget: null @@ -867,17 +867,17 @@ export class MouseTargetFactory { * Most probably Gecko */ private static _doHitTestWithCaretPositionFromPoint(ctx: HitTestContext, coords: ClientCoordinates): IHitTestResult { - let hitResult: { offsetNode: Node; offset: number; } = (document).caretPositionFromPoint(coords.clientX, coords.clientY); + const hitResult: { offsetNode: Node; offset: number; } = (document).caretPositionFromPoint(coords.clientX, coords.clientY); if (hitResult.offsetNode.nodeType === hitResult.offsetNode.TEXT_NODE) { // offsetNode is expected to be the token text - let parent1 = hitResult.offsetNode.parentNode; // expected to be the token span - let parent2 = parent1 ? parent1.parentNode : null; // expected to be the view line container span - let parent3 = parent2 ? parent2.parentNode : null; // expected to be the view line div - let parent3ClassName = parent3 && parent3.nodeType === parent3.ELEMENT_NODE ? (parent3).className : null; + const parent1 = hitResult.offsetNode.parentNode; // expected to be the token span + const parent2 = parent1 ? parent1.parentNode : null; // expected to be the view line container span + const parent3 = parent2 ? parent2.parentNode : null; // expected to be the view line div + const parent3ClassName = parent3 && parent3.nodeType === parent3.ELEMENT_NODE ? (parent3).className : null; if (parent3ClassName === ViewLine.CLASS_NAME) { - let p = ctx.getPositionFromDOMInfo(hitResult.offsetNode.parentNode, hitResult.offset); + const p = ctx.getPositionFromDOMInfo(hitResult.offsetNode.parentNode, hitResult.offset); return { position: p, hitTarget: null @@ -903,7 +903,7 @@ export class MouseTargetFactory { let resultPosition: Position | null = null; let resultHitTarget: Element | null = null; - let textRange: IETextRange = (document.body).createTextRange(); + const textRange: IETextRange = (document.body).createTextRange(); try { textRange.moveToPoint(coords.clientX, coords.clientY); } catch (err) { @@ -916,14 +916,14 @@ export class MouseTargetFactory { textRange.collapse(true); // Now, let's do our best to figure out what we hit :) - let parentElement = textRange ? textRange.parentElement() : null; - let parent1 = parentElement ? parentElement.parentNode : null; - let parent2 = parent1 ? parent1.parentNode : null; + const parentElement = textRange ? textRange.parentElement() : null; + const parent1 = parentElement ? parentElement.parentNode : null; + const parent2 = parent1 ? parent1.parentNode : null; - let parent2ClassName = parent2 && parent2.nodeType === parent2.ELEMENT_NODE ? (parent2).className : ''; + const parent2ClassName = parent2 && parent2.nodeType === parent2.ELEMENT_NODE ? (parent2).className : ''; if (parent2ClassName === ViewLine.CLASS_NAME) { - let rangeToContainEntireSpan = textRange.duplicate(); + const rangeToContainEntireSpan = textRange.duplicate(); rangeToContainEntireSpan.moveToElementText(parentElement!); rangeToContainEntireSpan.setEndPoint('EndToStart', textRange); diff --git a/src/vs/editor/browser/controller/pointerHandler.ts b/src/vs/editor/browser/controller/pointerHandler.ts index c97ebab90ad..f744847b48f 100644 --- a/src/vs/editor/browser/controller/pointerHandler.ts +++ b/src/vs/editor/browser/controller/pointerHandler.ts @@ -18,7 +18,7 @@ interface IThrottledGestureEvent { } function gestureChangeEventMerger(lastEvent: IThrottledGestureEvent, currentEvent: MSGestureEvent): IThrottledGestureEvent { - let r = { + const r = { translationY: currentEvent.translationY, translationX: currentEvent.translationX }; @@ -48,13 +48,13 @@ class MsPointerHandler extends MouseHandler implements IDisposable { this._installGestureHandlerTimeout = window.setTimeout(() => { this._installGestureHandlerTimeout = -1; if ((window).MSGesture) { - let touchGesture = new MSGesture(); - let penGesture = new MSGesture(); + const touchGesture = new MSGesture(); + const penGesture = new MSGesture(); touchGesture.target = this.viewHelper.linesContentDomNode; penGesture.target = this.viewHelper.linesContentDomNode; this.viewHelper.linesContentDomNode.addEventListener('MSPointerDown', (e: MSPointerEvent) => { // Circumvent IE11 breaking change in e.pointerType & TypeScript's stale definitions - let pointerType = e.pointerType; + const pointerType = e.pointerType; if (pointerType === ((e).MSPOINTER_TYPE_MOUSE || 'mouse')) { this._lastPointerType = 'mouse'; return; @@ -80,8 +80,8 @@ class MsPointerHandler extends MouseHandler implements IDisposable { } private _onCaptureGestureTap(rawEvent: MSGestureEvent): void { - let e = new EditorMouseEvent(rawEvent, this.viewHelper.viewDomNode); - let t = this._createMouseTarget(e, false); + const e = new EditorMouseEvent(rawEvent, this.viewHelper.viewDomNode); + const t = this._createMouseTarget(e, false); if (t.position) { this.viewController.moveTo(t.position); } @@ -127,12 +127,12 @@ class StandardPointerHandler extends MouseHandler implements IDisposable { // TODO@Alex: replace the usage of MSGesture here with something that works across all browsers if ((window).MSGesture) { - let touchGesture = new MSGesture(); - let penGesture = new MSGesture(); + const touchGesture = new MSGesture(); + const penGesture = new MSGesture(); touchGesture.target = this.viewHelper.linesContentDomNode; penGesture.target = this.viewHelper.linesContentDomNode; this.viewHelper.linesContentDomNode.addEventListener('pointerdown', (e: MSPointerEvent) => { - let pointerType = e.pointerType; + const pointerType = e.pointerType; if (pointerType === 'mouse') { this._lastPointerType = 'mouse'; return; @@ -158,8 +158,8 @@ class StandardPointerHandler extends MouseHandler implements IDisposable { } private _onCaptureGestureTap(rawEvent: MSGestureEvent): void { - let e = new EditorMouseEvent(rawEvent, this.viewHelper.viewDomNode); - let t = this._createMouseTarget(e, false); + const e = new EditorMouseEvent(rawEvent, this.viewHelper.viewDomNode); + const t = this._createMouseTarget(e, false); if (t.position) { this.viewController.moveTo(t.position); } @@ -207,7 +207,7 @@ class TouchHandler extends MouseHandler { this.viewHelper.focusTextArea(); - let target = this._createMouseTarget(new EditorMouseEvent(event, this.viewHelper.viewDomNode), false); + const target = this._createMouseTarget(new EditorMouseEvent(event, this.viewHelper.viewDomNode), false); if (target.position) { this.viewController.moveTo(target.position); @@ -220,7 +220,7 @@ class TouchHandler extends MouseHandler { } export class PointerHandler implements IDisposable { - private handler: MouseHandler; + private readonly handler: MouseHandler; constructor(context: ViewContext, viewController: ViewController, viewHelper: IPointerHandlerHelper) { if (window.navigator.msPointerEnabled) { diff --git a/src/vs/editor/browser/controller/textAreaState.ts b/src/vs/editor/browser/controller/textAreaState.ts index 697ce880544..b907a3bfa6a 100644 --- a/src/vs/editor/browser/controller/textAreaState.ts +++ b/src/vs/editor/browser/controller/textAreaState.ts @@ -233,26 +233,26 @@ export class PagedScreenReaderStrategy { } private static _getRangeForPage(page: number): Range { - let offset = page * PagedScreenReaderStrategy._LINES_PER_PAGE; - let startLineNumber = offset + 1; - let endLineNumber = offset + PagedScreenReaderStrategy._LINES_PER_PAGE; + const offset = page * PagedScreenReaderStrategy._LINES_PER_PAGE; + const startLineNumber = offset + 1; + const endLineNumber = offset + PagedScreenReaderStrategy._LINES_PER_PAGE; return new Range(startLineNumber, 1, endLineNumber + 1, 1); } public static fromEditorSelection(previousState: TextAreaState, model: ISimpleModel, selection: Range, trimLongText: boolean): TextAreaState { - let selectionStartPage = PagedScreenReaderStrategy._getPageOfLine(selection.startLineNumber); - let selectionStartPageRange = PagedScreenReaderStrategy._getRangeForPage(selectionStartPage); + const selectionStartPage = PagedScreenReaderStrategy._getPageOfLine(selection.startLineNumber); + const selectionStartPageRange = PagedScreenReaderStrategy._getRangeForPage(selectionStartPage); - let selectionEndPage = PagedScreenReaderStrategy._getPageOfLine(selection.endLineNumber); - let selectionEndPageRange = PagedScreenReaderStrategy._getRangeForPage(selectionEndPage); + const selectionEndPage = PagedScreenReaderStrategy._getPageOfLine(selection.endLineNumber); + const selectionEndPageRange = PagedScreenReaderStrategy._getRangeForPage(selectionEndPage); - let pretextRange = selectionStartPageRange.intersectRanges(new Range(1, 1, selection.startLineNumber, selection.startColumn))!; + const pretextRange = selectionStartPageRange.intersectRanges(new Range(1, 1, selection.startLineNumber, selection.startColumn))!; let pretext = model.getValueInRange(pretextRange, EndOfLinePreference.LF); - let lastLine = model.getLineCount(); - let lastLineMaxColumn = model.getLineMaxColumn(lastLine); - let posttextRange = selectionEndPageRange.intersectRanges(new Range(selection.endLineNumber, selection.endColumn, lastLine, lastLineMaxColumn))!; + const lastLine = model.getLineCount(); + const lastLineMaxColumn = model.getLineMaxColumn(lastLine); + const posttextRange = selectionEndPageRange.intersectRanges(new Range(selection.endLineNumber, selection.endColumn, lastLine, lastLineMaxColumn))!; let posttext = model.getValueInRange(posttextRange, EndOfLinePreference.LF); @@ -261,8 +261,8 @@ export class PagedScreenReaderStrategy { // take full selection text = model.getValueInRange(selection, EndOfLinePreference.LF); } else { - let selectionRange1 = selectionStartPageRange.intersectRanges(selection)!; - let selectionRange2 = selectionEndPageRange.intersectRanges(selection)!; + const selectionRange1 = selectionStartPageRange.intersectRanges(selection)!; + const selectionRange2 = selectionEndPageRange.intersectRanges(selection)!; text = ( model.getValueInRange(selectionRange1, EndOfLinePreference.LF) + String.fromCharCode(8230) diff --git a/src/vs/editor/browser/core/editorState.ts b/src/vs/editor/browser/core/editorState.ts index f1351bec8db..9206df4dcf9 100644 --- a/src/vs/editor/browser/core/editorState.ts +++ b/src/vs/editor/browser/core/editorState.ts @@ -29,7 +29,7 @@ export class EditorState { this.flags = flags; if ((this.flags & CodeEditorStateFlag.Value) !== 0) { - let model = editor.getModel(); + const model = editor.getModel(); this.modelVersionId = model ? strings.format('{0}#{1}', model.uri.toString(), model.getVersionId()) : null; } if ((this.flags & CodeEditorStateFlag.Position) !== 0) { @@ -49,7 +49,7 @@ export class EditorState { if (!(other instanceof EditorState)) { return false; } - let state = other; + const state = other; if (this.modelVersionId !== state.modelVersionId) { return false; diff --git a/src/vs/editor/browser/editorDom.ts b/src/vs/editor/browser/editorDom.ts index 4168c714fbb..92357091f08 100644 --- a/src/vs/editor/browser/editorDom.ts +++ b/src/vs/editor/browser/editorDom.ts @@ -59,7 +59,7 @@ export class EditorPagePosition { } export function createEditorPagePosition(editorViewDomNode: HTMLElement): EditorPagePosition { - let editorPos = dom.getDomNodePagePosition(editorViewDomNode); + const editorPos = dom.getDomNodePagePosition(editorViewDomNode); return new EditorPagePosition(editorPos.left, editorPos.top, editorPos.width, editorPos.height); } @@ -89,7 +89,7 @@ export interface EditorMouseEventMerger { export class EditorMouseEventFactory { - private _editorViewDomNode: HTMLElement; + private readonly _editorViewDomNode: HTMLElement; constructor(editorViewDomNode: HTMLElement) { this._editorViewDomNode = editorViewDomNode; @@ -124,7 +124,7 @@ export class EditorMouseEventFactory { } public onMouseMoveThrottled(target: HTMLElement, callback: (e: EditorMouseEvent) => void, merger: EditorMouseEventMerger, minimumTimeMs: number): IDisposable { - let myMerger: dom.IEventMerger = (lastEvent: EditorMouseEvent, currentEvent: MouseEvent): EditorMouseEvent => { + const myMerger: dom.IEventMerger = (lastEvent: EditorMouseEvent, currentEvent: MouseEvent): EditorMouseEvent => { return merger(lastEvent, this._create(currentEvent)); }; return dom.addDisposableThrottledListener(target, 'mousemove', callback, myMerger, minimumTimeMs); @@ -133,8 +133,8 @@ export class EditorMouseEventFactory { export class GlobalEditorMouseMoveMonitor extends Disposable { - private _editorViewDomNode: HTMLElement; - private _globalMouseMoveMonitor: GlobalMouseMoveMonitor; + private readonly _editorViewDomNode: HTMLElement; + private readonly _globalMouseMoveMonitor: GlobalMouseMoveMonitor; private _keydownListener: IDisposable | null; constructor(editorViewDomNode: HTMLElement) { @@ -157,7 +157,7 @@ export class GlobalEditorMouseMoveMonitor extends Disposable { this._globalMouseMoveMonitor.stopMonitoring(true); }, true); - let myMerger: dom.IEventMerger = (lastEvent: EditorMouseEvent, currentEvent: MouseEvent): EditorMouseEvent => { + const myMerger: dom.IEventMerger = (lastEvent: EditorMouseEvent, currentEvent: MouseEvent): EditorMouseEvent => { return merger(lastEvent, new EditorMouseEvent(currentEvent, this._editorViewDomNode)); }; diff --git a/src/vs/editor/browser/editorExtensions.ts b/src/vs/editor/browser/editorExtensions.ts index b7061803c01..7406de01ffb 100644 --- a/src/vs/editor/browser/editorExtensions.ts +++ b/src/vs/editor/browser/editorExtensions.ts @@ -127,7 +127,7 @@ export abstract class EditorCommand extends Command { */ public static bindToContribution(controllerGetter: (editor: ICodeEditor) => T): EditorControllerCommand { return class EditorControllerCommandImpl extends EditorCommand { - private _callback: (controller: T, args: any) => void; + private readonly _callback: (controller: T, args: any) => void; constructor(opts: IContributionCommandOptions) { super(opts); @@ -136,7 +136,7 @@ export abstract class EditorCommand extends Command { } public runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void { - let controller = controllerGetter(editor); + const controller = controllerGetter(editor); if (controller) { this._callback(controllerGetter(editor), args); } @@ -148,7 +148,7 @@ export abstract class EditorCommand extends Command { const codeEditorService = accessor.get(ICodeEditorService); // Find the editor with text focus or active - let editor = codeEditorService.getFocusedCodeEditor() || codeEditorService.getActiveCodeEditor(); + const editor = codeEditorService.getFocusedCodeEditor() || codeEditorService.getActiveCodeEditor(); if (!editor) { // well, at least we tried... return; @@ -267,7 +267,7 @@ export function registerDefaultLanguageCommand(id: string, handler: (model: ITex return accessor.get(ITextModelService).createModelReference(resource).then(reference => { return new Promise((resolve, reject) => { try { - let result = handler(reference.object.textEditorModel, Position.lift(position), args); + const result = handler(reference.object.textEditorModel, Position.lift(position), args); resolve(result); } catch (err) { reject(err); @@ -320,9 +320,9 @@ class EditorContributionRegistry { public static readonly INSTANCE = new EditorContributionRegistry(); - private editorContributions: IEditorContributionCtor[]; - private editorActions: EditorAction[]; - private editorCommands: { [commandId: string]: EditorCommand; }; + private readonly editorContributions: IEditorContributionCtor[]; + private readonly editorActions: EditorAction[]; + private readonly editorCommands: { [commandId: string]: EditorCommand; }; constructor() { this.editorContributions = []; diff --git a/src/vs/editor/browser/services/abstractCodeEditorService.ts b/src/vs/editor/browser/services/abstractCodeEditorService.ts index 55b6a6dcec1..c4a4be40d46 100644 --- a/src/vs/editor/browser/services/abstractCodeEditorService.ts +++ b/src/vs/editor/browser/services/abstractCodeEditorService.ts @@ -31,8 +31,8 @@ export abstract class AbstractCodeEditorService extends Disposable implements IC public readonly onDidChangeTransientModelProperty: Event = this._onDidChangeTransientModelProperty.event; - private _codeEditors: { [editorId: string]: ICodeEditor; }; - private _diffEditors: { [editorId: string]: IDiffEditor; }; + private readonly _codeEditors: { [editorId: string]: ICodeEditor; }; + private readonly _diffEditors: { [editorId: string]: IDiffEditor; }; constructor() { super(); @@ -73,7 +73,7 @@ export abstract class AbstractCodeEditorService extends Disposable implements IC getFocusedCodeEditor(): ICodeEditor | null { let editorWithWidgetFocus: ICodeEditor | null = null; - let editors = this.listCodeEditors(); + const editors = this.listCodeEditors(); for (const editor of editors) { if (editor.hasTextFocus()) { @@ -93,7 +93,7 @@ export abstract class AbstractCodeEditorService extends Disposable implements IC abstract removeDecorationType(key: string): void; abstract resolveDecorationOptions(decorationTypeKey: string | undefined, writable: boolean): IModelDecorationOptions; - private _transientWatchers: { [uri: string]: ModelTransientSettingWatcher; } = {}; + private readonly _transientWatchers: { [uri: string]: ModelTransientSettingWatcher; } = {}; public setTransientModelProperty(model: ITextModel, key: string, value: any): void { const uri = model.uri.toString(); diff --git a/src/vs/editor/browser/services/codeEditorServiceImpl.ts b/src/vs/editor/browser/services/codeEditorServiceImpl.ts index 36dd7415722..7ad0bcc4078 100644 --- a/src/vs/editor/browser/services/codeEditorServiceImpl.ts +++ b/src/vs/editor/browser/services/codeEditorServiceImpl.ts @@ -16,9 +16,9 @@ import { ITheme, IThemeService, ThemeColor } from 'vs/platform/theme/common/them export abstract class CodeEditorServiceImpl extends AbstractCodeEditorService { - private _styleSheet: HTMLStyleElement; - private _decorationOptionProviders: { [key: string]: IModelDecorationOptionsProvider }; - private _themeService: IThemeService; + private readonly _styleSheet: HTMLStyleElement; + private readonly _decorationOptionProviders: { [key: string]: IModelDecorationOptionsProvider }; + private readonly _themeService: IThemeService; constructor(@IThemeService themeService: IThemeService, styleSheet = dom.createStyleSheet()) { super(); @@ -30,7 +30,7 @@ export abstract class CodeEditorServiceImpl extends AbstractCodeEditorService { public registerDecorationType(key: string, options: IDecorationRenderOptions, parentTypeKey?: string): void { let provider = this._decorationOptionProviders[key]; if (!provider) { - let providerArgs: ProviderArguments = { + const providerArgs: ProviderArguments = { styleSheet: this._styleSheet, key: key, parentTypeKey: parentTypeKey, @@ -47,7 +47,7 @@ export abstract class CodeEditorServiceImpl extends AbstractCodeEditorService { } public removeDecorationType(key: string): void { - let provider = this._decorationOptionProviders[key]; + const provider = this._decorationOptionProviders[key]; if (provider) { provider.refCount--; if (provider.refCount <= 0) { @@ -59,7 +59,7 @@ export abstract class CodeEditorServiceImpl extends AbstractCodeEditorService { } public resolveDecorationOptions(decorationTypeKey: string, writable: boolean): IModelDecorationOptions { - let provider = this._decorationOptionProviders[decorationTypeKey]; + const provider = this._decorationOptionProviders[decorationTypeKey]; if (!provider) { throw new Error('Unknown decoration type key: ' + decorationTypeKey); } @@ -79,7 +79,7 @@ class DecorationSubTypeOptionsProvider implements IModelDecorationOptionsProvide public refCount: number; - private _parentTypeKey: string | undefined; + private readonly _parentTypeKey: string | undefined; private _beforeContentRules: DecorationCSSRules | null; private _afterContentRules: DecorationCSSRules | null; @@ -92,7 +92,7 @@ class DecorationSubTypeOptionsProvider implements IModelDecorationOptionsProvide } public getOptions(codeEditorService: AbstractCodeEditorService, writable: boolean): IModelDecorationOptions { - let options = codeEditorService.resolveDecorationOptions(this._parentTypeKey, true); + const options = codeEditorService.resolveDecorationOptions(this._parentTypeKey, true); if (this._beforeContentRules) { options.beforeContentClassName = this._beforeContentRules.className; } @@ -168,12 +168,12 @@ class DecorationTypeOptionsProvider implements IModelDecorationOptionsProvider { this.afterContentClassName = createCSSRules(ModelDecorationCSSRuleType.AfterContentClassName); this.glyphMarginClassName = createCSSRules(ModelDecorationCSSRuleType.GlyphMarginClassName); - let options = providerArgs.options; + const options = providerArgs.options; this.isWholeLine = Boolean(options.isWholeLine); this.stickiness = options.rangeBehavior; - let lightOverviewRulerColor = options.light && options.light.overviewRulerColor || options.overviewRulerColor; - let darkOverviewRulerColor = options.dark && options.dark.overviewRulerColor || options.overviewRulerColor; + const lightOverviewRulerColor = options.light && options.light.overviewRulerColor || options.overviewRulerColor; + const darkOverviewRulerColor = options.dark && options.dark.overviewRulerColor || options.overviewRulerColor; if ( typeof lightOverviewRulerColor !== 'undefined' || typeof darkOverviewRulerColor !== 'undefined' @@ -245,13 +245,13 @@ const _CSS_MAP: { [prop: string]: string; } = { class DecorationCSSRules { private _theme: ITheme; - private _className: string; - private _unThemedSelector: string; + private readonly _className: string; + private readonly _unThemedSelector: string; private _hasContent: boolean; private _hasLetterSpacing: boolean; - private _ruleType: ModelDecorationCSSRuleType; + private readonly _ruleType: ModelDecorationCSSRuleType; private _themeListener: IDisposable | null; - private _providerArgs: ProviderArguments; + private readonly _providerArgs: ProviderArguments; private _usesThemeColors: boolean; public constructor(ruleType: ModelDecorationCSSRuleType, providerArgs: ProviderArguments, themeService: IThemeService) { @@ -307,7 +307,7 @@ class DecorationCSSRules { } private _buildCSS(): void { - let options = this._providerArgs.options; + const options = this._providerArgs.options; let unthemedCSS: string, lightCSS: string, darkCSS: string; switch (this._ruleType) { case ModelDecorationCSSRuleType.ClassName: @@ -338,7 +338,7 @@ class DecorationCSSRules { default: throw new Error('Unknown rule type: ' + this._ruleType); } - let sheet = this._providerArgs.styleSheet.sheet; + const sheet = this._providerArgs.styleSheet.sheet; let hasContent = false; if (unthemedCSS.length > 0) { @@ -367,7 +367,7 @@ class DecorationCSSRules { if (!opts) { return ''; } - let cssTextArr: string[] = []; + const cssTextArr: string[] = []; this.collectCSSText(opts, ['backgroundColor'], cssTextArr); this.collectCSSText(opts, ['outline', 'outlineColor', 'outlineStyle', 'outlineWidth'], cssTextArr); this.collectBorderSettingsCSSText(opts, cssTextArr); @@ -381,7 +381,7 @@ class DecorationCSSRules { if (!opts) { return ''; } - let cssTextArr: string[] = []; + const cssTextArr: string[] = []; this.collectCSSText(opts, ['fontStyle', 'fontWeight', 'textDecoration', 'cursor', 'color', 'opacity', 'letterSpacing'], cssTextArr); if (opts.letterSpacing) { this._hasLetterSpacing = true; @@ -396,7 +396,7 @@ class DecorationCSSRules { if (!opts) { return ''; } - let cssTextArr: string[] = []; + const cssTextArr: string[] = []; if (typeof opts !== 'undefined') { this.collectBorderSettingsCSSText(opts, cssTextArr); @@ -425,7 +425,7 @@ class DecorationCSSRules { if (!opts) { return ''; } - let cssTextArr: string[] = []; + const cssTextArr: string[] = []; if (typeof opts.gutterIconPath !== 'undefined') { cssTextArr.push(strings.format(_CSS_MAP.gutterIconPath, URI.revive(opts.gutterIconPath).toString(true).replace(/'/g, '%27'))); @@ -446,9 +446,9 @@ class DecorationCSSRules { } private collectCSSText(opts: any, properties: string[], cssTextArr: string[]): boolean { - let lenBefore = cssTextArr.length; + const lenBefore = cssTextArr.length; for (let property of properties) { - let value = this.resolveValue(opts[property]); + const value = this.resolveValue(opts[property]); if (typeof value === 'string') { cssTextArr.push(strings.format(_CSS_MAP[property], value)); } @@ -459,7 +459,7 @@ class DecorationCSSRules { private resolveValue(value: string | ThemeColor): string { if (isThemeColor(value)) { this._usesThemeColors = true; - let color = this._theme.getColor(value.id); + const color = this._theme.getColor(value.id); if (color) { return color.toString(); } diff --git a/src/vs/editor/browser/view/viewController.ts b/src/vs/editor/browser/view/viewController.ts index bb087386420..7c7db40a3cf 100644 --- a/src/vs/editor/browser/view/viewController.ts +++ b/src/vs/editor/browser/view/viewController.ts @@ -98,7 +98,7 @@ export class ViewController { } private _validateViewColumn(viewPosition: Position): Position { - let minColumn = this.viewModel.getLineMinColumn(viewPosition.lineNumber); + const minColumn = this.viewModel.getLineMinColumn(viewPosition.lineNumber); if (viewPosition.column < minColumn) { return new Position(viewPosition.lineNumber, minColumn); } diff --git a/src/vs/editor/browser/view/viewImpl.ts b/src/vs/editor/browser/view/viewImpl.ts index f3b304c9142..b6240b274a1 100644 --- a/src/vs/editor/browser/view/viewImpl.ts +++ b/src/vs/editor/browser/view/viewImpl.ts @@ -63,11 +63,11 @@ const invalidFunc = () => { throw new Error(`Invalid change accessor`); }; export class View extends ViewEventHandler { - private eventDispatcher: ViewEventDispatcher; + private readonly eventDispatcher: ViewEventDispatcher; private _scrollbar: EditorScrollbar; - private _context: ViewContext; - private _cursor: Cursor; + private readonly _context: ViewContext; + private readonly _cursor: Cursor; // The view lines private viewLines: ViewLines; @@ -105,7 +105,7 @@ export class View extends ViewEventHandler { this._renderAnimationFrame = null; this.outgoingEvents = outgoingEvents; - let viewController = new ViewController(configuration, model, this.outgoingEvents, commandDelegate); + const viewController = new ViewController(configuration, model, this.outgoingEvents, commandDelegate); // The event dispatcher will always go through _renderOnce before dispatching any events this.eventDispatcher = new ViewEventDispatcher((callback: () => void) => this._renderOnce(callback)); @@ -167,21 +167,21 @@ export class View extends ViewEventHandler { this.viewParts.push(this.viewZones); // Decorations overview ruler - let decorationsOverviewRuler = new DecorationsOverviewRuler(this._context); + const decorationsOverviewRuler = new DecorationsOverviewRuler(this._context); this.viewParts.push(decorationsOverviewRuler); - let scrollDecoration = new ScrollDecorationViewPart(this._context); + const scrollDecoration = new ScrollDecorationViewPart(this._context); this.viewParts.push(scrollDecoration); - let contentViewOverlays = new ContentViewOverlays(this._context); + const contentViewOverlays = new ContentViewOverlays(this._context); this.viewParts.push(contentViewOverlays); contentViewOverlays.addDynamicOverlay(new CurrentLineHighlightOverlay(this._context)); contentViewOverlays.addDynamicOverlay(new SelectionsOverlay(this._context)); contentViewOverlays.addDynamicOverlay(new IndentGuidesOverlay(this._context)); contentViewOverlays.addDynamicOverlay(new DecorationsOverlay(this._context)); - let marginViewOverlays = new MarginViewOverlays(this._context); + const marginViewOverlays = new MarginViewOverlays(this._context); this.viewParts.push(marginViewOverlays); marginViewOverlays.addDynamicOverlay(new CurrentLineMarginHighlightOverlay(this._context)); marginViewOverlays.addDynamicOverlay(new GlyphMarginOverlay(this._context)); @@ -189,7 +189,7 @@ export class View extends ViewEventHandler { marginViewOverlays.addDynamicOverlay(new LinesDecorationsOverlay(this._context)); marginViewOverlays.addDynamicOverlay(new LineNumbersOverlay(this._context)); - let margin = new Margin(this._context); + const margin = new Margin(this._context); margin.getDomNode().appendChild(this.viewZones.marginDomNode); margin.getDomNode().appendChild(marginViewOverlays.getDomNode()); this.viewParts.push(margin); @@ -205,16 +205,16 @@ export class View extends ViewEventHandler { this.overlayWidgets = new ViewOverlayWidgets(this._context); this.viewParts.push(this.overlayWidgets); - let rulers = new Rulers(this._context); + const rulers = new Rulers(this._context); this.viewParts.push(rulers); - let minimap = new Minimap(this._context); + const minimap = new Minimap(this._context); this.viewParts.push(minimap); // -------------- Wire dom nodes up if (decorationsOverviewRuler) { - let overviewRulerData = this._scrollbar.getOverviewRulerLayoutInfo(); + const overviewRulerData = this._scrollbar.getOverviewRulerLayoutInfo(); overviewRulerData.parent.insertBefore(decorationsOverviewRuler.getDomNode(), overviewRulerData.insertBefore); } @@ -297,7 +297,7 @@ export class View extends ViewEventHandler { } private getEditorClassName() { - let focused = this._textAreaHandler.isFocused() ? ' focused' : ''; + const focused = this._textAreaHandler.isFocused() ? ' focused' : ''; return this._context.configuration.editor.editorClassName + ' ' + getThemeTypeSelector(this._context.theme.type) + focused; } @@ -356,7 +356,7 @@ export class View extends ViewEventHandler { } private _renderOnce(callback: () => any): any { - let r = safeInvokeNoArg(callback); + const r = safeInvokeNoArg(callback); this._scheduleRender(); return r; } @@ -379,7 +379,7 @@ export class View extends ViewEventHandler { private _getViewPartsToRender(): ViewPart[] { let result: ViewPart[] = [], resultLen = 0; for (let i = 0, len = this.viewParts.length; i < len; i++) { - let viewPart = this.viewParts[i]; + const viewPart = this.viewParts[i]; if (viewPart.shouldRender()) { result[resultLen++] = viewPart; } @@ -402,7 +402,7 @@ export class View extends ViewEventHandler { const partialViewportData = this._context.viewLayout.getLinesViewportData(); this._context.model.setViewport(partialViewportData.startLineNumber, partialViewportData.endLineNumber, partialViewportData.centeredLineNumber); - let viewportData = new ViewportData( + const viewportData = new ViewportData( this._cursor.getViewSelections(), partialViewportData, this._context.viewLayout.getWhitespaceViewportData(), @@ -422,16 +422,16 @@ export class View extends ViewEventHandler { viewPartsToRender = this._getViewPartsToRender(); } - let renderingContext = new RenderingContext(this._context.viewLayout, viewportData, this.viewLines); + const renderingContext = new RenderingContext(this._context.viewLayout, viewportData, this.viewLines); // Render the rest of the parts for (let i = 0, len = viewPartsToRender.length; i < len; i++) { - let viewPart = viewPartsToRender[i]; + const viewPart = viewPartsToRender[i]; viewPart.prepareRender(renderingContext); } for (let i = 0, len = viewPartsToRender.length; i < len; i++) { - let viewPart = viewPartsToRender[i]; + const viewPart = viewPartsToRender[i]; viewPart.render(renderingContext); viewPart.onDidRender(); } @@ -452,11 +452,11 @@ export class View extends ViewEventHandler { } public getOffsetForColumn(modelLineNumber: number, modelColumn: number): number { - let modelPosition = this._context.model.validateModelPosition({ + const modelPosition = this._context.model.validateModelPosition({ lineNumber: modelLineNumber, column: modelColumn }); - let viewPosition = this._context.model.coordinatesConverter.convertModelPositionToViewPosition(modelPosition); + const viewPosition = this._context.model.coordinatesConverter.convertModelPositionToViewPosition(modelPosition); this._flushAccumulatedAndRenderNow(); const visibleRange = this.viewLines.visibleRangeForPosition(new Position(viewPosition.lineNumber, viewPosition.column)); if (!visibleRange) { @@ -477,7 +477,7 @@ export class View extends ViewEventHandler { let zonesHaveChanged = false; this._renderOnce(() => { - let changeAccessor: editorBrowser.IViewZoneChangeAccessor = { + const changeAccessor: editorBrowser.IViewZoneChangeAccessor = { addZone: (zone: editorBrowser.IViewZone): number => { zonesHaveChanged = true; return this.viewZones.addZone(zone); @@ -516,7 +516,7 @@ export class View extends ViewEventHandler { // Force everything to render... this.viewLines.forceShouldRender(); for (let i = 0, len = this.viewParts.length; i < len; i++) { - let viewPart = this.viewParts[i]; + const viewPart = this.viewParts[i]; viewPart.forceShouldRender(); } } @@ -542,9 +542,9 @@ export class View extends ViewEventHandler { } public layoutContentWidget(widgetData: IContentWidgetData): void { - let newPosition = widgetData.position ? widgetData.position.position : null; - let newRange = widgetData.position ? widgetData.position.range : null; - let newPreference = widgetData.position ? widgetData.position.preference : null; + const newPosition = widgetData.position ? widgetData.position.position : null; + const newRange = widgetData.position ? widgetData.position.range : null; + const newPreference = widgetData.position ? widgetData.position.preference : null; this.contentWidgets.setWidgetPosition(widgetData.widget, newPosition, newRange, newPreference); this._scheduleRender(); } @@ -561,8 +561,8 @@ export class View extends ViewEventHandler { } public layoutOverlayWidget(widgetData: IOverlayWidgetData): void { - let newPreference = widgetData.position ? widgetData.position.preference : null; - let shouldRender = this.overlayWidgets.setWidgetPosition(widgetData.widget, newPreference); + const newPreference = widgetData.position ? widgetData.position.preference : null; + const shouldRender = this.overlayWidgets.setWidgetPosition(widgetData.widget, newPreference); if (shouldRender) { this._scheduleRender(); } diff --git a/src/vs/editor/browser/view/viewLayer.ts b/src/vs/editor/browser/view/viewLayer.ts index aede693ffdb..b16f615c3b1 100644 --- a/src/vs/editor/browser/view/viewLayer.ts +++ b/src/vs/editor/browser/view/viewLayer.ts @@ -77,7 +77,7 @@ export class RenderedLinesCollection { } public getLine(lineNumber: number): T { - let lineIndex = lineNumber - this._rendLineNumberStart; + const lineIndex = lineNumber - this._rendLineNumberStart; if (lineIndex < 0 || lineIndex >= this._lines.length) { throw new Error('Illegal value for lineNumber'); } @@ -93,12 +93,12 @@ export class RenderedLinesCollection { return null; } - let startLineNumber = this.getStartLineNumber(); - let endLineNumber = this.getEndLineNumber(); + const startLineNumber = this.getStartLineNumber(); + const endLineNumber = this.getEndLineNumber(); if (deleteToLineNumber < startLineNumber) { // deleting above the viewport - let deleteCnt = deleteToLineNumber - deleteFromLineNumber + 1; + const deleteCnt = deleteToLineNumber - deleteFromLineNumber + 1; this._rendLineNumberStart -= deleteCnt; return null; } @@ -112,7 +112,7 @@ export class RenderedLinesCollection { let deleteStartIndex = 0; let deleteCount = 0; for (let lineNumber = startLineNumber; lineNumber <= endLineNumber; lineNumber++) { - let lineIndex = lineNumber - this._rendLineNumberStart; + const lineIndex = lineNumber - this._rendLineNumberStart; if (deleteFromLineNumber <= lineNumber && lineNumber <= deleteToLineNumber) { // this is a line to be deleted @@ -141,7 +141,7 @@ export class RenderedLinesCollection { this._rendLineNumberStart -= deleteAboveCount; } - let deleted = this._lines.splice(deleteStartIndex, deleteCount); + const deleted = this._lines.splice(deleteStartIndex, deleteCount); return deleted; } @@ -151,8 +151,8 @@ export class RenderedLinesCollection { return false; } - let startLineNumber = this.getStartLineNumber(); - let endLineNumber = this.getEndLineNumber(); + const startLineNumber = this.getStartLineNumber(); + const endLineNumber = this.getEndLineNumber(); let someoneNotified = false; @@ -173,9 +173,9 @@ export class RenderedLinesCollection { return null; } - let insertCnt = insertToLineNumber - insertFromLineNumber + 1; - let startLineNumber = this.getStartLineNumber(); - let endLineNumber = this.getEndLineNumber(); + const insertCnt = insertToLineNumber - insertFromLineNumber + 1; + const startLineNumber = this.getStartLineNumber(); + const endLineNumber = this.getEndLineNumber(); if (insertFromLineNumber <= startLineNumber) { // inserting above the viewport @@ -190,19 +190,19 @@ export class RenderedLinesCollection { if (insertCnt + insertFromLineNumber > endLineNumber) { // insert inside the viewport in such a way that all remaining lines are pushed outside - let deleted = this._lines.splice(insertFromLineNumber - this._rendLineNumberStart, endLineNumber - insertFromLineNumber + 1); + const deleted = this._lines.splice(insertFromLineNumber - this._rendLineNumberStart, endLineNumber - insertFromLineNumber + 1); return deleted; } // insert inside the viewport, push out some lines, but not all remaining lines - let newLines: T[] = []; + const newLines: T[] = []; for (let i = 0; i < insertCnt; i++) { newLines[i] = this._createLine(); } - let insertIndex = insertFromLineNumber - this._rendLineNumberStart; - let beforeLines = this._lines.slice(0, insertIndex); - let afterLines = this._lines.slice(insertIndex, this._lines.length - insertCnt); - let deletedLines = this._lines.slice(this._lines.length - insertCnt, this._lines.length); + const insertIndex = insertFromLineNumber - this._rendLineNumberStart; + const beforeLines = this._lines.slice(0, insertIndex); + const afterLines = this._lines.slice(insertIndex, this._lines.length - insertCnt); + const deletedLines = this._lines.slice(this._lines.length - insertCnt, this._lines.length); this._lines = beforeLines.concat(newLines).concat(afterLines); @@ -215,23 +215,23 @@ export class RenderedLinesCollection { return false; } - let startLineNumber = this.getStartLineNumber(); - let endLineNumber = this.getEndLineNumber(); + const startLineNumber = this.getStartLineNumber(); + const endLineNumber = this.getEndLineNumber(); let notifiedSomeone = false; for (let i = 0, len = ranges.length; i < len; i++) { - let rng = ranges[i]; + const rng = ranges[i]; if (rng.toLineNumber < startLineNumber || rng.fromLineNumber > endLineNumber) { // range outside viewport continue; } - let from = Math.max(startLineNumber, rng.fromLineNumber); - let to = Math.min(endLineNumber, rng.toLineNumber); + const from = Math.max(startLineNumber, rng.fromLineNumber); + const to = Math.min(endLineNumber, rng.toLineNumber); for (let lineNumber = from; lineNumber <= to; lineNumber++) { - let lineIndex = lineNumber - this._rendLineNumberStart; + const lineIndex = lineNumber - this._rendLineNumberStart; this._lines[lineIndex].onTokensChanged(); notifiedSomeone = true; } @@ -258,7 +258,7 @@ export class VisibleLinesCollection { } private _createDomNode(): FastDomNode { - let domNode = createFastDomNode(document.createElement('div')); + const domNode = createFastDomNode(document.createElement('div')); domNode.setClassName('view-layer'); domNode.setPosition('absolute'); domNode.domNode.setAttribute('role', 'presentation'); @@ -283,11 +283,11 @@ export class VisibleLinesCollection { } public onLinesDeleted(e: viewEvents.ViewLinesDeletedEvent): boolean { - let deleted = this._linesCollection.onLinesDeleted(e.fromLineNumber, e.toLineNumber); + const deleted = this._linesCollection.onLinesDeleted(e.fromLineNumber, e.toLineNumber); if (deleted) { // Remove from DOM for (let i = 0, len = deleted.length; i < len; i++) { - let lineDomNode = deleted[i].getDomNode(); + const lineDomNode = deleted[i].getDomNode(); if (lineDomNode) { this.domNode.domNode.removeChild(lineDomNode); } @@ -298,11 +298,11 @@ export class VisibleLinesCollection { } public onLinesInserted(e: viewEvents.ViewLinesInsertedEvent): boolean { - let deleted = this._linesCollection.onLinesInserted(e.fromLineNumber, e.toLineNumber); + const deleted = this._linesCollection.onLinesInserted(e.fromLineNumber, e.toLineNumber); if (deleted) { // Remove from DOM for (let i = 0, len = deleted.length; i < len; i++) { - let lineDomNode = deleted[i].getDomNode(); + const lineDomNode = deleted[i].getDomNode(); if (lineDomNode) { this.domNode.domNode.removeChild(lineDomNode); } @@ -340,18 +340,18 @@ export class VisibleLinesCollection { public renderLines(viewportData: ViewportData): void { - let inp = this._linesCollection._get(); + const inp = this._linesCollection._get(); - let renderer = new ViewLayerRenderer(this.domNode.domNode, this._host, viewportData); + const renderer = new ViewLayerRenderer(this.domNode.domNode, this._host, viewportData); - let ctx: IRendererContext = { + const ctx: IRendererContext = { rendLineNumberStart: inp.rendLineNumberStart, lines: inp.lines, linesLength: inp.lines.length }; // Decide if this render will do a single update (single large .innerHTML) or many updates (inserting/removing dom nodes) - let resCtx = renderer.render(ctx, viewportData.startLineNumber, viewportData.endLineNumber, viewportData.relativeVerticalOffset); + const resCtx = renderer.render(ctx, viewportData.startLineNumber, viewportData.endLineNumber, viewportData.relativeVerticalOffset); this._linesCollection._set(resCtx.rendLineNumberStart, resCtx.lines); } @@ -377,7 +377,7 @@ class ViewLayerRenderer { public render(inContext: IRendererContext, startLineNumber: number, stopLineNumber: number, deltaTop: number[]): IRendererContext { - let ctx: IRendererContext = { + const ctx: IRendererContext = { rendLineNumberStart: inContext.rendLineNumberStart, lines: inContext.lines.slice(0), linesLength: inContext.linesLength @@ -406,15 +406,15 @@ class ViewLayerRenderer { if (ctx.rendLineNumberStart > startLineNumber) { // Insert lines before - let fromLineNumber = startLineNumber; - let toLineNumber = Math.min(stopLineNumber, ctx.rendLineNumberStart - 1); + const fromLineNumber = startLineNumber; + const toLineNumber = Math.min(stopLineNumber, ctx.rendLineNumberStart - 1); if (fromLineNumber <= toLineNumber) { this._insertLinesBefore(ctx, fromLineNumber, toLineNumber, deltaTop, startLineNumber); ctx.linesLength += toLineNumber - fromLineNumber + 1; } } else if (ctx.rendLineNumberStart < startLineNumber) { // Remove lines before - let removeCnt = Math.min(ctx.linesLength, startLineNumber - ctx.rendLineNumberStart); + const removeCnt = Math.min(ctx.linesLength, startLineNumber - ctx.rendLineNumberStart); if (removeCnt > 0) { this._removeLinesBefore(ctx, removeCnt); ctx.linesLength -= removeCnt; @@ -425,8 +425,8 @@ class ViewLayerRenderer { if (ctx.rendLineNumberStart + ctx.linesLength - 1 < stopLineNumber) { // Insert lines after - let fromLineNumber = ctx.rendLineNumberStart + ctx.linesLength; - let toLineNumber = stopLineNumber; + const fromLineNumber = ctx.rendLineNumberStart + ctx.linesLength; + const toLineNumber = stopLineNumber; if (fromLineNumber <= toLineNumber) { this._insertLinesAfter(ctx, fromLineNumber, toLineNumber, deltaTop, startLineNumber); @@ -435,9 +435,9 @@ class ViewLayerRenderer { } else if (ctx.rendLineNumberStart + ctx.linesLength - 1 > stopLineNumber) { // Remove lines after - let fromLineNumber = Math.max(0, stopLineNumber - ctx.rendLineNumberStart + 1); - let toLineNumber = ctx.linesLength - 1; - let removeCnt = toLineNumber - fromLineNumber + 1; + const fromLineNumber = Math.max(0, stopLineNumber - ctx.rendLineNumberStart + 1); + const toLineNumber = ctx.linesLength - 1; + const removeCnt = toLineNumber - fromLineNumber + 1; if (removeCnt > 0) { this._removeLinesAfter(ctx, removeCnt); @@ -455,13 +455,13 @@ class ViewLayerRenderer { const lines = ctx.lines; for (let i = startIndex; i <= endIndex; i++) { - let lineNumber = rendLineNumberStart + i; + const lineNumber = rendLineNumberStart + i; lines[i].layoutLine(lineNumber, deltaTop[lineNumber - deltaLN]); } } private _insertLinesBefore(ctx: IRendererContext, fromLineNumber: number, toLineNumber: number, deltaTop: number[], deltaLN: number): void { - let newLines: T[] = []; + const newLines: T[] = []; let newLinesLen = 0; for (let lineNumber = fromLineNumber; lineNumber <= toLineNumber; lineNumber++) { newLines[newLinesLen++] = this.host.createVisibleLine(); @@ -471,7 +471,7 @@ class ViewLayerRenderer { private _removeLinesBefore(ctx: IRendererContext, removeCount: number): void { for (let i = 0; i < removeCount; i++) { - let lineDomNode = ctx.lines[i].getDomNode(); + const lineDomNode = ctx.lines[i].getDomNode(); if (lineDomNode) { this.domNode.removeChild(lineDomNode); } @@ -480,7 +480,7 @@ class ViewLayerRenderer { } private _insertLinesAfter(ctx: IRendererContext, fromLineNumber: number, toLineNumber: number, deltaTop: number[], deltaLN: number): void { - let newLines: T[] = []; + const newLines: T[] = []; let newLinesLen = 0; for (let lineNumber = fromLineNumber; lineNumber <= toLineNumber; lineNumber++) { newLines[newLinesLen++] = this.host.createVisibleLine(); @@ -489,10 +489,10 @@ class ViewLayerRenderer { } private _removeLinesAfter(ctx: IRendererContext, removeCount: number): void { - let removeIndex = ctx.linesLength - removeCount; + const removeIndex = ctx.linesLength - removeCount; for (let i = 0; i < removeCount; i++) { - let lineDomNode = ctx.lines[removeIndex + i].getDomNode(); + const lineDomNode = ctx.lines[removeIndex + i].getDomNode(); if (lineDomNode) { this.domNode.removeChild(lineDomNode); } @@ -501,7 +501,7 @@ class ViewLayerRenderer { } private _finishRenderingNewLines(ctx: IRendererContext, domNodeIsEmpty: boolean, newLinesHTML: string, wasNew: boolean[]): void { - let lastChild = this.domNode.lastChild; + const lastChild = this.domNode.lastChild; if (domNodeIsEmpty || !lastChild) { this.domNode.innerHTML = newLinesHTML; } else { @@ -510,7 +510,7 @@ class ViewLayerRenderer { let currChild = this.domNode.lastChild; for (let i = ctx.linesLength - 1; i >= 0; i--) { - let line = ctx.lines[i]; + const line = ctx.lines[i]; if (wasNew[i]) { line.setDomNode(currChild); currChild = currChild.previousSibling; @@ -519,15 +519,15 @@ class ViewLayerRenderer { } private _finishRenderingInvalidLines(ctx: IRendererContext, invalidLinesHTML: string, wasInvalid: boolean[]): void { - let hugeDomNode = document.createElement('div'); + const hugeDomNode = document.createElement('div'); hugeDomNode.innerHTML = invalidLinesHTML; for (let i = 0; i < ctx.linesLength; i++) { - let line = ctx.lines[i]; + const line = ctx.lines[i]; if (wasInvalid[i]) { - let source = hugeDomNode.firstChild; - let lineDomNode = line.getDomNode()!; + const source = hugeDomNode.firstChild; + const lineDomNode = line.getDomNode()!; lineDomNode.parentNode!.replaceChild(source, lineDomNode); line.setDomNode(source); } @@ -543,7 +543,7 @@ class ViewLayerRenderer { const lines = ctx.lines; const rendLineNumberStart = ctx.rendLineNumberStart; - let wasNew: boolean[] = []; + const wasNew: boolean[] = []; { sb.reset(); let hadNewLine = false; @@ -577,10 +577,10 @@ class ViewLayerRenderer { sb.reset(); let hadInvalidLine = false; - let wasInvalid: boolean[] = []; + const wasInvalid: boolean[] = []; for (let i = 0; i < linesLength; i++) { - let line = lines[i]; + const line = lines[i]; wasInvalid[i] = false; if (wasNew[i]) { diff --git a/src/vs/editor/browser/view/viewOutgoingEvents.ts b/src/vs/editor/browser/view/viewOutgoingEvents.ts index 9c8c23470ad..ca110d888c8 100644 --- a/src/vs/editor/browser/view/viewOutgoingEvents.ts +++ b/src/vs/editor/browser/view/viewOutgoingEvents.ts @@ -32,7 +32,7 @@ export class ViewOutgoingEvents extends Disposable { public onMouseDrag: EventCallback | null = null; public onMouseDrop: EventCallback | null = null; - private _viewModel: IViewModel; + private readonly _viewModel: IViewModel; constructor(viewModel: IViewModel) { super(); diff --git a/src/vs/editor/browser/view/viewOverlays.ts b/src/vs/editor/browser/view/viewOverlays.ts index 1adf5bcad62..bc75a6b4cef 100644 --- a/src/vs/editor/browser/view/viewOverlays.ts +++ b/src/vs/editor/browser/view/viewOverlays.ts @@ -40,7 +40,7 @@ export class ViewOverlays extends ViewPart implements IVisibleLinesHost overlay.shouldRender()); + const toRender = this._dynamicOverlays.filter(overlay => overlay.shouldRender()); for (let i = 0, len = toRender.length; i < len; i++) { - let dynamicOverlay = toRender[i]; + const dynamicOverlay = toRender[i]; dynamicOverlay.prepareRender(ctx); dynamicOverlay.onDidRender(); } @@ -139,8 +139,8 @@ export class ViewOverlays extends ViewPart implements IVisibleLinesHost | null; private _renderedContent: string | null; private _lineHeight: number; @@ -179,7 +179,7 @@ export class ViewOverlayLine implements IVisibleLine { public renderLine(lineNumber: number, deltaTop: number, viewportData: ViewportData, sb: IStringBuilder): boolean { let result = ''; for (let i = 0, len = this._dynamicOverlays.length; i < len; i++) { - let dynamicOverlay = this._dynamicOverlays[i]; + const dynamicOverlay = this._dynamicOverlays[i]; result += dynamicOverlay.render(viewportData.startLineNumber, lineNumber); } @@ -276,7 +276,7 @@ export class MarginViewOverlays extends ViewOverlays { _viewOverlaysRender(ctx: RestrictedRenderingContext): void { super._viewOverlaysRender(ctx); - let height = Math.min(ctx.scrollHeight, 1000000); + const height = Math.min(ctx.scrollHeight, 1000000); this.domNode.setHeight(height); this.domNode.setWidth(this._contentLeft); } diff --git a/src/vs/editor/browser/view/viewPart.ts b/src/vs/editor/browser/view/viewPart.ts index 319ce014e58..a821807408b 100644 --- a/src/vs/editor/browser/view/viewPart.ts +++ b/src/vs/editor/browser/view/viewPart.ts @@ -50,7 +50,7 @@ export class PartFingerprints { } public static read(target: Element): PartFingerprint { - let r = target.getAttribute('data-mprt'); + const r = target.getAttribute('data-mprt'); if (r === null) { return PartFingerprint.None; } @@ -70,7 +70,7 @@ export class PartFingerprints { child = child.parentElement; } - let r = new Uint8Array(resultLen); + const r = new Uint8Array(resultLen); for (let i = 0; i < resultLen; i++) { r[i] = result[resultLen - i - 1]; } diff --git a/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts b/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts index d31268fdf88..f82b04c8475 100644 --- a/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts +++ b/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts @@ -29,7 +29,7 @@ class Coordinate { export class ViewContentWidgets extends ViewPart { - private _viewDomNode: FastDomNode; + private readonly _viewDomNode: FastDomNode; private _widgets: { [key: string]: Widget; }; public domNode: FastDomNode; @@ -59,7 +59,7 @@ export class ViewContentWidgets extends ViewPart { // --- begin event handlers public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { - let keys = Object.keys(this._widgets); + const keys = Object.keys(this._widgets); for (const widgetId of keys) { this._widgets[widgetId].onConfigurationChanged(e); } @@ -73,7 +73,7 @@ export class ViewContentWidgets extends ViewPart { return true; } public onLineMappingChanged(e: viewEvents.ViewLineMappingChangedEvent): boolean { - let keys = Object.keys(this._widgets); + const keys = Object.keys(this._widgets); for (const widgetId of keys) { this._widgets[widgetId].onLineMappingChanged(e); } @@ -139,21 +139,21 @@ export class ViewContentWidgets extends ViewPart { } public onBeforeRender(viewportData: ViewportData): void { - let keys = Object.keys(this._widgets); + const keys = Object.keys(this._widgets); for (const widgetId of keys) { this._widgets[widgetId].onBeforeRender(viewportData); } } public prepareRender(ctx: RenderingContext): void { - let keys = Object.keys(this._widgets); + const keys = Object.keys(this._widgets); for (const widgetId of keys) { this._widgets[widgetId].prepareRender(ctx); } } public render(ctx: RestrictedRenderingContext): void { - let keys = Object.keys(this._widgets); + const keys = Object.keys(this._widgets); for (const widgetId of keys) { this._widgets[widgetId].render(ctx); } @@ -180,7 +180,7 @@ class Widget { public readonly allowEditorOverflow: boolean; public readonly suppressMouseDown: boolean; - private _fixedOverflowWidgets: boolean; + private readonly _fixedOverflowWidgets: boolean; private _contentWidth: number; private _contentLeft: number; private _lineHeight: number; @@ -280,17 +280,17 @@ class Widget { // Our visible box is split horizontally by the current line => 2 boxes // a) the box above the line - let aboveLineTop = topLeft.top; - let heightAboveLine = aboveLineTop; + const aboveLineTop = topLeft.top; + const heightAboveLine = aboveLineTop; // b) the box under the line - let underLineTop = bottomLeft.top + this._lineHeight; - let heightUnderLine = ctx.viewportHeight - underLineTop; + const underLineTop = bottomLeft.top + this._lineHeight; + const heightUnderLine = ctx.viewportHeight - underLineTop; - let aboveTop = aboveLineTop - height; - let fitsAbove = (heightAboveLine >= height); - let belowTop = underLineTop; - let fitsBelow = (heightUnderLine >= height); + const aboveTop = aboveLineTop - height; + const fitsAbove = (heightAboveLine >= height); + const belowTop = underLineTop; + const fitsBelow = (heightUnderLine >= height); // And its left let actualAboveLeft = topLeft.left; @@ -320,8 +320,8 @@ class Widget { } private _layoutBoxInPage(topLeft: Coordinate, bottomLeft: Coordinate, width: number, height: number, ctx: RenderingContext): IBoxLayoutResult | null { - let aboveLeft0 = topLeft.left - ctx.scrollLeft; - let belowLeft0 = bottomLeft.left - ctx.scrollLeft; + const aboveLeft0 = topLeft.left - ctx.scrollLeft; + const belowLeft0 = bottomLeft.left - ctx.scrollLeft; if (aboveLeft0 < 0 || aboveLeft0 > this._contentWidth) { // Don't render if position is scrolled outside viewport @@ -333,39 +333,39 @@ class Widget { let aboveLeft = aboveLeft0 + this._contentLeft; let belowLeft = belowLeft0 + this._contentLeft; - let domNodePosition = dom.getDomNodePagePosition(this._viewDomNode.domNode); - let absoluteAboveTop = domNodePosition.top + aboveTop - dom.StandardWindow.scrollY; - let absoluteBelowTop = domNodePosition.top + belowTop - dom.StandardWindow.scrollY; + const domNodePosition = dom.getDomNodePagePosition(this._viewDomNode.domNode); + const absoluteAboveTop = domNodePosition.top + aboveTop - dom.StandardWindow.scrollY; + const absoluteBelowTop = domNodePosition.top + belowTop - dom.StandardWindow.scrollY; let absoluteAboveLeft = domNodePosition.left + aboveLeft - dom.StandardWindow.scrollX; let absoluteBelowLeft = domNodePosition.left + belowLeft - dom.StandardWindow.scrollX; - let INNER_WIDTH = window.innerWidth || document.documentElement!.clientWidth || document.body.clientWidth; - let INNER_HEIGHT = window.innerHeight || document.documentElement!.clientHeight || document.body.clientHeight; + const INNER_WIDTH = window.innerWidth || document.documentElement!.clientWidth || document.body.clientWidth; + const INNER_HEIGHT = window.innerHeight || document.documentElement!.clientHeight || document.body.clientHeight; // Leave some clearance to the bottom - let TOP_PADDING = 22; - let BOTTOM_PADDING = 22; + const TOP_PADDING = 22; + const BOTTOM_PADDING = 22; - let fitsAbove = (absoluteAboveTop >= TOP_PADDING), + const fitsAbove = (absoluteAboveTop >= TOP_PADDING), fitsBelow = (absoluteBelowTop + height <= INNER_HEIGHT - BOTTOM_PADDING); if (absoluteAboveLeft + width + 20 > INNER_WIDTH) { - let delta = absoluteAboveLeft - (INNER_WIDTH - width - 20); + const delta = absoluteAboveLeft - (INNER_WIDTH - width - 20); absoluteAboveLeft -= delta; aboveLeft -= delta; } if (absoluteBelowLeft + width + 20 > INNER_WIDTH) { - let delta = absoluteBelowLeft - (INNER_WIDTH - width - 20); + const delta = absoluteBelowLeft - (INNER_WIDTH - width - 20); absoluteBelowLeft -= delta; belowLeft -= delta; } if (absoluteAboveLeft < 0) { - let delta = absoluteAboveLeft; + const delta = absoluteAboveLeft; absoluteAboveLeft -= delta; aboveLeft -= delta; } if (absoluteBelowLeft < 0) { - let delta = absoluteBelowLeft; + const delta = absoluteBelowLeft; absoluteBelowLeft -= delta; belowLeft -= delta; } diff --git a/src/vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight.ts b/src/vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight.ts index 5a49e8d3070..d707bbad7c5 100644 --- a/src/vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight.ts +++ b/src/vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight.ts @@ -12,7 +12,7 @@ import * as viewEvents from 'vs/editor/common/view/viewEvents'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; export class CurrentLineHighlightOverlay extends DynamicViewOverlay { - private _context: ViewContext; + private readonly _context: ViewContext; private _lineHeight: number; private _renderLineHighlight: 'none' | 'gutter' | 'line' | 'all'; private _selectionIsEmpty: boolean; diff --git a/src/vs/editor/browser/viewParts/currentLineMarginHighlight/currentLineMarginHighlight.ts b/src/vs/editor/browser/viewParts/currentLineMarginHighlight/currentLineMarginHighlight.ts index b3054d0408a..55145073655 100644 --- a/src/vs/editor/browser/viewParts/currentLineMarginHighlight/currentLineMarginHighlight.ts +++ b/src/vs/editor/browser/viewParts/currentLineMarginHighlight/currentLineMarginHighlight.ts @@ -12,7 +12,7 @@ import * as viewEvents from 'vs/editor/common/view/viewEvents'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; export class CurrentLineMarginHighlightOverlay extends DynamicViewOverlay { - private _context: ViewContext; + private readonly _context: ViewContext; private _lineHeight: number; private _renderLineHighlight: 'none' | 'gutter' | 'line' | 'all'; private _selectionIsEmpty: boolean; diff --git a/src/vs/editor/browser/viewParts/decorations/decorations.ts b/src/vs/editor/browser/viewParts/decorations/decorations.ts index 7f4500dfb73..0716cc3b4e1 100644 --- a/src/vs/editor/browser/viewParts/decorations/decorations.ts +++ b/src/vs/editor/browser/viewParts/decorations/decorations.ts @@ -13,7 +13,7 @@ import { ViewModelDecoration } from 'vs/editor/common/viewModel/viewModel'; export class DecorationsOverlay extends DynamicViewOverlay { - private _context: ViewContext; + private readonly _context: ViewContext; private _lineHeight: number; private _typicalHalfwidthCharacterWidth: number; private _renderResult: string[] | null; @@ -69,12 +69,12 @@ export class DecorationsOverlay extends DynamicViewOverlay { // --- end event handlers public prepareRender(ctx: RenderingContext): void { - let _decorations = ctx.getDecorationsInViewport(); + const _decorations = ctx.getDecorationsInViewport(); // Keep only decorations with `className` let decorations: ViewModelDecoration[] = [], decorationsLen = 0; for (let i = 0, len = _decorations.length; i < len; i++) { - let d = _decorations[i]; + const d = _decorations[i]; if (d.options.className) { decorations[decorationsLen++] = d; } @@ -101,11 +101,11 @@ export class DecorationsOverlay extends DynamicViewOverlay { return Range.compareRangesUsingStarts(a.range, b.range); }); - let visibleStartLineNumber = ctx.visibleRange.startLineNumber; - let visibleEndLineNumber = ctx.visibleRange.endLineNumber; - let output: string[] = []; + const visibleStartLineNumber = ctx.visibleRange.startLineNumber; + const visibleEndLineNumber = ctx.visibleRange.endLineNumber; + const output: string[] = []; for (let lineNumber = visibleStartLineNumber; lineNumber <= visibleEndLineNumber; lineNumber++) { - let lineIndex = lineNumber - visibleStartLineNumber; + const lineIndex = lineNumber - visibleStartLineNumber; output[lineIndex] = ''; } @@ -116,18 +116,18 @@ export class DecorationsOverlay extends DynamicViewOverlay { } private _renderWholeLineDecorations(ctx: RenderingContext, decorations: ViewModelDecoration[], output: string[]): void { - let lineHeight = String(this._lineHeight); - let visibleStartLineNumber = ctx.visibleRange.startLineNumber; - let visibleEndLineNumber = ctx.visibleRange.endLineNumber; + const lineHeight = String(this._lineHeight); + const visibleStartLineNumber = ctx.visibleRange.startLineNumber; + const visibleEndLineNumber = ctx.visibleRange.endLineNumber; for (let i = 0, lenI = decorations.length; i < lenI; i++) { - let d = decorations[i]; + const d = decorations[i]; if (!d.options.isWholeLine) { continue; } - let decorationOutput = ( + const decorationOutput = ( '
' ); - let startLineNumber = Math.max(d.range.startLineNumber, visibleStartLineNumber); - let endLineNumber = Math.min(d.range.endLineNumber, visibleEndLineNumber); + const startLineNumber = Math.max(d.range.startLineNumber, visibleStartLineNumber); + const endLineNumber = Math.min(d.range.endLineNumber, visibleEndLineNumber); for (let j = startLineNumber; j <= endLineNumber; j++) { - let lineIndex = j - visibleStartLineNumber; + const lineIndex = j - visibleStartLineNumber; output[lineIndex] += decorationOutput; } } @@ -189,13 +189,13 @@ export class DecorationsOverlay extends DynamicViewOverlay { } private _renderNormalDecoration(ctx: RenderingContext, range: Range, className: string, showIfCollapsed: boolean, lineHeight: string, visibleStartLineNumber: number, output: string[]): void { - let linesVisibleRanges = ctx.linesVisibleRangesForRange(range, /*TODO@Alex*/className === 'findMatch'); + const linesVisibleRanges = ctx.linesVisibleRangesForRange(range, /*TODO@Alex*/className === 'findMatch'); if (!linesVisibleRanges) { return; } for (let j = 0, lenJ = linesVisibleRanges.length; j < lenJ; j++) { - let lineVisibleRanges = linesVisibleRanges[j]; + const lineVisibleRanges = linesVisibleRanges[j]; const lineIndex = lineVisibleRanges.lineNumber - visibleStartLineNumber; if (showIfCollapsed && lineVisibleRanges.ranges.length === 1) { @@ -228,7 +228,7 @@ export class DecorationsOverlay extends DynamicViewOverlay { if (!this._renderResult) { return ''; } - let lineIndex = lineNumber - startLineNumber; + const lineIndex = lineNumber - startLineNumber; if (lineIndex < 0 || lineIndex >= this._renderResult.length) { return ''; } diff --git a/src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts b/src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts index 8ead82c249a..d9b8c4dfd8f 100644 --- a/src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts +++ b/src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts @@ -17,8 +17,8 @@ import { getThemeTypeSelector } from 'vs/platform/theme/common/themeService'; export class EditorScrollbar extends ViewPart { - private scrollbar: SmoothScrollableElement; - private scrollbarDomNode: FastDomNode; + private readonly scrollbar: SmoothScrollableElement; + private readonly scrollbarDomNode: FastDomNode; constructor( context: ViewContext, @@ -31,7 +31,7 @@ export class EditorScrollbar extends ViewPart { const editor = this._context.configuration.editor; const configScrollbarOpts = editor.viewInfo.scrollbar; - let scrollbarOptions: ScrollableElementCreationOptions = { + const scrollbarOptions: ScrollableElementCreationOptions = { listenOnDomNode: viewDomNode.domNode, className: 'editor-scrollable' + ' ' + getThemeTypeSelector(context.theme.type), useShadows: false, @@ -62,11 +62,11 @@ export class EditorScrollbar extends ViewPart { // the browser will try desperately to reveal that dom node, unexpectedly // changing the .scrollTop of this.linesContent - let onBrowserDesperateReveal = (domNode: HTMLElement, lookAtScrollTop: boolean, lookAtScrollLeft: boolean) => { - let newScrollPosition: INewScrollPosition = {}; + const onBrowserDesperateReveal = (domNode: HTMLElement, lookAtScrollTop: boolean, lookAtScrollLeft: boolean) => { + const newScrollPosition: INewScrollPosition = {}; if (lookAtScrollTop) { - let deltaTop = domNode.scrollTop; + const deltaTop = domNode.scrollTop; if (deltaTop) { newScrollPosition.scrollTop = this._context.viewLayout.getCurrentScrollTop() + deltaTop; domNode.scrollTop = 0; @@ -74,7 +74,7 @@ export class EditorScrollbar extends ViewPart { } if (lookAtScrollLeft) { - let deltaLeft = domNode.scrollLeft; + const deltaLeft = domNode.scrollLeft; if (deltaLeft) { newScrollPosition.scrollLeft = this._context.viewLayout.getCurrentScrollLeft() + deltaLeft; domNode.scrollLeft = 0; @@ -126,7 +126,7 @@ export class EditorScrollbar extends ViewPart { public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { if (e.viewInfo) { const editor = this._context.configuration.editor; - let newOpts: ScrollableElementChangeOptions = { + const newOpts: ScrollableElementChangeOptions = { handleMouseWheel: editor.viewInfo.scrollbar.handleMouseWheel, mouseWheelScrollSensitivity: editor.viewInfo.scrollbar.mouseWheelScrollSensitivity, fastScrollSensitivity: editor.viewInfo.scrollbar.fastScrollSensitivity diff --git a/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.ts b/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.ts index 585dd754a7e..60c18790d0c 100644 --- a/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.ts +++ b/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.ts @@ -27,9 +27,9 @@ export abstract class DedupOverlay extends DynamicViewOverlay { protected _render(visibleStartLineNumber: number, visibleEndLineNumber: number, decorations: DecorationToRender[]): string[][] { - let output: string[][] = []; + const output: string[][] = []; for (let lineNumber = visibleStartLineNumber; lineNumber <= visibleEndLineNumber; lineNumber++) { - let lineIndex = lineNumber - visibleStartLineNumber; + const lineIndex = lineNumber - visibleStartLineNumber; output[lineIndex] = []; } @@ -50,10 +50,10 @@ export abstract class DedupOverlay extends DynamicViewOverlay { let prevClassName: string | null = null; let prevEndLineIndex = 0; for (let i = 0, len = decorations.length; i < len; i++) { - let d = decorations[i]; - let className = d.className; + const d = decorations[i]; + const className = d.className; let startLineIndex = Math.max(d.startLineNumber, visibleStartLineNumber) - visibleStartLineNumber; - let endLineIndex = Math.min(d.endLineNumber, visibleEndLineNumber) - visibleStartLineNumber; + const endLineIndex = Math.min(d.endLineNumber, visibleEndLineNumber) - visibleStartLineNumber; if (prevClassName === className) { startLineIndex = Math.max(prevEndLineIndex + 1, startLineIndex); @@ -74,7 +74,7 @@ export abstract class DedupOverlay extends DynamicViewOverlay { export class GlyphMarginOverlay extends DedupOverlay { - private _context: ViewContext; + private readonly _context: ViewContext; private _lineHeight: number; private _glyphMargin: boolean; private _glyphMarginLeft: number; @@ -138,11 +138,11 @@ export class GlyphMarginOverlay extends DedupOverlay { // --- end event handlers protected _getDecorations(ctx: RenderingContext): DecorationToRender[] { - let decorations = ctx.getDecorationsInViewport(); + const decorations = ctx.getDecorationsInViewport(); let r: DecorationToRender[] = [], rLen = 0; for (let i = 0, len = decorations.length; i < len; i++) { - let d = decorations[i]; - let glyphMarginClassName = d.options.glyphMarginClassName; + const d = decorations[i]; + const glyphMarginClassName = d.options.glyphMarginClassName; if (glyphMarginClassName) { r[rLen++] = new DecorationToRender(d.range.startLineNumber, d.range.endLineNumber, glyphMarginClassName); } @@ -156,19 +156,19 @@ export class GlyphMarginOverlay extends DedupOverlay { return; } - let visibleStartLineNumber = ctx.visibleRange.startLineNumber; - let visibleEndLineNumber = ctx.visibleRange.endLineNumber; - let toRender = this._render(visibleStartLineNumber, visibleEndLineNumber, this._getDecorations(ctx)); + const visibleStartLineNumber = ctx.visibleRange.startLineNumber; + const visibleEndLineNumber = ctx.visibleRange.endLineNumber; + const toRender = this._render(visibleStartLineNumber, visibleEndLineNumber, this._getDecorations(ctx)); - let lineHeight = this._lineHeight.toString(); - let left = this._glyphMarginLeft.toString(); - let width = this._glyphMarginWidth.toString(); - let common = '" style="left:' + left + 'px;width:' + width + 'px' + ';height:' + lineHeight + 'px;">'; + const lineHeight = this._lineHeight.toString(); + const left = this._glyphMarginLeft.toString(); + const width = this._glyphMarginWidth.toString(); + const common = '" style="left:' + left + 'px;width:' + width + 'px' + ';height:' + lineHeight + 'px;">'; - let output: string[] = []; + const output: string[] = []; for (let lineNumber = visibleStartLineNumber; lineNumber <= visibleEndLineNumber; lineNumber++) { - let lineIndex = lineNumber - visibleStartLineNumber; - let classNames = toRender[lineIndex]; + const lineIndex = lineNumber - visibleStartLineNumber; + const classNames = toRender[lineIndex]; if (classNames.length === 0) { output[lineIndex] = ''; @@ -188,7 +188,7 @@ export class GlyphMarginOverlay extends DedupOverlay { if (!this._renderResult) { return ''; } - let lineIndex = lineNumber - startLineNumber; + const lineIndex = lineNumber - startLineNumber; if (lineIndex < 0 || lineIndex >= this._renderResult.length) { return ''; } diff --git a/src/vs/editor/browser/viewParts/indentGuides/indentGuides.ts b/src/vs/editor/browser/viewParts/indentGuides/indentGuides.ts index 7a7e613f1e7..323a8097cec 100644 --- a/src/vs/editor/browser/viewParts/indentGuides/indentGuides.ts +++ b/src/vs/editor/browser/viewParts/indentGuides/indentGuides.ts @@ -14,7 +14,7 @@ import { registerThemingParticipant } from 'vs/platform/theme/common/themeServic export class IndentGuidesOverlay extends DynamicViewOverlay { - private _context: ViewContext; + private readonly _context: ViewContext; private _primaryLineNumber: number; private _lineHeight: number; private _spaceWidth: number; @@ -120,17 +120,17 @@ export class IndentGuidesOverlay extends DynamicViewOverlay { activeIndentLevel = activeIndentInfo.indent; } - let output: string[] = []; + const output: string[] = []; for (let lineNumber = visibleStartLineNumber; lineNumber <= visibleEndLineNumber; lineNumber++) { const containsActiveIndentGuide = (activeIndentStartLineNumber <= lineNumber && lineNumber <= activeIndentEndLineNumber); const lineIndex = lineNumber - visibleStartLineNumber; const indent = indents[lineIndex]; let result = ''; - let leftMostVisiblePosition = ctx.visibleRangeForPosition(new Position(lineNumber, 1)); + const leftMostVisiblePosition = ctx.visibleRangeForPosition(new Position(lineNumber, 1)); let left = leftMostVisiblePosition ? leftMostVisiblePosition.left : 0; for (let i = 1; i <= indent; i++) { - let className = (containsActiveIndentGuide && i === activeIndentLevel ? 'cigra' : 'cigr'); + const className = (containsActiveIndentGuide && i === activeIndentLevel ? 'cigra' : 'cigr'); result += `
`; left += indentWidth; if (left > scrollWidth) { @@ -147,7 +147,7 @@ export class IndentGuidesOverlay extends DynamicViewOverlay { if (!this._renderResult) { return ''; } - let lineIndex = lineNumber - startLineNumber; + const lineIndex = lineNumber - startLineNumber; if (lineIndex < 0 || lineIndex >= this._renderResult.length) { return ''; } diff --git a/src/vs/editor/browser/viewParts/lineNumbers/lineNumbers.ts b/src/vs/editor/browser/viewParts/lineNumbers/lineNumbers.ts index 7bd48eed211..8ccd25f7453 100644 --- a/src/vs/editor/browser/viewParts/lineNumbers/lineNumbers.ts +++ b/src/vs/editor/browser/viewParts/lineNumbers/lineNumbers.ts @@ -18,7 +18,7 @@ export class LineNumbersOverlay extends DynamicViewOverlay { public static readonly CLASS_NAME = 'line-numbers'; - private _context: ViewContext; + private readonly _context: ViewContext; private _lineHeight: number; private _renderLineNumbers: RenderLineNumbersType; @@ -97,7 +97,7 @@ export class LineNumbersOverlay extends DynamicViewOverlay { if (modelPosition.column !== 1) { return ''; } - let modelLineNumber = modelPosition.lineNumber; + const modelLineNumber = modelPosition.lineNumber; if (!this._renderFinalNewline) { const lineCount = this._context.model.getLineCount(); @@ -113,7 +113,7 @@ export class LineNumbersOverlay extends DynamicViewOverlay { } if (this._renderLineNumbers === RenderLineNumbersType.Relative) { - let diff = Math.abs(this._lastCursorModelPosition.lineNumber - modelLineNumber); + const diff = Math.abs(this._lastCursorModelPosition.lineNumber - modelLineNumber); if (diff === 0) { return '' + modelLineNumber + ''; } @@ -139,16 +139,16 @@ export class LineNumbersOverlay extends DynamicViewOverlay { return; } - let lineHeightClassName = (platform.isLinux ? (this._lineHeight % 2 === 0 ? ' lh-even' : ' lh-odd') : ''); - let visibleStartLineNumber = ctx.visibleRange.startLineNumber; - let visibleEndLineNumber = ctx.visibleRange.endLineNumber; - let common = '
'; + const lineHeightClassName = (platform.isLinux ? (this._lineHeight % 2 === 0 ? ' lh-even' : ' lh-odd') : ''); + const visibleStartLineNumber = ctx.visibleRange.startLineNumber; + const visibleEndLineNumber = ctx.visibleRange.endLineNumber; + const common = '
'; - let output: string[] = []; + const output: string[] = []; for (let lineNumber = visibleStartLineNumber; lineNumber <= visibleEndLineNumber; lineNumber++) { - let lineIndex = lineNumber - visibleStartLineNumber; + const lineIndex = lineNumber - visibleStartLineNumber; - let renderLineNumber = this._getLineRenderLineNumber(lineNumber); + const renderLineNumber = this._getLineRenderLineNumber(lineNumber); if (renderLineNumber) { output[lineIndex] = ( @@ -168,7 +168,7 @@ export class LineNumbersOverlay extends DynamicViewOverlay { if (!this._renderResult) { return ''; } - let lineIndex = lineNumber - startLineNumber; + const lineIndex = lineNumber - startLineNumber; if (lineIndex < 0 || lineIndex >= this._renderResult.length) { return ''; } diff --git a/src/vs/editor/browser/viewParts/lines/rangeUtil.ts b/src/vs/editor/browser/viewParts/lines/rangeUtil.ts index 218418ad05a..1561687a35e 100644 --- a/src/vs/editor/browser/viewParts/lines/rangeUtil.ts +++ b/src/vs/editor/browser/viewParts/lines/rangeUtil.ts @@ -49,7 +49,7 @@ export class RangeUtil { } private static _readClientRects(startElement: Node, startOffset: number, endElement: Node, endOffset: number, endNode: HTMLElement): ClientRectList | DOMRectList | null { - let range = this._createRange(); + const range = this._createRange(); try { range.setStart(startElement, startOffset); range.setEnd(endElement, endOffset); @@ -102,7 +102,7 @@ export class RangeUtil { // We go through FloatHorizontalRange because it has been observed in bi-di text // that the clientRects are not coming in sorted from the browser - let result: FloatHorizontalRange[] = []; + const result: FloatHorizontalRange[] = []; for (let i = 0, len = clientRects.length; i < len; i++) { const clientRect = clientRects[i]; result[i] = new FloatHorizontalRange(Math.max(0, clientRect.left - clientRectDeltaLeft), clientRect.width); @@ -113,8 +113,8 @@ export class RangeUtil { public static readHorizontalRanges(domNode: HTMLElement, startChildIndex: number, startOffset: number, endChildIndex: number, endOffset: number, clientRectDeltaLeft: number, endNode: HTMLElement): HorizontalRange[] | null { // Panic check - let min = 0; - let max = domNode.children.length - 1; + const min = 0; + const max = domNode.children.length - 1; if (min > max) { return null; } @@ -152,7 +152,7 @@ export class RangeUtil { startOffset = Math.min(startElement.textContent!.length, Math.max(0, startOffset)); endOffset = Math.min(endElement.textContent!.length, Math.max(0, endOffset)); - let clientRects = this._readClientRects(startElement, startOffset, endElement, endOffset, endNode); + const clientRects = this._readClientRects(startElement, startOffset, endElement, endOffset, endNode); return this._createHorizontalRangesFromClientRects(clientRects, clientRectDeltaLeft); } } diff --git a/src/vs/editor/browser/viewParts/lines/viewLine.ts b/src/vs/editor/browser/viewParts/lines/viewLine.ts index 67a3cfc6392..e2397249709 100644 --- a/src/vs/editor/browser/viewParts/lines/viewLine.ts +++ b/src/vs/editor/browser/viewParts/lines/viewLine.ts @@ -180,8 +180,8 @@ export class ViewLine implements IVisibleLine { continue; } - let startColumn = (selection.startLineNumber === lineNumber ? selection.startColumn : lineData.minColumn); - let endColumn = (selection.endLineNumber === lineNumber ? selection.endColumn : lineData.maxColumn); + const startColumn = (selection.startLineNumber === lineNumber ? selection.startColumn : lineData.minColumn); + const endColumn = (selection.endLineNumber === lineNumber ? selection.endColumn : lineData.maxColumn); if (startColumn < endColumn) { actualInlineDecorations.push(new LineDecoration(startColumn, endColumn, 'inline-selected-text', InlineDecorationType.Regular)); @@ -189,7 +189,7 @@ export class ViewLine implements IVisibleLine { } } - let renderLineInput = new RenderLineInput( + const renderLineInput = new RenderLineInput( options.useMonospaceOptimizations, options.canUseHalfwidthRightwardsArrow, lineData.content, @@ -369,7 +369,7 @@ class FastRenderedViewLine implements IRenderedViewLine { } public getColumnOfNodeOffset(lineNumber: number, spanNode: HTMLElement, offset: number): number { - let spanNodeTextContentLength = spanNode.textContent!.length; + const spanNodeTextContentLength = spanNode.textContent!.length; let spanIndex = -1; while (spanNode) { @@ -377,7 +377,7 @@ class FastRenderedViewLine implements IRenderedViewLine { spanIndex++; } - let charOffset = this._characterMapping.partDataToCharOffset(spanIndex, spanNodeTextContentLength, offset); + const charOffset = this._characterMapping.partDataToCharOffset(spanIndex, spanNodeTextContentLength, offset); return charOffset + 1; } } @@ -398,7 +398,7 @@ class RenderedViewLine implements IRenderedViewLine { /** * This is a map that is used only when the line is guaranteed to have no RTL text. */ - private _pixelOffsetCache: Int32Array | null; + private readonly _pixelOffsetCache: Int32Array | null; constructor(domNode: FastDomNode, renderLineInput: RenderLineInput, characterMapping: CharacterMapping, containsRTL: boolean, containsForeignElements: ForeignElementType) { this.domNode = domNode; @@ -446,12 +446,12 @@ class RenderedViewLine implements IRenderedViewLine { public getVisibleRangesForRange(startColumn: number, endColumn: number, context: DomReadingContext): HorizontalRange[] | null { if (this._pixelOffsetCache !== null) { // the text is LTR - let startOffset = this._readPixelOffset(startColumn, context); + const startOffset = this._readPixelOffset(startColumn, context); if (startOffset === -1) { return null; } - let endOffset = this._readPixelOffset(endColumn, context); + const endOffset = this._readPixelOffset(endColumn, context); if (endOffset === -1) { return null; } @@ -464,7 +464,7 @@ class RenderedViewLine implements IRenderedViewLine { protected _readVisibleRangesForRange(startColumn: number, endColumn: number, context: DomReadingContext): HorizontalRange[] | null { if (startColumn === endColumn) { - let pixelOffset = this._readPixelOffset(startColumn, context); + const pixelOffset = this._readPixelOffset(startColumn, context); if (pixelOffset === -1) { return null; } else { @@ -495,12 +495,12 @@ class RenderedViewLine implements IRenderedViewLine { if (this._pixelOffsetCache !== null) { // the text is LTR - let cachedPixelOffset = this._pixelOffsetCache[column]; + const cachedPixelOffset = this._pixelOffsetCache[column]; if (cachedPixelOffset !== -1) { return cachedPixelOffset; } - let result = this._actualReadPixelOffset(column, context); + const result = this._actualReadPixelOffset(column, context); this._pixelOffsetCache[column] = result; return result; } @@ -511,7 +511,7 @@ class RenderedViewLine implements IRenderedViewLine { private _actualReadPixelOffset(column: number, context: DomReadingContext): number { if (this._characterMapping.length === 0) { // This line has no content - let r = RangeUtil.readHorizontalRanges(this._getReadingTarget(), 0, 0, 0, 0, context.clientRectDeltaLeft, context.endNode); + const r = RangeUtil.readHorizontalRanges(this._getReadingTarget(), 0, 0, 0, 0, context.clientRectDeltaLeft, context.endNode); if (!r || r.length === 0) { return -1; } @@ -523,11 +523,11 @@ class RenderedViewLine implements IRenderedViewLine { return this.getWidth(); } - let partData = this._characterMapping.charOffsetToPartData(column - 1); - let partIndex = CharacterMapping.getPartIndex(partData); - let charOffsetInPart = CharacterMapping.getCharIndex(partData); + const partData = this._characterMapping.charOffsetToPartData(column - 1); + const partIndex = CharacterMapping.getPartIndex(partData); + const charOffsetInPart = CharacterMapping.getCharIndex(partData); - let r = RangeUtil.readHorizontalRanges(this._getReadingTarget(), partIndex, charOffsetInPart, partIndex, charOffsetInPart, context.clientRectDeltaLeft, context.endNode); + const r = RangeUtil.readHorizontalRanges(this._getReadingTarget(), partIndex, charOffsetInPart, partIndex, charOffsetInPart, context.clientRectDeltaLeft, context.endNode); if (!r || r.length === 0) { return -1; } @@ -542,13 +542,13 @@ class RenderedViewLine implements IRenderedViewLine { return [new HorizontalRange(0, this.getWidth())]; } - let startPartData = this._characterMapping.charOffsetToPartData(startColumn - 1); - let startPartIndex = CharacterMapping.getPartIndex(startPartData); - let startCharOffsetInPart = CharacterMapping.getCharIndex(startPartData); + const startPartData = this._characterMapping.charOffsetToPartData(startColumn - 1); + const startPartIndex = CharacterMapping.getPartIndex(startPartData); + const startCharOffsetInPart = CharacterMapping.getCharIndex(startPartData); - let endPartData = this._characterMapping.charOffsetToPartData(endColumn - 1); - let endPartIndex = CharacterMapping.getPartIndex(endPartData); - let endCharOffsetInPart = CharacterMapping.getCharIndex(endPartData); + const endPartData = this._characterMapping.charOffsetToPartData(endColumn - 1); + const endPartIndex = CharacterMapping.getPartIndex(endPartData); + const endCharOffsetInPart = CharacterMapping.getCharIndex(endPartData); return RangeUtil.readHorizontalRanges(this._getReadingTarget(), startPartIndex, startCharOffsetInPart, endPartIndex, endCharOffsetInPart, context.clientRectDeltaLeft, context.endNode); } @@ -557,7 +557,7 @@ class RenderedViewLine implements IRenderedViewLine { * Returns the column for the text found at a specific offset inside a rendered dom node */ public getColumnOfNodeOffset(lineNumber: number, spanNode: HTMLElement, offset: number): number { - let spanNodeTextContentLength = spanNode.textContent!.length; + const spanNodeTextContentLength = spanNode.textContent!.length; let spanIndex = -1; while (spanNode) { @@ -565,14 +565,14 @@ class RenderedViewLine implements IRenderedViewLine { spanIndex++; } - let charOffset = this._characterMapping.partDataToCharOffset(spanIndex, spanNodeTextContentLength, offset); + const charOffset = this._characterMapping.partDataToCharOffset(spanIndex, spanNodeTextContentLength, offset); return charOffset + 1; } } class WebKitRenderedViewLine extends RenderedViewLine { protected _readVisibleRangesForRange(startColumn: number, endColumn: number, context: DomReadingContext): HorizontalRange[] | null { - let output = super._readVisibleRangesForRange(startColumn, endColumn, context); + const output = super._readVisibleRangesForRange(startColumn, endColumn, context); if (!output || output.length === 0 || startColumn === endColumn || (startColumn === 1 && endColumn === this._characterMapping.length)) { return output; @@ -583,9 +583,9 @@ class WebKitRenderedViewLine extends RenderedViewLine { if (!this.input.containsRTL) { // This is an attempt to patch things up // Find position of last column - let endPixelOffset = this._readPixelOffset(endColumn, context); + const endPixelOffset = this._readPixelOffset(endColumn, context); if (endPixelOffset !== -1) { - let lastRange = output[output.length - 1]; + const lastRange = output[output.length - 1]; if (lastRange.left < endPixelOffset) { // Trim down the width of the last visible range to not go after the last column's position lastRange.width = endPixelOffset - lastRange.left; diff --git a/src/vs/editor/browser/viewParts/lines/viewLines.ts b/src/vs/editor/browser/viewParts/lines/viewLines.ts index b09c5536dec..ba4d49c639a 100644 --- a/src/vs/editor/browser/viewParts/lines/viewLines.ts +++ b/src/vs/editor/browser/viewParts/lines/viewLines.ts @@ -76,10 +76,10 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost, // --- width private _maxLineWidth: number; - private _asyncUpdateLineWidths: RunOnceScheduler; + private readonly _asyncUpdateLineWidths: RunOnceScheduler; private _horizontalRevealRequest: HorizontalRevealRequest | null; - private _lastRenderedData: LastRenderedData; + private readonly _lastRenderedData: LastRenderedData; constructor(context: ViewContext, linesContent: FastDomNode) { super(context); @@ -169,14 +169,14 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost, private _onOptionsMaybeChanged(): boolean { const conf = this._context.configuration; - let newViewLineOptions = new ViewLineOptions(conf, this._context.theme.type); + const newViewLineOptions = new ViewLineOptions(conf, this._context.theme.type); if (!this._viewLineOptions.equals(newViewLineOptions)) { this._viewLineOptions = newViewLineOptions; - let startLineNumber = this._visibleLines.getStartLineNumber(); - let endLineNumber = this._visibleLines.getEndLineNumber(); + const startLineNumber = this._visibleLines.getStartLineNumber(); + const endLineNumber = this._visibleLines.getEndLineNumber(); for (let lineNumber = startLineNumber; lineNumber <= endLineNumber; lineNumber++) { - let line = this._visibleLines.getVisibleLine(lineNumber); + const line = this._visibleLines.getVisibleLine(lineNumber); line.onOptionsChanged(this._viewLineOptions); } return true; @@ -185,8 +185,8 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost, return false; } public onCursorStateChanged(e: viewEvents.ViewCursorStateChangedEvent): boolean { - let rendStartLineNumber = this._visibleLines.getStartLineNumber(); - let rendEndLineNumber = this._visibleLines.getEndLineNumber(); + const rendStartLineNumber = this._visibleLines.getStartLineNumber(); + const rendEndLineNumber = this._visibleLines.getEndLineNumber(); let r = false; for (let lineNumber = rendStartLineNumber; lineNumber <= rendEndLineNumber; lineNumber++) { r = this._visibleLines.getVisibleLine(lineNumber).onSelectionChanged() || r; @@ -195,8 +195,8 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost, } public onDecorationsChanged(e: viewEvents.ViewDecorationsChangedEvent): boolean { if (true/*e.inlineDecorationsChanged*/) { - let rendStartLineNumber = this._visibleLines.getStartLineNumber(); - let rendEndLineNumber = this._visibleLines.getEndLineNumber(); + const rendStartLineNumber = this._visibleLines.getStartLineNumber(); + const rendEndLineNumber = this._visibleLines.getEndLineNumber(); for (let lineNumber = rendStartLineNumber; lineNumber <= rendEndLineNumber; lineNumber++) { this._visibleLines.getVisibleLine(lineNumber).onDecorationsChanged(); } @@ -204,7 +204,7 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost, return true; } public onFlushed(e: viewEvents.ViewFlushedEvent): boolean { - let shouldRender = this._visibleLines.onFlushed(e); + const shouldRender = this._visibleLines.onFlushed(e); this._maxLineWidth = 0; return shouldRender; } @@ -282,12 +282,12 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost, // ----------- HELPERS FOR OTHERS public getPositionFromDOMInfo(spanNode: HTMLElement, offset: number): Position | null { - let viewLineDomNode = this._getViewLineDomNode(spanNode); + const viewLineDomNode = this._getViewLineDomNode(spanNode); if (viewLineDomNode === null) { // Couldn't find view line node return null; } - let lineNumber = this._getLineNumberFor(viewLineDomNode); + const lineNumber = this._getLineNumberFor(viewLineDomNode); if (lineNumber === -1) { // Couldn't find view line node @@ -304,15 +304,15 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost, return new Position(lineNumber, 1); } - let rendStartLineNumber = this._visibleLines.getStartLineNumber(); - let rendEndLineNumber = this._visibleLines.getEndLineNumber(); + const rendStartLineNumber = this._visibleLines.getStartLineNumber(); + const rendEndLineNumber = this._visibleLines.getEndLineNumber(); if (lineNumber < rendStartLineNumber || lineNumber > rendEndLineNumber) { // Couldn't find line return null; } let column = this._visibleLines.getVisibleLine(lineNumber).getColumnOfNodeOffset(lineNumber, spanNode, offset); - let minColumn = this._context.model.getLineMinColumn(lineNumber); + const minColumn = this._context.model.getLineMinColumn(lineNumber); if (column < minColumn) { column = minColumn; } @@ -333,10 +333,10 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost, * @returns the line number of this view line dom node. */ private _getLineNumberFor(domNode: HTMLElement): number { - let startLineNumber = this._visibleLines.getStartLineNumber(); - let endLineNumber = this._visibleLines.getEndLineNumber(); + const startLineNumber = this._visibleLines.getStartLineNumber(); + const endLineNumber = this._visibleLines.getEndLineNumber(); for (let lineNumber = startLineNumber; lineNumber <= endLineNumber; lineNumber++) { - let line = this._visibleLines.getVisibleLine(lineNumber); + const line = this._visibleLines.getVisibleLine(lineNumber); if (domNode === line.getDomNode()) { return lineNumber; } @@ -345,8 +345,8 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost, } public getLineWidth(lineNumber: number): number { - let rendStartLineNumber = this._visibleLines.getStartLineNumber(); - let rendEndLineNumber = this._visibleLines.getEndLineNumber(); + const rendStartLineNumber = this._visibleLines.getStartLineNumber(); + const rendEndLineNumber = this._visibleLines.getEndLineNumber(); if (lineNumber < rendStartLineNumber || lineNumber > rendEndLineNumber) { // Couldn't find line return -1; @@ -362,38 +362,38 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost, return null; } - let originalEndLineNumber = _range.endLineNumber; + const originalEndLineNumber = _range.endLineNumber; const range = Range.intersectRanges(_range, this._lastRenderedData.getCurrentVisibleRange()); if (!range) { return null; } let visibleRanges: LineVisibleRanges[] = [], visibleRangesLen = 0; - let domReadingContext = new DomReadingContext(this.domNode.domNode, this._textRangeRestingSpot); + const domReadingContext = new DomReadingContext(this.domNode.domNode, this._textRangeRestingSpot); let nextLineModelLineNumber: number = 0; if (includeNewLines) { nextLineModelLineNumber = this._context.model.coordinatesConverter.convertViewPositionToModelPosition(new Position(range.startLineNumber, 1)).lineNumber; } - let rendStartLineNumber = this._visibleLines.getStartLineNumber(); - let rendEndLineNumber = this._visibleLines.getEndLineNumber(); + const rendStartLineNumber = this._visibleLines.getStartLineNumber(); + const rendEndLineNumber = this._visibleLines.getEndLineNumber(); for (let lineNumber = range.startLineNumber; lineNumber <= range.endLineNumber; lineNumber++) { if (lineNumber < rendStartLineNumber || lineNumber > rendEndLineNumber) { continue; } - let startColumn = lineNumber === range.startLineNumber ? range.startColumn : 1; - let endColumn = lineNumber === range.endLineNumber ? range.endColumn : this._context.model.getLineMaxColumn(lineNumber); - let visibleRangesForLine = this._visibleLines.getVisibleLine(lineNumber).getVisibleRangesForRange(startColumn, endColumn, domReadingContext); + const startColumn = lineNumber === range.startLineNumber ? range.startColumn : 1; + const endColumn = lineNumber === range.endLineNumber ? range.endColumn : this._context.model.getLineMaxColumn(lineNumber); + const visibleRangesForLine = this._visibleLines.getVisibleLine(lineNumber).getVisibleRangesForRange(startColumn, endColumn, domReadingContext); if (!visibleRangesForLine || visibleRangesForLine.length === 0) { continue; } if (includeNewLines && lineNumber < originalEndLineNumber) { - let currentLineModelLineNumber = nextLineModelLineNumber; + const currentLineModelLineNumber = nextLineModelLineNumber; nextLineModelLineNumber = this._context.model.coordinatesConverter.convertViewPositionToModelPosition(new Position(lineNumber + 1, 1)).lineNumber; if (currentLineModelLineNumber !== nextLineModelLineNumber) { @@ -425,19 +425,19 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost, } let result: HorizontalRange[] = []; - let domReadingContext = new DomReadingContext(this.domNode.domNode, this._textRangeRestingSpot); + const domReadingContext = new DomReadingContext(this.domNode.domNode, this._textRangeRestingSpot); - let rendStartLineNumber = this._visibleLines.getStartLineNumber(); - let rendEndLineNumber = this._visibleLines.getEndLineNumber(); + const rendStartLineNumber = this._visibleLines.getStartLineNumber(); + const rendEndLineNumber = this._visibleLines.getEndLineNumber(); for (let lineNumber = range.startLineNumber; lineNumber <= range.endLineNumber; lineNumber++) { if (lineNumber < rendStartLineNumber || lineNumber > rendEndLineNumber) { continue; } - let startColumn = lineNumber === range.startLineNumber ? range.startColumn : 1; - let endColumn = lineNumber === range.endLineNumber ? range.endColumn : this._context.model.getLineMaxColumn(lineNumber); - let visibleRangesForLine = this._visibleLines.getVisibleLine(lineNumber).getVisibleRangesForRange(startColumn, endColumn, domReadingContext); + const startColumn = lineNumber === range.startLineNumber ? range.startColumn : 1; + const endColumn = lineNumber === range.endLineNumber ? range.endColumn : this._context.model.getLineMaxColumn(lineNumber); + const visibleRangesForLine = this._visibleLines.getVisibleLine(lineNumber).getVisibleRangesForRange(startColumn, endColumn, domReadingContext); if (!visibleRangesForLine || visibleRangesForLine.length === 0) { continue; @@ -542,9 +542,9 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost, this.onDidRender(); // compute new scroll position - let newScrollLeft = this._computeScrollLeftToRevealRange(revealLineNumber, revealStartColumn, revealEndColumn); + const newScrollLeft = this._computeScrollLeftToRevealRange(revealLineNumber, revealStartColumn, revealEndColumn); - let isViewportWrapping = this._isViewportWrapping; + const isViewportWrapping = this._isViewportWrapping; if (!isViewportWrapping) { // ensure `scrollWidth` is large enough this._ensureMaxLineWidth(newScrollLeft.maxHorizontalOffset); @@ -579,7 +579,7 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost, // --- width private _ensureMaxLineWidth(lineWidth: number): void { - let iLineWidth = Math.ceil(lineWidth); + const iLineWidth = Math.ceil(lineWidth); if (this._maxLineWidth < iLineWidth) { this._maxLineWidth = iLineWidth; this._context.viewLayout.onMaxLineWidthChanged(this._maxLineWidth); @@ -587,9 +587,9 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost, } private _computeScrollTopToRevealRange(viewport: Viewport, range: Range, verticalType: viewEvents.VerticalRevealType): number { - let viewportStartY = viewport.top; - let viewportHeight = viewport.height; - let viewportEndY = viewportStartY + viewportHeight; + const viewportStartY = viewport.top; + const viewportHeight = viewport.height; + const viewportEndY = viewportStartY + viewportHeight; let boxStartY: number; let boxEndY: number; @@ -609,7 +609,7 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost, newScrollTop = viewportStartY; } else { // Box is outside the viewport... center it - let boxMiddleY = (boxStartY + boxEndY) / 2; + const boxMiddleY = (boxStartY + boxEndY) / 2; newScrollTop = Math.max(0, boxMiddleY - viewportHeight / 2); } } else { @@ -623,11 +623,11 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost, let maxHorizontalOffset = 0; - let viewport = this._context.viewLayout.getCurrentViewport(); - let viewportStartX = viewport.left; - let viewportEndX = viewportStartX + viewport.width; + const viewport = this._context.viewLayout.getCurrentViewport(); + const viewportStartX = viewport.left; + const viewportEndX = viewportStartX + viewport.width; - let visibleRanges = this.visibleRangesForRange2(new Range(lineNumber, startColumn, lineNumber, endColumn)); + const visibleRanges = this.visibleRangesForRange2(new Range(lineNumber, startColumn, lineNumber, endColumn)); let boxStartX = Number.MAX_VALUE; let boxEndX = 0; @@ -653,7 +653,7 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost, boxStartX = Math.max(0, boxStartX - ViewLines.HORIZONTAL_EXTRA_PX); boxEndX += this._revealHorizontalRightPadding; - let newScrollLeft = this._computeMinimumScrolling(viewportStartX, viewportEndX, boxStartX, boxEndX); + const newScrollLeft = this._computeMinimumScrolling(viewportStartX, viewportEndX, boxStartX, boxEndX); return { scrollLeft: newScrollLeft, maxHorizontalOffset: maxHorizontalOffset @@ -668,8 +668,8 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost, revealAtStart = !!revealAtStart; revealAtEnd = !!revealAtEnd; - let viewportLength = viewportEnd - viewportStart; - let boxLength = boxEnd - boxStart; + const viewportLength = viewportEnd - viewportStart; + const boxLength = boxEnd - boxStart; if (boxLength < viewportLength) { // The box would fit in the viewport diff --git a/src/vs/editor/browser/viewParts/linesDecorations/linesDecorations.ts b/src/vs/editor/browser/viewParts/linesDecorations/linesDecorations.ts index 5a0937df3ba..fe764290bad 100644 --- a/src/vs/editor/browser/viewParts/linesDecorations/linesDecorations.ts +++ b/src/vs/editor/browser/viewParts/linesDecorations/linesDecorations.ts @@ -11,7 +11,7 @@ import * as viewEvents from 'vs/editor/common/view/viewEvents'; export class LinesDecorationsOverlay extends DedupOverlay { - private _context: ViewContext; + private readonly _context: ViewContext; private _decorationsLeft: number; private _decorationsWidth: number; @@ -66,11 +66,11 @@ export class LinesDecorationsOverlay extends DedupOverlay { // --- end event handlers protected _getDecorations(ctx: RenderingContext): DecorationToRender[] { - let decorations = ctx.getDecorationsInViewport(); + const decorations = ctx.getDecorationsInViewport(); let r: DecorationToRender[] = [], rLen = 0; for (let i = 0, len = decorations.length; i < len; i++) { - let d = decorations[i]; - let linesDecorationsClassName = d.options.linesDecorationsClassName; + const d = decorations[i]; + const linesDecorationsClassName = d.options.linesDecorationsClassName; if (linesDecorationsClassName) { r[rLen++] = new DecorationToRender(d.range.startLineNumber, d.range.endLineNumber, linesDecorationsClassName); } @@ -79,18 +79,18 @@ export class LinesDecorationsOverlay extends DedupOverlay { } public prepareRender(ctx: RenderingContext): void { - let visibleStartLineNumber = ctx.visibleRange.startLineNumber; - let visibleEndLineNumber = ctx.visibleRange.endLineNumber; - let toRender = this._render(visibleStartLineNumber, visibleEndLineNumber, this._getDecorations(ctx)); + const visibleStartLineNumber = ctx.visibleRange.startLineNumber; + const visibleEndLineNumber = ctx.visibleRange.endLineNumber; + const toRender = this._render(visibleStartLineNumber, visibleEndLineNumber, this._getDecorations(ctx)); - let left = this._decorationsLeft.toString(); - let width = this._decorationsWidth.toString(); - let common = '" style="left:' + left + 'px;width:' + width + 'px;">
'; + const left = this._decorationsLeft.toString(); + const width = this._decorationsWidth.toString(); + const common = '" style="left:' + left + 'px;width:' + width + 'px;">
'; - let output: string[] = []; + const output: string[] = []; for (let lineNumber = visibleStartLineNumber; lineNumber <= visibleEndLineNumber; lineNumber++) { - let lineIndex = lineNumber - visibleStartLineNumber; - let classNames = toRender[lineIndex]; + const lineIndex = lineNumber - visibleStartLineNumber; + const classNames = toRender[lineIndex]; let lineOutput = ''; for (let i = 0, len = classNames.length; i < len; i++) { lineOutput += '
'; diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts index 789924de50f..25970859495 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimap.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts @@ -200,7 +200,7 @@ class MinimapLayout { * Compute a desired `scrollPosition` such that the slider moves by `delta`. */ public getDesiredScrollTopFromDelta(delta: number): number { - let desiredSliderPosition = this.sliderTop + delta; + const desiredSliderPosition = this.sliderTop + delta; return Math.round(desiredSliderPosition / this._computedSliderRatio); } @@ -350,7 +350,7 @@ class RenderData { } _get(): { imageData: ImageData; rendLineNumberStart: number; lines: MinimapLine[]; } { - let tmp = this._renderedLines._get(); + const tmp = this._renderedLines._get(); return { imageData: this._imageData, rendLineNumberStart: tmp.rendLineNumberStart, @@ -396,7 +396,7 @@ class MinimapBuffers { public getBuffer(): ImageData { // rotate buffers this._lastUsedBuffer = 1 - this._lastUsedBuffer; - let result = this._buffers[this._lastUsedBuffer]; + const result = this._buffers[this._lastUsedBuffer]; // fill with background color result.data.set(this._backgroundFillData); @@ -409,7 +409,7 @@ class MinimapBuffers { const backgroundG = background.g; const backgroundB = background.b; - let result = new Uint8ClampedArray(WIDTH * HEIGHT * 4); + const result = new Uint8ClampedArray(WIDTH * HEIGHT * 4); let offset = 0; for (let i = 0; i < HEIGHT; i++) { for (let j = 0; j < WIDTH; j++) { @@ -584,7 +584,7 @@ export class Minimap extends ViewPart { } private _onOptionsMaybeChanged(): boolean { - let opts = new MinimapOptions(this._context.configuration); + const opts = new MinimapOptions(this._context.configuration); if (this._options.equals(opts)) { return false; } @@ -721,7 +721,7 @@ export class Minimap extends ViewPart { // Render the rest of lines let dy = 0; - let renderedLines: MinimapLine[] = []; + const renderedLines: MinimapLine[] = []; for (let lineIndex = 0, lineCount = endLineNumber - startLineNumber + 1; lineIndex < lineCount; lineIndex++) { if (needed[lineIndex]) { Minimap._renderLine( @@ -764,7 +764,7 @@ export class Minimap extends ViewPart { lastRenderData: RenderData | null, ): [number, number, boolean[]] { - let needed: boolean[] = []; + const needed: boolean[] = []; if (!lastRenderData) { for (let i = 0, len = endLineNumber - startLineNumber + 1; i < len; i++) { needed[i] = true; @@ -801,10 +801,10 @@ export class Minimap extends ViewPart { continue; } - let sourceStart = source_dy * WIDTH * 4; - let sourceEnd = (source_dy + minimapLineHeight) * WIDTH * 4; - let destStart = dest_dy * WIDTH * 4; - let destEnd = (dest_dy + minimapLineHeight) * WIDTH * 4; + const sourceStart = source_dy * WIDTH * 4; + const sourceEnd = (source_dy + minimapLineHeight) * WIDTH * 4; + const destStart = dest_dy * WIDTH * 4; + const destEnd = (dest_dy + minimapLineHeight) * WIDTH * 4; if (copySourceEnd === sourceStart && copyDestEnd === destStart) { // contiguous zone => extend copy request @@ -881,7 +881,7 @@ export class Minimap extends ViewPart { const charCode = content.charCodeAt(charIndex); if (charCode === CharCode.Tab) { - let insertSpacesCount = tabSize - (charIndex + tabsCharDelta) % tabSize; + const insertSpacesCount = tabSize - (charIndex + tabsCharDelta) % tabSize; tabsCharDelta += insertSpacesCount - 1; // No need to render anything since tab is invisible dx += insertSpacesCount * charWidth; @@ -890,7 +890,7 @@ export class Minimap extends ViewPart { dx += charWidth; } else { // Render twice for a full width character - let count = strings.isFullWidthCharacter(charCode) ? 2 : 1; + const count = strings.isFullWidthCharacter(charCode) ? 2 : 1; for (let i = 0; i < count; i++) { if (renderMinimap === RenderMinimap.Large) { diff --git a/src/vs/editor/browser/viewParts/overlayWidgets/overlayWidgets.ts b/src/vs/editor/browser/viewParts/overlayWidgets/overlayWidgets.ts index 2b25eb1575b..87a35ca24f2 100644 --- a/src/vs/editor/browser/viewParts/overlayWidgets/overlayWidgets.ts +++ b/src/vs/editor/browser/viewParts/overlayWidgets/overlayWidgets.ts @@ -24,7 +24,7 @@ interface IWidgetMap { export class ViewOverlayWidgets extends ViewPart { private _widgets: IWidgetMap; - private _domNode: FastDomNode; + private readonly _domNode: FastDomNode; private _verticalScrollbarWidth: number; private _minimapWidth: number; @@ -90,7 +90,7 @@ export class ViewOverlayWidgets extends ViewPart { } public setWidgetPosition(widget: IOverlayWidget, preference: OverlayWidgetPositionPreference | null): boolean { - let widgetData = this._widgets[widget.getId()]; + const widgetData = this._widgets[widget.getId()]; if (widgetData.preference === preference) { return false; } @@ -102,7 +102,7 @@ export class ViewOverlayWidgets extends ViewPart { } public removeWidget(widget: IOverlayWidget): void { - let widgetId = widget.getId(); + const widgetId = widget.getId(); if (this._widgets.hasOwnProperty(widgetId)) { const widgetData = this._widgets[widgetId]; const domNode = widgetData.domNode.domNode; @@ -125,7 +125,7 @@ export class ViewOverlayWidgets extends ViewPart { domNode.setTop(0); domNode.setRight((2 * this._verticalScrollbarWidth) + this._minimapWidth); } else if (widgetData.preference === OverlayWidgetPositionPreference.BOTTOM_RIGHT_CORNER) { - let widgetHeight = domNode.domNode.clientHeight; + const widgetHeight = domNode.domNode.clientHeight; domNode.setTop((this._editorHeight - widgetHeight - 2 * this._horizontalScrollbarHeight)); domNode.setRight((2 * this._verticalScrollbarWidth) + this._minimapWidth); } else if (widgetData.preference === OverlayWidgetPositionPreference.TOP_CENTER) { @@ -141,9 +141,9 @@ export class ViewOverlayWidgets extends ViewPart { public render(ctx: RestrictedRenderingContext): void { this._domNode.setWidth(this._editorWidth); - let keys = Object.keys(this._widgets); + const keys = Object.keys(this._widgets); for (let i = 0, len = keys.length; i < len; i++) { - let widgetId = keys[i]; + const widgetId = keys[i]; this._renderWidget(this._widgets[widgetId]); } } diff --git a/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts b/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts index a8ee68ea1cc..2aaaac909bf 100644 --- a/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts +++ b/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts @@ -340,7 +340,7 @@ export class DecorationsOverviewRuler extends ViewPart { let y1 = (viewLayout.getVerticalOffsetForLineNumber(startLineNumber) * heightRatio) | 0; let y2 = ((viewLayout.getVerticalOffsetForLineNumber(endLineNumber) + lineHeight) * heightRatio) | 0; - let height = y2 - y1; + const height = y2 - y1; if (height < minDecorationHeight) { let yCenter = ((y1 + y2) / 2) | 0; if (yCenter < halfMinDecorationHeight) { diff --git a/src/vs/editor/browser/viewParts/overviewRuler/overviewRuler.ts b/src/vs/editor/browser/viewParts/overviewRuler/overviewRuler.ts index 748a3488d04..4b164f01c6b 100644 --- a/src/vs/editor/browser/viewParts/overviewRuler/overviewRuler.ts +++ b/src/vs/editor/browser/viewParts/overviewRuler/overviewRuler.ts @@ -13,9 +13,9 @@ import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler'; export class OverviewRuler extends ViewEventHandler implements IOverviewRuler { - private _context: ViewContext; - private _domNode: FastDomNode; - private _zoneManager: OverviewZoneManager; + private readonly _context: ViewContext; + private readonly _domNode: FastDomNode; + private readonly _zoneManager: OverviewZoneManager; constructor(context: ViewContext, cssClassName: string) { super(); @@ -114,10 +114,10 @@ export class OverviewRuler extends ViewEventHandler implements IOverviewRuler { const width = this._zoneManager.getCanvasWidth(); const height = this._zoneManager.getCanvasHeight(); - let colorZones = this._zoneManager.resolveColorZones(); - let id2Color = this._zoneManager.getId2Color(); + const colorZones = this._zoneManager.resolveColorZones(); + const id2Color = this._zoneManager.getId2Color(); - let ctx = this._domNode.domNode.getContext('2d')!; + const ctx = this._domNode.domNode.getContext('2d')!; ctx.clearRect(0, 0, width, height); if (colorZones.length > 0) { this._renderOneLane(ctx, colorZones, id2Color, width); diff --git a/src/vs/editor/browser/viewParts/rulers/rulers.ts b/src/vs/editor/browser/viewParts/rulers/rulers.ts index 5eca6700d6e..7d4d402d24e 100644 --- a/src/vs/editor/browser/viewParts/rulers/rulers.ts +++ b/src/vs/editor/browser/viewParts/rulers/rulers.ts @@ -15,7 +15,7 @@ import { registerThemingParticipant } from 'vs/platform/theme/common/themeServic export class Rulers extends ViewPart { public domNode: FastDomNode; - private _renderedRulers: FastDomNode[]; + private readonly _renderedRulers: FastDomNode[]; private _rulers: number[]; private _typicalHalfwidthCharacterWidth: number; @@ -68,7 +68,7 @@ export class Rulers extends ViewPart { const rulerWidth = tabSize; let addCount = desiredCount - currentCount; while (addCount > 0) { - let node = createFastDomNode(document.createElement('div')); + const node = createFastDomNode(document.createElement('div')); node.setClassName('view-ruler'); node.setWidth(rulerWidth); this.domNode.appendChild(node); @@ -80,7 +80,7 @@ export class Rulers extends ViewPart { let removeCount = currentCount - desiredCount; while (removeCount > 0) { - let node = this._renderedRulers.pop()!; + const node = this._renderedRulers.pop()!; this.domNode.removeChild(node); removeCount--; } @@ -91,7 +91,7 @@ export class Rulers extends ViewPart { this._ensureRulersCount(); for (let i = 0, len = this._rulers.length; i < len; i++) { - let node = this._renderedRulers[i]; + const node = this._renderedRulers[i]; node.setHeight(Math.min(ctx.scrollHeight, 1000000)); node.setLeft(this._rulers[i] * this._typicalHalfwidthCharacterWidth); diff --git a/src/vs/editor/browser/viewParts/scrollDecoration/scrollDecoration.ts b/src/vs/editor/browser/viewParts/scrollDecoration/scrollDecoration.ts index 6d65eea7386..2ae336437e4 100644 --- a/src/vs/editor/browser/viewParts/scrollDecoration/scrollDecoration.ts +++ b/src/vs/editor/browser/viewParts/scrollDecoration/scrollDecoration.ts @@ -14,7 +14,7 @@ import { registerThemingParticipant } from 'vs/platform/theme/common/themeServic export class ScrollDecorationViewPart extends ViewPart { - private _domNode: FastDomNode; + private readonly _domNode: FastDomNode; private _scrollTop: number; private _width: number; private _shouldShow: boolean; @@ -38,7 +38,7 @@ export class ScrollDecorationViewPart extends ViewPart { } private _updateShouldShow(): boolean { - let newShouldShow = (this._useShadows && this._scrollTop > 0); + const newShouldShow = (this._useShadows && this._scrollTop > 0); if (this._shouldShow !== newShouldShow) { this._shouldShow = newShouldShow; return true; diff --git a/src/vs/editor/browser/viewParts/selections/selections.ts b/src/vs/editor/browser/viewParts/selections/selections.ts index 99d5696fede..3ee089a3d94 100644 --- a/src/vs/editor/browser/viewParts/selections/selections.ts +++ b/src/vs/editor/browser/viewParts/selections/selections.ts @@ -73,7 +73,7 @@ export class SelectionsOverlay extends DynamicViewOverlay { private static readonly ROUNDED_PIECE_WIDTH = 10; - private _context: ViewContext; + private readonly _context: ViewContext; private _lineHeight: number; private _roundedSelection: boolean; private _typicalHalfwidthCharacterWidth: number; @@ -143,7 +143,7 @@ export class SelectionsOverlay extends DynamicViewOverlay { private _visibleRangesHaveGaps(linesVisibleRanges: LineVisibleRangesWithStyle[]): boolean { for (let i = 0, len = linesVisibleRanges.length; i < len; i++) { - let lineVisibleRanges = linesVisibleRanges[i]; + const lineVisibleRanges = linesVisibleRanges[i]; if (lineVisibleRanges.ranges.length > 1) { // There are two ranges on the same line @@ -161,7 +161,7 @@ export class SelectionsOverlay extends DynamicViewOverlay { if (previousFrame && previousFrame.length > 0 && linesVisibleRanges.length > 0) { - let topLineNumber = linesVisibleRanges[0].lineNumber; + const topLineNumber = linesVisibleRanges[0].lineNumber; if (topLineNumber === viewport.startLineNumber) { for (let i = 0; !previousFrameTop && i < previousFrame.length; i++) { if (previousFrame[i].lineNumber === topLineNumber) { @@ -170,7 +170,7 @@ export class SelectionsOverlay extends DynamicViewOverlay { } } - let bottomLineNumber = linesVisibleRanges[linesVisibleRanges.length - 1].lineNumber; + const bottomLineNumber = linesVisibleRanges[linesVisibleRanges.length - 1].lineNumber; if (bottomLineNumber === viewport.endLineNumber) { for (let i = previousFrame.length - 1; !previousFrameBottom && i >= 0; i--) { if (previousFrame[i].lineNumber === bottomLineNumber) { @@ -189,24 +189,24 @@ export class SelectionsOverlay extends DynamicViewOverlay { for (let i = 0, len = linesVisibleRanges.length; i < len; i++) { // We know for a fact that there is precisely one range on each line - let curLineRange = linesVisibleRanges[i].ranges[0]; - let curLeft = curLineRange.left; - let curRight = curLineRange.left + curLineRange.width; + const curLineRange = linesVisibleRanges[i].ranges[0]; + const curLeft = curLineRange.left; + const curRight = curLineRange.left + curLineRange.width; - let startStyle = { + const startStyle = { top: CornerStyle.EXTERN, bottom: CornerStyle.EXTERN }; - let endStyle = { + const endStyle = { top: CornerStyle.EXTERN, bottom: CornerStyle.EXTERN }; if (i > 0) { // Look above - let prevLeft = linesVisibleRanges[i - 1].ranges[0].left; - let prevRight = linesVisibleRanges[i - 1].ranges[0].left + linesVisibleRanges[i - 1].ranges[0].width; + const prevLeft = linesVisibleRanges[i - 1].ranges[0].left; + const prevRight = linesVisibleRanges[i - 1].ranges[0].left + linesVisibleRanges[i - 1].ranges[0].width; if (abs(curLeft - prevLeft) < epsilon) { startStyle.top = CornerStyle.FLAT; @@ -227,8 +227,8 @@ export class SelectionsOverlay extends DynamicViewOverlay { if (i + 1 < len) { // Look below - let nextLeft = linesVisibleRanges[i + 1].ranges[0].left; - let nextRight = linesVisibleRanges[i + 1].ranges[0].left + linesVisibleRanges[i + 1].ranges[0].width; + const nextLeft = linesVisibleRanges[i + 1].ranges[0].left; + const nextRight = linesVisibleRanges[i + 1].ranges[0].left + linesVisibleRanges[i + 1].ranges[0].width; if (abs(curLeft - nextLeft) < epsilon) { startStyle.bottom = CornerStyle.FLAT; @@ -253,9 +253,9 @@ export class SelectionsOverlay extends DynamicViewOverlay { } private _getVisibleRangesWithStyle(selection: Range, ctx: RenderingContext, previousFrame: LineVisibleRangesWithStyle[] | null): LineVisibleRangesWithStyle[] { - let _linesVisibleRanges = ctx.linesVisibleRangesForRange(selection, true) || []; - let linesVisibleRanges = _linesVisibleRanges.map(toStyled); - let visibleRangesHaveGaps = this._visibleRangesHaveGaps(linesVisibleRanges); + const _linesVisibleRanges = ctx.linesVisibleRangesForRange(selection, true) || []; + const linesVisibleRanges = _linesVisibleRanges.map(toStyled); + const visibleRangesHaveGaps = this._visibleRangesHaveGaps(linesVisibleRanges); if (!isIEWithZoomingIssuesNearRoundedBorders && !visibleRangesHaveGaps && this._roundedSelection) { this._enrichVisibleRangesWithStyle(ctx.visibleRange, linesVisibleRanges, previousFrame); @@ -282,25 +282,25 @@ export class SelectionsOverlay extends DynamicViewOverlay { } private _actualRenderOneSelection(output2: string[], visibleStartLineNumber: number, hasMultipleSelections: boolean, visibleRanges: LineVisibleRangesWithStyle[]): void { - let visibleRangesHaveStyle = (visibleRanges.length > 0 && visibleRanges[0].ranges[0].startStyle); - let fullLineHeight = (this._lineHeight).toString(); - let reducedLineHeight = (this._lineHeight - 1).toString(); + const visibleRangesHaveStyle = (visibleRanges.length > 0 && visibleRanges[0].ranges[0].startStyle); + const fullLineHeight = (this._lineHeight).toString(); + const reducedLineHeight = (this._lineHeight - 1).toString(); - let firstLineNumber = (visibleRanges.length > 0 ? visibleRanges[0].lineNumber : 0); - let lastLineNumber = (visibleRanges.length > 0 ? visibleRanges[visibleRanges.length - 1].lineNumber : 0); + const firstLineNumber = (visibleRanges.length > 0 ? visibleRanges[0].lineNumber : 0); + const lastLineNumber = (visibleRanges.length > 0 ? visibleRanges[visibleRanges.length - 1].lineNumber : 0); for (let i = 0, len = visibleRanges.length; i < len; i++) { - let lineVisibleRanges = visibleRanges[i]; - let lineNumber = lineVisibleRanges.lineNumber; - let lineIndex = lineNumber - visibleStartLineNumber; + const lineVisibleRanges = visibleRanges[i]; + const lineNumber = lineVisibleRanges.lineNumber; + const lineIndex = lineNumber - visibleStartLineNumber; - let lineHeight = hasMultipleSelections ? (lineNumber === lastLineNumber || lineNumber === firstLineNumber ? reducedLineHeight : fullLineHeight) : fullLineHeight; - let top = hasMultipleSelections ? (lineNumber === firstLineNumber ? 1 : 0) : 0; + const lineHeight = hasMultipleSelections ? (lineNumber === lastLineNumber || lineNumber === firstLineNumber ? reducedLineHeight : fullLineHeight) : fullLineHeight; + const top = hasMultipleSelections ? (lineNumber === firstLineNumber ? 1 : 0) : 0; let lineOutput = ''; for (let j = 0, lenJ = lineVisibleRanges.ranges.length; j < lenJ; j++) { - let visibleRange = lineVisibleRanges.ranges[j]; + const visibleRange = lineVisibleRanges.ranges[j]; if (visibleRangesHaveStyle) { const startStyle = visibleRange.startStyle!; @@ -366,23 +366,23 @@ export class SelectionsOverlay extends DynamicViewOverlay { private _previousFrameVisibleRangesWithStyle: (LineVisibleRangesWithStyle[] | null)[] = []; public prepareRender(ctx: RenderingContext): void { - let output: string[] = []; - let visibleStartLineNumber = ctx.visibleRange.startLineNumber; - let visibleEndLineNumber = ctx.visibleRange.endLineNumber; + const output: string[] = []; + const visibleStartLineNumber = ctx.visibleRange.startLineNumber; + const visibleEndLineNumber = ctx.visibleRange.endLineNumber; for (let lineNumber = visibleStartLineNumber; lineNumber <= visibleEndLineNumber; lineNumber++) { - let lineIndex = lineNumber - visibleStartLineNumber; + const lineIndex = lineNumber - visibleStartLineNumber; output[lineIndex] = ''; } - let thisFrameVisibleRangesWithStyle: (LineVisibleRangesWithStyle[] | null)[] = []; + const thisFrameVisibleRangesWithStyle: (LineVisibleRangesWithStyle[] | null)[] = []; for (let i = 0, len = this._selections.length; i < len; i++) { - let selection = this._selections[i]; + const selection = this._selections[i]; if (selection.isEmpty()) { thisFrameVisibleRangesWithStyle[i] = null; continue; } - let visibleRangesWithStyle = this._getVisibleRangesWithStyle(selection, ctx, this._previousFrameVisibleRangesWithStyle[i]); + const visibleRangesWithStyle = this._getVisibleRangesWithStyle(selection, ctx, this._previousFrameVisibleRangesWithStyle[i]); thisFrameVisibleRangesWithStyle[i] = visibleRangesWithStyle; this._actualRenderOneSelection(output, visibleStartLineNumber, this._selections.length > 1, visibleRangesWithStyle); } @@ -395,7 +395,7 @@ export class SelectionsOverlay extends DynamicViewOverlay { if (!this._renderResult) { return ''; } - let lineIndex = lineNumber - startLineNumber; + const lineIndex = lineNumber - startLineNumber; if (lineIndex < 0 || lineIndex >= this._renderResult.length) { return ''; } diff --git a/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts b/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts index 3d3450b6275..982b021bd1a 100644 --- a/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts +++ b/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts @@ -28,16 +28,16 @@ export class ViewCursors extends ViewPart { private _isVisible: boolean; - private _domNode: FastDomNode; + private readonly _domNode: FastDomNode; - private _startCursorBlinkAnimation: TimeoutTimer; - private _cursorFlatBlinkInterval: IntervalTimer; + private readonly _startCursorBlinkAnimation: TimeoutTimer; + private readonly _cursorFlatBlinkInterval: IntervalTimer; private _blinkingEnabled: boolean; private _editorHasFocus: boolean; - private _primaryCursor: ViewCursor; - private _secondaryCursors: ViewCursor[]; + private readonly _primaryCursor: ViewCursor; + private readonly _secondaryCursors: ViewCursor[]; private _renderData: IViewCursorRenderData[]; constructor(context: ViewContext) { @@ -108,15 +108,15 @@ export class ViewCursors extends ViewPart { if (this._secondaryCursors.length < secondaryPositions.length) { // Create new cursors - let addCnt = secondaryPositions.length - this._secondaryCursors.length; + const addCnt = secondaryPositions.length - this._secondaryCursors.length; for (let i = 0; i < addCnt; i++) { - let newCursor = new ViewCursor(this._context); + const newCursor = new ViewCursor(this._context); this._domNode.domNode.insertBefore(newCursor.getDomNode().domNode, this._primaryCursor.getDomNode().domNode.nextSibling); this._secondaryCursors.push(newCursor); } } else if (this._secondaryCursors.length > secondaryPositions.length) { // Remove some cursors - let removeCnt = this._secondaryCursors.length - secondaryPositions.length; + const removeCnt = this._secondaryCursors.length - secondaryPositions.length; for (let i = 0; i < removeCnt; i++) { this._domNode.removeChild(this._secondaryCursors[0].getDomNode()); this._secondaryCursors.splice(0, 1); @@ -129,7 +129,7 @@ export class ViewCursors extends ViewPart { } public onCursorStateChanged(e: viewEvents.ViewCursorStateChangedEvent): boolean { - let positions: Position[] = []; + const positions: Position[] = []; for (let i = 0, len = e.selections.length; i < len; i++) { positions[i] = e.selections[i].getPosition(); } @@ -169,7 +169,7 @@ export class ViewCursors extends ViewPart { return true; } public onTokensChanged(e: viewEvents.ViewTokensChangedEvent): boolean { - let shouldRender = (position: Position) => { + const shouldRender = (position: Position) => { for (let i = 0, len = e.ranges.length; i < len; i++) { if (e.ranges[i].fromLineNumber <= position.lineNumber && position.lineNumber <= e.ranges[i].toLineNumber) { return true; @@ -209,11 +209,11 @@ export class ViewCursors extends ViewPart { this._startCursorBlinkAnimation.cancel(); this._cursorFlatBlinkInterval.cancel(); - let blinkingStyle = this._getCursorBlinking(); + const blinkingStyle = this._getCursorBlinking(); // hidden and solid are special as they involve no animations - let isHidden = (blinkingStyle === TextEditorCursorBlinkingStyle.Hidden); - let isSolid = (blinkingStyle === TextEditorCursorBlinkingStyle.Solid); + const isHidden = (blinkingStyle === TextEditorCursorBlinkingStyle.Hidden); + const isSolid = (blinkingStyle === TextEditorCursorBlinkingStyle.Solid); if (isHidden) { this._hide(); diff --git a/src/vs/editor/browser/viewParts/viewZones/viewZones.ts b/src/vs/editor/browser/viewParts/viewZones/viewZones.ts index f148821b006..a51fbe5bf02 100644 --- a/src/vs/editor/browser/viewParts/viewZones/viewZones.ts +++ b/src/vs/editor/browser/viewParts/viewZones/viewZones.ts @@ -69,11 +69,11 @@ export class ViewZones extends ViewPart { private _recomputeWhitespacesProps(): boolean { let hadAChange = false; - let keys = Object.keys(this._zones); + const keys = Object.keys(this._zones); for (let i = 0, len = keys.length; i < len; i++) { - let id = keys[i]; - let zone = this._zones[id]; - let props = this._computeWhitespaceProps(zone.delegate); + const id = keys[i]; + const zone = this._zones[id]; + const props = this._computeWhitespaceProps(zone.delegate); if (this._context.viewLayout.changeWhitespace(parseInt(id, 10), props.afterViewLineNumber, props.heightInPx)) { this._safeCallOnComputedHeight(zone.delegate, props.heightInPx); hadAChange = true; @@ -150,7 +150,7 @@ export class ViewZones extends ViewPart { column: zone.afterColumn }); } else { - let validAfterLineNumber = this._context.model.validateModelPosition({ + const validAfterLineNumber = this._context.model.validateModelPosition({ lineNumber: zone.afterLineNumber, column: 1 }).lineNumber; @@ -174,8 +174,8 @@ export class ViewZones extends ViewPart { }); } - let viewPosition = this._context.model.coordinatesConverter.convertModelPositionToViewPosition(zoneAfterModelPosition); - let isVisible = this._context.model.coordinatesConverter.modelPositionIsVisible(zoneBeforeModelPosition); + const viewPosition = this._context.model.coordinatesConverter.convertModelPositionToViewPosition(zoneAfterModelPosition); + const isVisible = this._context.model.coordinatesConverter.modelPositionIsVisible(zoneBeforeModelPosition); return { afterViewLineNumber: viewPosition.lineNumber, heightInPx: (isVisible ? this._heightInPixels(zone) : 0), @@ -184,10 +184,10 @@ export class ViewZones extends ViewPart { } public addZone(zone: IViewZone): number { - let props = this._computeWhitespaceProps(zone); - let whitespaceId = this._context.viewLayout.addWhitespace(props.afterViewLineNumber, this._getZoneOrdinal(zone), props.heightInPx, props.minWidthInPx); + const props = this._computeWhitespaceProps(zone); + const whitespaceId = this._context.viewLayout.addWhitespace(props.afterViewLineNumber, this._getZoneOrdinal(zone), props.heightInPx, props.minWidthInPx); - let myZone: IMyViewZone = { + const myZone: IMyViewZone = { whitespaceId: whitespaceId, delegate: zone, isVisible: false, @@ -221,7 +221,7 @@ export class ViewZones extends ViewPart { public removeZone(id: number): boolean { if (this._zones.hasOwnProperty(id.toString())) { - let zone = this._zones[id.toString()]; + const zone = this._zones[id.toString()]; delete this._zones[id.toString()]; this._context.viewLayout.removeWhitespace(zone.whitespaceId); @@ -245,9 +245,9 @@ export class ViewZones extends ViewPart { public layoutZone(id: number): boolean { let changed = false; if (this._zones.hasOwnProperty(id.toString())) { - let zone = this._zones[id.toString()]; - let props = this._computeWhitespaceProps(zone.delegate); - // let newOrdinal = this._getZoneOrdinal(zone.delegate); + const zone = this._zones[id.toString()]; + const props = this._computeWhitespaceProps(zone.delegate); + // const newOrdinal = this._getZoneOrdinal(zone.delegate); changed = this._context.viewLayout.changeWhitespace(zone.whitespaceId, props.afterViewLineNumber, props.heightInPx) || changed; // TODO@Alex: change `newOrdinal` too @@ -261,7 +261,7 @@ export class ViewZones extends ViewPart { public shouldSuppressMouseDownOnViewZone(id: number): boolean { if (this._zones.hasOwnProperty(id.toString())) { - let zone = this._zones[id.toString()]; + const zone = this._zones[id.toString()]; return Boolean(zone.delegate.suppressMouseDown); } return false; @@ -310,7 +310,7 @@ export class ViewZones extends ViewPart { public render(ctx: RestrictedRenderingContext): void { const visibleWhitespaces = ctx.viewportData.whitespaceViewportData; - let visibleZones: { [id: string]: IViewWhitespaceViewportData; } = {}; + const visibleZones: { [id: string]: IViewWhitespaceViewportData; } = {}; let hasVisibleZone = false; for (let i = 0, len = visibleWhitespaces.length; i < len; i++) { @@ -318,10 +318,10 @@ export class ViewZones extends ViewPart { hasVisibleZone = true; } - let keys = Object.keys(this._zones); + const keys = Object.keys(this._zones); for (let i = 0, len = keys.length; i < len; i++) { - let id = keys[i]; - let zone = this._zones[id]; + const id = keys[i]; + const zone = this._zones[id]; let newTop = 0; let newHeight = 0; diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index 5a0796a17d2..16263681984 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -141,11 +141,11 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE private readonly _onDidLayoutChange: Emitter = this._register(new Emitter()); public readonly onDidLayoutChange: Event = this._onDidLayoutChange.event; - private _editorTextFocus: BooleanEventEmitter = this._register(new BooleanEventEmitter()); + private readonly _editorTextFocus: BooleanEventEmitter = this._register(new BooleanEventEmitter()); public readonly onDidFocusEditorText: Event = this._editorTextFocus.onDidChangeToTrue; public readonly onDidBlurEditorText: Event = this._editorTextFocus.onDidChangeToFalse; - private _editorWidgetFocus: BooleanEventEmitter = this._register(new BooleanEventEmitter()); + private readonly _editorWidgetFocus: BooleanEventEmitter = this._register(new BooleanEventEmitter()); public readonly onDidFocusEditorWidget: Event = this._editorWidgetFocus.onDidChangeToTrue; public readonly onDidBlurEditorWidget: Event = this._editorWidgetFocus.onDidChangeToFalse; @@ -294,9 +294,9 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE contributions = EditorExtensionsRegistry.getEditorContributions(); } for (let i = 0, len = contributions.length; i < len; i++) { - let ctor = contributions[i]; + const ctor = contributions[i]; try { - let contribution = this._instantiationService.createInstance(ctor, this); + const contribution = this._instantiationService.createInstance(ctor, this); this._contributions[contribution.getId()] = contribution; } catch (err) { onUnexpectedError(err); @@ -339,9 +339,9 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE this._focusTracker.dispose(); - let keys = Object.keys(this._contributions); + const keys = Object.keys(this._contributions); for (let i = 0, len = keys.length; i < len; i++) { - let contributionId = keys[i]; + const contributionId = keys[i]; this._contributions[contributionId].dispose(); } @@ -374,7 +374,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE return ''; } - let preserveBOM: boolean = (options && options.preserveBOM) ? true : false; + const preserveBOM: boolean = (options && options.preserveBOM) ? true : false; let eolPreference = EndOfLinePreference.TextDefined; if (options && options.lineEnding && options.lineEnding === '\n') { eolPreference = EndOfLinePreference.LF; @@ -409,10 +409,10 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE return; } - let detachedModel = this._detachModel(); + const detachedModel = this._detachModel(); this._attachModel(model); - let e: editorCommon.IModelChangedEvent = { + const e: editorCommon.IModelChangedEvent = { oldModelUrl: detachedModel ? detachedModel.uri : null, newModelUrl: model ? model.uri : null }; @@ -426,7 +426,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE this._decorationTypeKeysToIds = {}; if (this._decorationTypeSubtypes) { for (let decorationType in this._decorationTypeSubtypes) { - let subTypes = this._decorationTypeSubtypes[decorationType]; + const subTypes = this._decorationTypeSubtypes[decorationType]; for (let subType in subTypes) { this._removeDecorationType(decorationType + '-' + subType); } @@ -450,11 +450,11 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE } private static _getVerticalOffsetForPosition(modelData: ModelData, modelLineNumber: number, modelColumn: number): number { - let modelPosition = modelData.model.validatePosition({ + const modelPosition = modelData.model.validatePosition({ lineNumber: modelLineNumber, column: modelColumn }); - let viewPosition = modelData.viewModel.coordinatesConverter.convertModelPositionToViewPosition(modelPosition); + const viewPosition = modelData.viewModel.coordinatesConverter.convertModelPositionToViewPosition(modelPosition); return modelData.viewModel.viewLayout.getVerticalOffsetForLineNumber(viewPosition.lineNumber); } @@ -483,8 +483,8 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE return rawPosition.column; } - let position = this._modelData.model.validatePosition(rawPosition); - let tabSize = this._modelData.model.getOptions().tabSize; + const position = this._modelData.model.validatePosition(rawPosition); + const tabSize = this._modelData.model.getOptions().tabSize; return CursorColumns.visibleColumnFromColumn(this._modelData.model.getLineContent(position.lineNumber), position.column, tabSize) + 1; } @@ -608,8 +608,8 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE public setSelection(selection: ISelection): void; public setSelection(editorSelection: Selection): void; public setSelection(something: any): void { - let isSelection = Selection.isISelection(something); - let isRange = Range.isIRange(something); + const isSelection = Selection.isISelection(something); + const isRange = Range.isIRange(something); if (!isSelection && !isRange) { throw new Error('Invalid arguments'); @@ -619,7 +619,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE this._setSelectionImpl(something); } else if (isRange) { // act as if it was an IRange - let selection: ISelection = { + const selection: ISelection = { selectionStartLineNumber: something.startLineNumber, selectionStartColumn: something.startColumn, positionLineNumber: something.endLineNumber, @@ -633,7 +633,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE if (!this._modelData) { return; } - let selection = new Selection(sel.selectionStartLineNumber, sel.selectionStartColumn, sel.positionLineNumber, sel.positionColumn); + const selection = new Selection(sel.selectionStartLineNumber, sel.selectionStartColumn, sel.positionLineNumber, sel.positionColumn); this._modelData.cursor.setSelections('api', [selection]); } @@ -825,7 +825,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE } const codeEditorState = s as editorCommon.ICodeEditorViewState | null; if (codeEditorState && codeEditorState.cursorState && codeEditorState.viewState) { - let cursorState = codeEditorState.cursorState; + const cursorState = codeEditorState.cursorState; if (Array.isArray(cursorState)) { this._modelData.cursor.restoreState(cursorState); } else { @@ -833,11 +833,11 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE this._modelData.cursor.restoreState([cursorState]); } - let contributionsState = codeEditorState.contributionsState || {}; - let keys = Object.keys(this._contributions); + const contributionsState = codeEditorState.contributionsState || {}; + const keys = Object.keys(this._contributions); for (let i = 0, len = keys.length; i < len; i++) { - let id = keys[i]; - let contribution = this._contributions[id]; + const id = keys[i]; + const contribution = this._contributions[id]; if (typeof contribution.restoreViewState === 'function') { contribution.restoreViewState(contributionsState[id]); } @@ -859,11 +859,11 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE } public getActions(): editorCommon.IEditorAction[] { - let result: editorCommon.IEditorAction[] = []; + const result: editorCommon.IEditorAction[] = []; - let keys = Object.keys(this._actions); + const keys = Object.keys(this._actions); for (let i = 0, len = keys.length; i < len; i++) { - let id = keys[i]; + const id = keys[i]; result.push(this._actions[id]); } @@ -1038,18 +1038,18 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE public setDecorations(decorationTypeKey: string, decorationOptions: editorCommon.IDecorationOptions[]): void { - let newDecorationsSubTypes: { [key: string]: boolean } = {}; - let oldDecorationsSubTypes = this._decorationTypeSubtypes[decorationTypeKey] || {}; + const newDecorationsSubTypes: { [key: string]: boolean } = {}; + const oldDecorationsSubTypes = this._decorationTypeSubtypes[decorationTypeKey] || {}; this._decorationTypeSubtypes[decorationTypeKey] = newDecorationsSubTypes; - let newModelDecorations: IModelDeltaDecoration[] = []; + const newModelDecorations: IModelDeltaDecoration[] = []; for (let decorationOption of decorationOptions) { let typeKey = decorationTypeKey; if (decorationOption.renderOptions) { // identify custom reder options by a hash code over all keys and values // For custom render options register a decoration type if necessary - let subType = hash(decorationOption.renderOptions).toString(16); + const subType = hash(decorationOption.renderOptions).toString(16); // The fact that `decorationTypeKey` appears in the typeKey has no influence // it is just a mechanism to get predictable and unique keys (repeatable for the same options and unique across clients) typeKey = decorationTypeKey + '-' + subType; @@ -1059,7 +1059,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE } newDecorationsSubTypes[subType] = true; } - let opts = this._resolveDecorationOptions(typeKey, !!decorationOption.hoverMessage); + const opts = this._resolveDecorationOptions(typeKey, !!decorationOption.hoverMessage); if (decorationOption.hoverMessage) { opts.hoverMessage = decorationOption.hoverMessage; } @@ -1074,33 +1074,33 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE } // update all decorations - let oldDecorationsIds = this._decorationTypeKeysToIds[decorationTypeKey] || []; + const oldDecorationsIds = this._decorationTypeKeysToIds[decorationTypeKey] || []; this._decorationTypeKeysToIds[decorationTypeKey] = this.deltaDecorations(oldDecorationsIds, newModelDecorations); } public setDecorationsFast(decorationTypeKey: string, ranges: IRange[]): void { // remove decoration sub types that are no longer used, deregister decoration type if necessary - let oldDecorationsSubTypes = this._decorationTypeSubtypes[decorationTypeKey] || {}; + const oldDecorationsSubTypes = this._decorationTypeSubtypes[decorationTypeKey] || {}; for (let subType in oldDecorationsSubTypes) { this._removeDecorationType(decorationTypeKey + '-' + subType); } this._decorationTypeSubtypes[decorationTypeKey] = {}; const opts = ModelDecorationOptions.createDynamic(this._resolveDecorationOptions(decorationTypeKey, false)); - let newModelDecorations: IModelDeltaDecoration[] = new Array(ranges.length); + const newModelDecorations: IModelDeltaDecoration[] = new Array(ranges.length); for (let i = 0, len = ranges.length; i < len; i++) { newModelDecorations[i] = { range: ranges[i], options: opts }; } // update all decorations - let oldDecorationsIds = this._decorationTypeKeysToIds[decorationTypeKey] || []; + const oldDecorationsIds = this._decorationTypeKeysToIds[decorationTypeKey] || []; this._decorationTypeKeysToIds[decorationTypeKey] = this.deltaDecorations(oldDecorationsIds, newModelDecorations); } public removeDecorations(decorationTypeKey: string): void { // remove decorations for type and sub type - let oldDecorationsIds = this._decorationTypeKeysToIds[decorationTypeKey]; + const oldDecorationsIds = this._decorationTypeKeysToIds[decorationTypeKey]; if (oldDecorationsIds) { this.deltaDecorations(oldDecorationsIds, []); } @@ -1161,7 +1161,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE } public addContentWidget(widget: editorBrowser.IContentWidget): void { - let widgetData: IContentWidgetData = { + const widgetData: IContentWidgetData = { widget: widget, position: widget.getPosition() }; @@ -1178,9 +1178,9 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE } public layoutContentWidget(widget: editorBrowser.IContentWidget): void { - let widgetId = widget.getId(); + const widgetId = widget.getId(); if (this._contentWidgets.hasOwnProperty(widgetId)) { - let widgetData = this._contentWidgets[widgetId]; + const widgetData = this._contentWidgets[widgetId]; widgetData.position = widget.getPosition(); if (this._modelData && this._modelData.hasRealView) { this._modelData.view.layoutContentWidget(widgetData); @@ -1189,9 +1189,9 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE } public removeContentWidget(widget: editorBrowser.IContentWidget): void { - let widgetId = widget.getId(); + const widgetId = widget.getId(); if (this._contentWidgets.hasOwnProperty(widgetId)) { - let widgetData = this._contentWidgets[widgetId]; + const widgetData = this._contentWidgets[widgetId]; delete this._contentWidgets[widgetId]; if (this._modelData && this._modelData.hasRealView) { this._modelData.view.removeContentWidget(widgetData); @@ -1200,7 +1200,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE } public addOverlayWidget(widget: editorBrowser.IOverlayWidget): void { - let widgetData: IOverlayWidgetData = { + const widgetData: IOverlayWidgetData = { widget: widget, position: widget.getPosition() }; @@ -1217,9 +1217,9 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE } public layoutOverlayWidget(widget: editorBrowser.IOverlayWidget): void { - let widgetId = widget.getId(); + const widgetId = widget.getId(); if (this._overlayWidgets.hasOwnProperty(widgetId)) { - let widgetData = this._overlayWidgets[widgetId]; + const widgetData = this._overlayWidgets[widgetId]; widgetData.position = widget.getPosition(); if (this._modelData && this._modelData.hasRealView) { this._modelData.view.layoutOverlayWidget(widgetData); @@ -1228,9 +1228,9 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE } public removeOverlayWidget(widget: editorBrowser.IOverlayWidget): void { - let widgetId = widget.getId(); + const widgetId = widget.getId(); if (this._overlayWidgets.hasOwnProperty(widgetId)) { - let widgetData = this._overlayWidgets[widgetId]; + const widgetData = this._overlayWidgets[widgetId]; delete this._overlayWidgets[widgetId]; if (this._modelData && this._modelData.hasRealView) { this._modelData.view.removeOverlayWidget(widgetData); @@ -1242,7 +1242,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE if (!this._modelData || !this._modelData.hasRealView) { return; } - let hasChanges = this._modelData.view.change(callback); + const hasChanges = this._modelData.view.change(callback); if (hasChanges) { this._onDidChangeViewZones.fire(); } @@ -1260,11 +1260,11 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE return null; } - let position = this._modelData.model.validatePosition(rawPosition); - let layoutInfo = this._configuration.editor.layoutInfo; + const position = this._modelData.model.validatePosition(rawPosition); + const layoutInfo = this._configuration.editor.layoutInfo; - let top = CodeEditorWidget._getVerticalOffsetForPosition(this._modelData, position.lineNumber, position.column) - this.getScrollTop(); - let left = this._modelData.view.getOffsetForColumn(position.lineNumber, position.column) + layoutInfo.glyphMarginWidth + layoutInfo.lineNumbersWidth + layoutInfo.decorationsWidth - this.getScrollLeft(); + const top = CodeEditorWidget._getVerticalOffsetForPosition(this._modelData, position.lineNumber, position.column) - this.getScrollTop(); + const left = this._modelData.view.getOffsetForColumn(position.lineNumber, position.column) + layoutInfo.glyphMarginWidth + layoutInfo.lineNumbersWidth + layoutInfo.decorationsWidth - this.getScrollLeft(); return { top: top, @@ -1329,7 +1329,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE })); listenersToRemove.push(cursor.onDidChange((e: CursorStateChangedEvent) => { - let positions: Position[] = []; + const positions: Position[] = []; for (let i = 0, len = e.selections.length; i < len; i++) { positions[i] = e.selections[i].getPosition(); } @@ -1357,13 +1357,13 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE let keys = Object.keys(this._contentWidgets); for (let i = 0, len = keys.length; i < len; i++) { - let widgetId = keys[i]; + const widgetId = keys[i]; view.addContentWidget(this._contentWidgets[widgetId]); } keys = Object.keys(this._overlayWidgets); for (let i = 0, len = keys.length; i < len; i++) { - let widgetId = keys[i]; + const widgetId = keys[i]; view.addOverlayWidget(this._overlayWidgets[widgetId]); } @@ -1534,7 +1534,7 @@ export class BooleanEventEmitter extends Disposable { } public setValue(_value: boolean) { - let value = (_value ? BooleanEventValue.True : BooleanEventValue.False); + const value = (_value ? BooleanEventValue.True : BooleanEventValue.False); if (this._value === value) { return; } @@ -1549,16 +1549,16 @@ export class BooleanEventEmitter extends Disposable { class EditorContextKeysManager extends Disposable { - private _editor: CodeEditorWidget; - private _editorFocus: IContextKey; - private _textInputFocus: IContextKey; - private _editorTextFocus: IContextKey; - private _editorTabMovesFocus: IContextKey; - private _editorReadonly: IContextKey; - private _hasMultipleSelections: IContextKey; - private _hasNonEmptySelection: IContextKey; - private _canUndo: IContextKey; - private _canRedo: IContextKey; + private readonly _editor: CodeEditorWidget; + private readonly _editorFocus: IContextKey; + private readonly _textInputFocus: IContextKey; + private readonly _editorTextFocus: IContextKey; + private readonly _editorTabMovesFocus: IContextKey; + private readonly _editorReadonly: IContextKey; + private readonly _hasMultipleSelections: IContextKey; + private readonly _hasNonEmptySelection: IContextKey; + private readonly _canUndo: IContextKey; + private readonly _canRedo: IContextKey; constructor( editor: CodeEditorWidget, @@ -1595,14 +1595,14 @@ class EditorContextKeysManager extends Disposable { } private _updateFromConfig(): void { - let config = this._editor.getConfiguration(); + const config = this._editor.getConfiguration(); this._editorTabMovesFocus.set(config.tabFocusMode); this._editorReadonly.set(config.readOnly); } private _updateFromSelection(): void { - let selections = this._editor.getSelections(); + const selections = this._editor.getSelections(); if (!selections) { this._hasMultipleSelections.reset(); this._hasNonEmptySelection.reset(); @@ -1627,25 +1627,25 @@ class EditorContextKeysManager extends Disposable { export class EditorModeContext extends Disposable { - private _editor: CodeEditorWidget; + private readonly _editor: CodeEditorWidget; - private _langId: IContextKey; - private _hasCompletionItemProvider: IContextKey; - private _hasCodeActionsProvider: IContextKey; - private _hasCodeLensProvider: IContextKey; - private _hasDefinitionProvider: IContextKey; - private _hasDeclarationProvider: IContextKey; - private _hasImplementationProvider: IContextKey; - private _hasTypeDefinitionProvider: IContextKey; - private _hasHoverProvider: IContextKey; - private _hasDocumentHighlightProvider: IContextKey; - private _hasDocumentSymbolProvider: IContextKey; - private _hasReferenceProvider: IContextKey; - private _hasRenameProvider: IContextKey; - private _hasDocumentFormattingProvider: IContextKey; - private _hasDocumentSelectionFormattingProvider: IContextKey; - private _hasSignatureHelpProvider: IContextKey; - private _isInWalkThrough: IContextKey; + private readonly _langId: IContextKey; + private readonly _hasCompletionItemProvider: IContextKey; + private readonly _hasCodeActionsProvider: IContextKey; + private readonly _hasCodeLensProvider: IContextKey; + private readonly _hasDefinitionProvider: IContextKey; + private readonly _hasDeclarationProvider: IContextKey; + private readonly _hasImplementationProvider: IContextKey; + private readonly _hasTypeDefinitionProvider: IContextKey; + private readonly _hasHoverProvider: IContextKey; + private readonly _hasDocumentHighlightProvider: IContextKey; + private readonly _hasDocumentSymbolProvider: IContextKey; + private readonly _hasReferenceProvider: IContextKey; + private readonly _hasRenameProvider: IContextKey; + private readonly _hasDocumentFormattingProvider: IContextKey; + private readonly _hasDocumentSelectionFormattingProvider: IContextKey; + private readonly _hasSignatureHelpProvider: IContextKey; + private readonly _isInWalkThrough: IContextKey; constructor( editor: CodeEditorWidget, @@ -1751,7 +1751,7 @@ export class EditorModeContext extends Disposable { class CodeEditorWidgetFocusTracker extends Disposable { private _hasFocus: boolean; - private _domFocusTracker: dom.IFocusTracker; + private readonly _domFocusTracker: dom.IFocusTracker; private readonly _onChange: Emitter = this._register(new Emitter()); public readonly onChange: Event = this._onChange.event; diff --git a/src/vs/editor/browser/widget/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditorWidget.ts index 1f32835046f..d8ee01053ee 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget.ts @@ -148,7 +148,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE private readonly id: number; - private _domElement: HTMLElement; + private readonly _domElement: HTMLElement; protected readonly _containerDomElement: HTMLElement; private readonly _overviewDomElement: HTMLElement; private readonly _overviewViewportDomElement: FastDomNode; @@ -160,12 +160,12 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE private originalEditor: CodeEditorWidget; private _originalDomNode: HTMLElement; - private _originalEditorState: VisualEditorState; + private readonly _originalEditorState: VisualEditorState; private _originalOverviewRuler: editorBrowser.IOverviewRuler; private modifiedEditor: CodeEditorWidget; private _modifiedDomNode: HTMLElement; - private _modifiedEditorState: VisualEditorState; + private readonly _modifiedEditorState: VisualEditorState; private _modifiedOverviewRuler: editorBrowser.IOverviewRuler; private _currentlyChangingViewZones: boolean; @@ -184,15 +184,15 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE private _enableSplitViewResizing: boolean; private _strategy: IDiffEditorWidgetStyle; - private _updateDecorationsRunner: RunOnceScheduler; + private readonly _updateDecorationsRunner: RunOnceScheduler; - private _editorWorkerService: IEditorWorkerService; + private readonly _editorWorkerService: IEditorWorkerService; protected _contextKeyService: IContextKeyService; - private _codeEditorService: ICodeEditorService; - private _themeService: IThemeService; - private _notificationService: INotificationService; + private readonly _codeEditorService: ICodeEditorService; + private readonly _themeService: IThemeService; + private readonly _notificationService: INotificationService; - private _reviewPane: DiffReview; + private readonly _reviewPane: DiffReview; constructor( domElement: HTMLElement, @@ -1251,7 +1251,7 @@ interface IMyViewZone { class ForeignViewZonesIterator { private _index: number; - private _source: IEditorWhitespace[]; + private readonly _source: IEditorWhitespace[]; public current: IEditorWhitespace | null; constructor(source: IEditorWhitespace[]) { @@ -1272,9 +1272,9 @@ class ForeignViewZonesIterator { abstract class ViewZonesComputer { - private lineChanges: editorCommon.ILineChange[]; - private originalForeignVZ: IEditorWhitespace[]; - private modifiedForeignVZ: IEditorWhitespace[]; + private readonly lineChanges: editorCommon.ILineChange[]; + private readonly originalForeignVZ: IEditorWhitespace[]; + private readonly modifiedForeignVZ: IEditorWhitespace[]; constructor(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[]) { this.lineChanges = lineChanges; @@ -1532,7 +1532,7 @@ class DiffEdtorWidgetSideBySide extends DiffEditorWidgetStyle implements IDiffEd static MINIMUM_EDITOR_WIDTH = 100; private _disableSash: boolean; - private _sash: Sash; + private readonly _sash: Sash; private _sashRatio: number | null; private _sashPosition: number | null; private _startSashPosition: number; @@ -1911,10 +1911,10 @@ class DiffEdtorWidgetInline extends DiffEditorWidgetStyle implements IDiffEditor class InlineViewZonesComputer extends ViewZonesComputer { - private originalModel: ITextModel; - private modifiedEditorConfiguration: editorOptions.InternalEditorOptions; - private modifiedEditorTabSize: number; - private renderIndicators: boolean; + private readonly originalModel: ITextModel; + private readonly modifiedEditorConfiguration: editorOptions.InternalEditorOptions; + private readonly modifiedEditorTabSize: number; + private readonly renderIndicators: boolean; constructor(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor, renderIndicators: boolean) { super(lineChanges, originalForeignVZ, modifiedForeignVZ); diff --git a/src/vs/editor/browser/widget/embeddedCodeEditorWidget.ts b/src/vs/editor/browser/widget/embeddedCodeEditorWidget.ts index 5489ac6bea7..1c6f251a3ed 100644 --- a/src/vs/editor/browser/widget/embeddedCodeEditorWidget.ts +++ b/src/vs/editor/browser/widget/embeddedCodeEditorWidget.ts @@ -19,8 +19,8 @@ import { IAccessibilityService } from 'vs/platform/accessibility/common/accessib export class EmbeddedCodeEditorWidget extends CodeEditorWidget { - private _parentEditor: ICodeEditor; - private _overwriteOptions: IEditorOptions; + private readonly _parentEditor: ICodeEditor; + private readonly _overwriteOptions: IEditorOptions; constructor( domElement: HTMLElement, @@ -62,8 +62,8 @@ export class EmbeddedCodeEditorWidget extends CodeEditorWidget { export class EmbeddedDiffEditorWidget extends DiffEditorWidget { - private _parentEditor: ICodeEditor; - private _overwriteOptions: IDiffEditorOptions; + private readonly _parentEditor: ICodeEditor; + private readonly _overwriteOptions: IDiffEditorOptions; constructor( domElement: HTMLElement, diff --git a/src/vs/editor/common/commands/replaceCommand.ts b/src/vs/editor/common/commands/replaceCommand.ts index 28671285ac8..c6cd6494050 100644 --- a/src/vs/editor/common/commands/replaceCommand.ts +++ b/src/vs/editor/common/commands/replaceCommand.ts @@ -98,9 +98,9 @@ export class ReplaceCommandWithOffsetCursorState implements ICommand { export class ReplaceCommandThatPreservesSelection implements ICommand { - private _range: Range; - private _text: string; - private _initialSelection: Selection; + private readonly _range: Range; + private readonly _text: string; + private readonly _initialSelection: Selection; private _selectionId: string; constructor(editRange: Range, text: string, initialSelection: Selection) { diff --git a/src/vs/editor/common/commands/shiftCommand.ts b/src/vs/editor/common/commands/shiftCommand.ts index f24790ec137..16ff871c456 100644 --- a/src/vs/editor/common/commands/shiftCommand.ts +++ b/src/vs/editor/common/commands/shiftCommand.ts @@ -68,8 +68,8 @@ export class ShiftCommand implements ICommand { } } - private _opts: IShiftCommandOpts; - private _selection: Selection; + private readonly _opts: IShiftCommandOpts; + private readonly _selection: Selection; private _selectionId: string; private _useLastEditRangeForCursorEndPosition: boolean; private _selectionStartColumnStaysPut: boolean; diff --git a/src/vs/editor/common/commands/surroundSelectionCommand.ts b/src/vs/editor/common/commands/surroundSelectionCommand.ts index 7a6b7274016..ed60c299399 100644 --- a/src/vs/editor/common/commands/surroundSelectionCommand.ts +++ b/src/vs/editor/common/commands/surroundSelectionCommand.ts @@ -9,9 +9,9 @@ import { ICommand, ICursorStateComputerData, IEditOperationBuilder } from 'vs/ed import { ITextModel } from 'vs/editor/common/model'; export class SurroundSelectionCommand implements ICommand { - private _range: Selection; - private _charBeforeSelection: string; - private _charAfterSelection: string; + private readonly _range: Selection; + private readonly _charBeforeSelection: string; + private readonly _charAfterSelection: string; constructor(range: Selection, charBeforeSelection: string, charAfterSelection: string) { this._range = range; diff --git a/src/vs/editor/common/commands/trimTrailingWhitespaceCommand.ts b/src/vs/editor/common/commands/trimTrailingWhitespaceCommand.ts index 5a9d6e3902b..2d442842b97 100644 --- a/src/vs/editor/common/commands/trimTrailingWhitespaceCommand.ts +++ b/src/vs/editor/common/commands/trimTrailingWhitespaceCommand.ts @@ -13,9 +13,9 @@ import { IIdentifiedSingleEditOperation, ITextModel } from 'vs/editor/common/mod export class TrimTrailingWhitespaceCommand implements ICommand { - private selection: Selection; + private readonly selection: Selection; private selectionId: string; - private cursors: Position[]; + private readonly cursors: Position[]; constructor(selection: Selection, cursors: Position[]) { this.selection = selection; 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/core/characterClassifier.ts b/src/vs/editor/common/core/characterClassifier.ts index 83b5d93b1df..9d448641d17 100644 --- a/src/vs/editor/common/core/characterClassifier.ts +++ b/src/vs/editor/common/core/characterClassifier.ts @@ -63,7 +63,7 @@ const enum Boolean { export class CharacterSet { - private _actual: CharacterClassifier; + private readonly _actual: CharacterClassifier; constructor() { this._actual = new CharacterClassifier(Boolean.False); 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/core/uint.ts b/src/vs/editor/common/core/uint.ts index 67556d1a8bc..d89c8043598 100644 --- a/src/vs/editor/common/core/uint.ts +++ b/src/vs/editor/common/core/uint.ts @@ -5,7 +5,7 @@ export class Uint8Matrix { - private _data: Uint8Array; + private readonly _data: Uint8Array; public readonly rows: number; public readonly cols: number; 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 a15dd004bef..48a268b0bd6 100644 --- a/src/vs/editor/common/model.ts +++ b/src/vs/editor/common/model.ts @@ -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. diff --git a/src/vs/editor/common/model/editStack.ts b/src/vs/editor/common/model/editStack.ts index b60b083c1b6..add6b310ea5 100644 --- a/src/vs/editor/common/model/editStack.ts +++ b/src/vs/editor/common/model/editStack.ts @@ -102,7 +102,7 @@ export interface IUndoRedoResult { export class EditStack { - private model: TextModel; + private readonly model: TextModel; private currentOpenStackElement: IStackElement | null; private past: IStackElement[]; private future: IStackElement[]; diff --git a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts index 5f847c76ee3..7b7439f32c4 100644 --- a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts +++ b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts @@ -157,10 +157,10 @@ export class StringBuffer { * 2. TreeNode/Buffers normalization should not happen during snapshot reading. */ class PieceTreeSnapshot implements ITextSnapshot { - private _pieces: Piece[]; + private readonly _pieces: Piece[]; private _index: number; - private _tree: PieceTreeBase; - private _BOM: string; + private readonly _tree: PieceTreeBase; + private readonly _BOM: string; constructor(tree: PieceTreeBase, BOM: string) { this._pieces = []; @@ -205,7 +205,7 @@ interface CacheEntry { } class PieceTreeSearchCache { - private _limit: number; + private readonly _limit: number; private _cache: CacheEntry[]; constructor(limit: number) { diff --git a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts index 1030643a13d..1424533f32f 100644 --- a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts +++ b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts @@ -27,8 +27,8 @@ export interface IReverseSingleEditOperation extends IIdentifiedSingleEditOperat } export class PieceTreeTextBuffer implements ITextBuffer { - private _pieceTree: PieceTreeBase; - private _BOM: string; + private readonly _pieceTree: PieceTreeBase; + private readonly _BOM: string; private _mightContainRTL: boolean; private _mightContainNonBasicASCII: boolean; diff --git a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder.ts b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder.ts index cfa58453501..5b24e6f1c64 100644 --- a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder.ts +++ b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder.ts @@ -62,12 +62,12 @@ export class PieceTreeTextBufferFactory implements ITextBufferFactory { } export class PieceTreeTextBufferBuilder implements ITextBufferBuilder { - private chunks: StringBuffer[]; + private readonly chunks: StringBuffer[]; private BOM: string; private _hasPreviousChar: boolean; private _previousChar: number; - private _tmpLineStarts: number[]; + private readonly _tmpLineStarts: number[]; private cr: number; private lf: number; diff --git a/src/vs/editor/common/model/textModel.ts b/src/vs/editor/common/model/textModel.ts index 1b67ebef2c8..c8fc1231a9b 100644 --- a/src/vs/editor/common/model/textModel.ts +++ b/src/vs/editor/common/model/textModel.ts @@ -265,8 +265,8 @@ export class TextModel extends Disposable implements model.ITextModel { //#region Tokenization private _languageIdentifier: LanguageIdentifier; - private _tokenizationListener: IDisposable; - private _languageRegistryListener: IDisposable; + private readonly _tokenizationListener: IDisposable; + private readonly _languageRegistryListener: IDisposable; private _revalidateTokensTimeout: any; /*private*/_tokens: ModelLinesTokens; //#endregion @@ -2730,12 +2730,12 @@ class DecorationsTrees { /** * This tree holds decorations that do not show up in the overview ruler. */ - private _decorationsTree0: IntervalTree; + private readonly _decorationsTree0: IntervalTree; /** * This tree holds decorations that show up in the overview ruler. */ - private _decorationsTree1: IntervalTree; + private readonly _decorationsTree1: IntervalTree; constructor() { this._decorationsTree0 = new IntervalTree(); diff --git a/src/vs/editor/common/model/textModelSearch.ts b/src/vs/editor/common/model/textModelSearch.ts index 9f776c68654..1b63ef93f9f 100644 --- a/src/vs/editor/common/model/textModelSearch.ts +++ b/src/vs/editor/common/model/textModelSearch.ts @@ -509,8 +509,8 @@ export function isValidMatch(wordSeparators: WordCharacterClassifier, text: stri } export class Searcher { - private _wordSeparators: WordCharacterClassifier | null; - private _searchRegex: RegExp; + private readonly _wordSeparators: WordCharacterClassifier | null; + private readonly _searchRegex: RegExp; private _prevMatchStartIndex: number; private _prevMatchLength: number; diff --git a/src/vs/editor/common/model/textModelTokens.ts b/src/vs/editor/common/model/textModelTokens.ts index cff6814a9a7..8387872c18f 100644 --- a/src/vs/editor/common/model/textModelTokens.ts +++ b/src/vs/editor/common/model/textModelTokens.ts @@ -472,7 +472,7 @@ export class ModelLinesTokens { export class ModelTokensChangedEventBuilder { - private _ranges: { fromLineNumber: number; toLineNumber: number; }[]; + private readonly _ranges: { fromLineNumber: number; toLineNumber: number; }[]; constructor() { this._ranges = []; diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index ec137f5bd5b..7b49c05eaea 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -287,7 +287,7 @@ export const enum CompletionItemKind { /** * @internal */ -export let completionKindToCssClass = (function () { +export const completionKindToCssClass = (function () { let data = Object.create(null); data[CompletionItemKind.Method] = 'method'; data[CompletionItemKind.Function] = 'function'; diff --git a/src/vs/editor/common/modes/abstractMode.ts b/src/vs/editor/common/modes/abstractMode.ts index 10919a8f31f..dbed73b8b7f 100644 --- a/src/vs/editor/common/modes/abstractMode.ts +++ b/src/vs/editor/common/modes/abstractMode.ts @@ -7,7 +7,7 @@ import { IMode, LanguageIdentifier } from 'vs/editor/common/modes'; export class FrankensteinMode implements IMode { - private _languageIdentifier: LanguageIdentifier; + private readonly _languageIdentifier: LanguageIdentifier; constructor(languageIdentifier: LanguageIdentifier) { this._languageIdentifier = languageIdentifier; diff --git a/src/vs/editor/common/modes/languageConfigurationRegistry.ts b/src/vs/editor/common/modes/languageConfigurationRegistry.ts index 1c46797d5e7..8bea5db7e8d 100644 --- a/src/vs/editor/common/modes/languageConfigurationRegistry.ts +++ b/src/vs/editor/common/modes/languageConfigurationRegistry.ts @@ -175,7 +175,7 @@ export class LanguageConfigurationChangeEvent { export class LanguageConfigurationRegistryImpl { - private _entries: RichEditSupport[]; + private readonly _entries: RichEditSupport[]; private readonly _onDidChange = new Emitter(); public readonly onDidChange: Event = this._onDidChange.event; diff --git a/src/vs/editor/common/modes/languageFeatureRegistry.ts b/src/vs/editor/common/modes/languageFeatureRegistry.ts index 37b10402317..25c1c221e5b 100644 --- a/src/vs/editor/common/modes/languageFeatureRegistry.ts +++ b/src/vs/editor/common/modes/languageFeatureRegistry.ts @@ -29,7 +29,7 @@ function isExclusive(selector: LanguageSelector): boolean { export class LanguageFeatureRegistry { private _clock: number = 0; - private _entries: Entry[] = []; + private readonly _entries: Entry[] = []; private readonly _onDidChange = new Emitter(); constructor() { diff --git a/src/vs/editor/common/modes/linkComputer.ts b/src/vs/editor/common/modes/linkComputer.ts index 290b424ee2d..00bf7d01b36 100644 --- a/src/vs/editor/common/modes/linkComputer.ts +++ b/src/vs/editor/common/modes/linkComputer.ts @@ -35,8 +35,8 @@ export type Edge = [State, number, State]; export class StateMachine { - private _states: Uint8Matrix; - private _maxCharCode: number; + private readonly _states: Uint8Matrix; + private readonly _maxCharCode: number; constructor(edges: Edge[]) { let maxCharCode = 0; diff --git a/src/vs/editor/common/modes/modesRegistry.ts b/src/vs/editor/common/modes/modesRegistry.ts index ea7a8f05d1a..fbdda5d65a4 100644 --- a/src/vs/editor/common/modes/modesRegistry.ts +++ b/src/vs/editor/common/modes/modesRegistry.ts @@ -17,7 +17,7 @@ export const Extensions = { export class EditorModesRegistry { - private _languages: ILanguageExtensionPoint[]; + private readonly _languages: ILanguageExtensionPoint[]; private _dynamicLanguages: ILanguageExtensionPoint[]; private readonly _onDidChangeLanguages = new Emitter(); diff --git a/src/vs/editor/common/modes/supports/inplaceReplaceSupport.ts b/src/vs/editor/common/modes/supports/inplaceReplaceSupport.ts index d49dc63f57e..3ca8affc18f 100644 --- a/src/vs/editor/common/modes/supports/inplaceReplaceSupport.ts +++ b/src/vs/editor/common/modes/supports/inplaceReplaceSupport.ts @@ -64,7 +64,7 @@ export class BasicInplaceReplace { return null; } - private _defaultValueSet: string[][] = [ + private readonly _defaultValueSet: string[][] = [ ['true', 'false'], ['True', 'False'], ['Private', 'Public', 'Friend', 'ReadOnly', 'Partial', 'Protected', 'WriteOnly'], diff --git a/src/vs/editor/common/modes/supports/tokenization.ts b/src/vs/editor/common/modes/supports/tokenization.ts index d245a41fb89..12566c3b00e 100644 --- a/src/vs/editor/common/modes/supports/tokenization.ts +++ b/src/vs/editor/common/modes/supports/tokenization.ts @@ -151,8 +151,8 @@ const colorRegExp = /^#?([0-9A-Fa-f]{6})([0-9A-Fa-f]{2})?$/; export class ColorMap { private _lastColorId: number; - private _id2color: Color[]; - private _color2id: Map; + private readonly _id2color: Color[]; + private readonly _color2id: Map; constructor() { this._lastColorId = 0; diff --git a/src/vs/editor/common/modes/tokenizationRegistry.ts b/src/vs/editor/common/modes/tokenizationRegistry.ts index 158608f91e4..82ad12da605 100644 --- a/src/vs/editor/common/modes/tokenizationRegistry.ts +++ b/src/vs/editor/common/modes/tokenizationRegistry.ts @@ -10,8 +10,8 @@ import { ColorId, ITokenizationRegistry, ITokenizationSupport, ITokenizationSupp export class TokenizationRegistryImpl implements ITokenizationRegistry { - private _map: { [language: string]: ITokenizationSupport }; - private _promises: { [language: string]: Thenable }; + private readonly _map: { [language: string]: ITokenizationSupport }; + private readonly _promises: { [language: string]: Thenable }; private readonly _onDidChange = new Emitter(); public readonly onDidChange: Event = this._onDidChange.event; diff --git a/src/vs/editor/common/services/editorSimpleWorker.ts b/src/vs/editor/common/services/editorSimpleWorker.ts index 4ef83f744a7..acbc2fe7795 100644 --- a/src/vs/editor/common/services/editorSimpleWorker.ts +++ b/src/vs/editor/common/services/editorSimpleWorker.ts @@ -323,7 +323,7 @@ declare var require: any; * @internal */ export abstract class BaseEditorSimpleWorker { - private _foreignModuleFactory: IForeignModuleFactory | null; + private readonly _foreignModuleFactory: IForeignModuleFactory | null; private _foreignModule: any; constructor(foreignModuleFactory: IForeignModuleFactory | null) { @@ -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/editorWorkerServiceImpl.ts b/src/vs/editor/common/services/editorWorkerServiceImpl.ts index 501de316cb7..d427f63ebad 100644 --- a/src/vs/editor/common/services/editorWorkerServiceImpl.ts +++ b/src/vs/editor/common/services/editorWorkerServiceImpl.ts @@ -146,7 +146,7 @@ class WordBasedCompletionItemProvider implements modes.CompletionItemProvider { class WorkerManager extends Disposable { - private _modelService: IModelService; + private readonly _modelService: IModelService; private _editorWorkerClient: EditorWorkerClient | null; private _lastWorkerUsedTime: number; @@ -211,8 +211,8 @@ class WorkerManager extends Disposable { class EditorModelManager extends Disposable { - private _proxy: EditorSimpleWorkerImpl; - private _modelService: IModelService; + private readonly _proxy: EditorSimpleWorkerImpl; + private readonly _modelService: IModelService; private _syncedModels: { [modelUrl: string]: IDisposable[]; } = Object.create(null); private _syncedModelsLastUsedTime: { [modelUrl: string]: number; } = Object.create(null); @@ -312,8 +312,8 @@ interface IWorkerClient { } class SynchronousWorkerClient implements IWorkerClient { - private _instance: T; - private _proxyObj: Promise; + private readonly _instance: T; + private readonly _proxyObj: Promise; constructor(instance: T) { this._instance = instance; @@ -331,9 +331,9 @@ class SynchronousWorkerClient implements IWorkerClient export class EditorWorkerClient extends Disposable { - private _modelService: IModelService; + private readonly _modelService: IModelService; private _worker: IWorkerClient | null; - private _workerFactory: DefaultWorkerFactory; + private readonly _workerFactory: DefaultWorkerFactory; private _modelManager: EditorModelManager | null; constructor(modelService: IModelService, label: string | undefined) { diff --git a/src/vs/editor/common/services/languagesRegistry.ts b/src/vs/editor/common/services/languagesRegistry.ts index 5534b442510..a57f2096f36 100644 --- a/src/vs/editor/common/services/languagesRegistry.ts +++ b/src/vs/editor/common/services/languagesRegistry.ts @@ -36,8 +36,8 @@ export class LanguagesRegistry extends Disposable { private readonly _warnOnOverwrite: boolean; private _nextLanguageId2: number; - private _languageIdToLanguage: string[]; - private _languageToLanguageId: { [id: string]: number; }; + private readonly _languageIdToLanguage: string[]; + private readonly _languageToLanguageId: { [id: string]: number; }; private _languages: { [id: string]: IResolvedLanguage; }; private _mimeTypesMap: { [mimeType: string]: LanguageIdentifier; }; 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/modelServiceImpl.ts b/src/vs/editor/common/services/modelServiceImpl.ts index 135d4303ac4..4357d27d9c7 100644 --- a/src/vs/editor/common/services/modelServiceImpl.ts +++ b/src/vs/editor/common/services/modelServiceImpl.ts @@ -91,9 +91,9 @@ const DEFAULT_EOL = (platform.isLinux || platform.isMacintosh) ? DefaultEndOfLin export class ModelServiceImpl extends Disposable implements IModelService { public _serviceBrand: any; - private _configurationService: IConfigurationService; - private _configurationServiceSubscription: IDisposable; - private _resourcePropertiesService: ITextResourcePropertiesService; + private readonly _configurationService: IConfigurationService; + private readonly _configurationServiceSubscription: IDisposable; + private readonly _resourcePropertiesService: ITextResourcePropertiesService; private readonly _onModelAdded: Emitter = this._register(new Emitter()); public readonly onModelAdded: Event = this._onModelAdded.event; @@ -111,7 +111,7 @@ export class ModelServiceImpl extends Disposable implements IModelService { /** * All the models known in the system. */ - private _models: { [modelId: string]: ModelData; }; + private readonly _models: { [modelId: string]: ModelData; }; constructor( @IConfigurationService configurationService: IConfigurationService, diff --git a/src/vs/editor/common/services/resolverService.ts b/src/vs/editor/common/services/resolverService.ts index 2b04d4b1218..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 { @@ -49,6 +49,10 @@ export interface ITextEditorModel extends IEditorModel { isReadonly(): boolean; } -export interface IActiveTextEditorModel extends ITextEditorModel { +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/common/services/webWorker.ts b/src/vs/editor/common/services/webWorker.ts index 4e54ac849a4..a75cd977352 100644 --- a/src/vs/editor/common/services/webWorker.ts +++ b/src/vs/editor/common/services/webWorker.ts @@ -52,7 +52,7 @@ export interface IWebWorkerOptions { class MonacoWebWorkerImpl extends EditorWorkerClient implements MonacoWebWorker { - private _foreignModuleId: string; + private readonly _foreignModuleId: string; private _foreignModuleCreateData: any | null; private _foreignProxy: Promise | null; diff --git a/src/vs/editor/common/view/overviewZoneManager.ts b/src/vs/editor/common/view/overviewZoneManager.ts index 24c204f5c13..c34c29d7f2e 100644 --- a/src/vs/editor/common/view/overviewZoneManager.ts +++ b/src/vs/editor/common/view/overviewZoneManager.ts @@ -75,7 +75,7 @@ export class OverviewRulerZone { export class OverviewZoneManager { - private _getVerticalOffsetForLine: (lineNumber: number) => number; + private readonly _getVerticalOffsetForLine: (lineNumber: number) => number; private _zones: OverviewRulerZone[]; private _colorZonesInvalid: boolean; private _lineHeight: number; @@ -85,8 +85,8 @@ export class OverviewZoneManager { private _pixelRatio: number; private _lastAssignedId: number; - private _color2Id: { [color: string]: number; }; - private _id2Color: string[]; + private readonly _color2Id: { [color: string]: number; }; + private readonly _id2Color: string[]; constructor(getVerticalOffsetForLine: (lineNumber: number) => number) { this._getVerticalOffsetForLine = getVerticalOffsetForLine; diff --git a/src/vs/editor/common/view/viewEventDispatcher.ts b/src/vs/editor/common/view/viewEventDispatcher.ts index 47e26f7916d..54bd7a6e986 100644 --- a/src/vs/editor/common/view/viewEventDispatcher.ts +++ b/src/vs/editor/common/view/viewEventDispatcher.ts @@ -8,8 +8,8 @@ import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler'; export class ViewEventDispatcher { - private _eventHandlerGateKeeper: (callback: () => void) => void; - private _eventHandlers: ViewEventHandler[]; + private readonly _eventHandlerGateKeeper: (callback: () => void) => void; + private readonly _eventHandlers: ViewEventHandler[]; private _eventQueue: ViewEvent[] | null; private _isConsumingQueue: boolean; diff --git a/src/vs/editor/common/viewLayout/lineDecorations.ts b/src/vs/editor/common/viewLayout/lineDecorations.ts index ed6cfcfe507..5e79341a942 100644 --- a/src/vs/editor/common/viewLayout/lineDecorations.ts +++ b/src/vs/editor/common/viewLayout/lineDecorations.ts @@ -102,8 +102,8 @@ export class DecorationSegment { class Stack { public count: number; - private stopOffsets: number[]; - private classNames: string[]; + private readonly stopOffsets: number[]; + private readonly classNames: string[]; constructor() { this.stopOffsets = []; diff --git a/src/vs/editor/common/viewLayout/linesLayout.ts b/src/vs/editor/common/viewLayout/linesLayout.ts index f48f66bae7e..2967fb3b003 100644 --- a/src/vs/editor/common/viewLayout/linesLayout.ts +++ b/src/vs/editor/common/viewLayout/linesLayout.ts @@ -30,7 +30,7 @@ export class LinesLayout { /** * Contains whitespace information in pixels */ - private _whitespaces: WhitespaceComputer; + private readonly _whitespaces: WhitespaceComputer; constructor(lineCount: number, lineHeight: number) { this._lineCount = lineCount; diff --git a/src/vs/editor/common/viewLayout/whitespaceComputer.ts b/src/vs/editor/common/viewLayout/whitespaceComputer.ts index 1177a572199..537c36ac807 100644 --- a/src/vs/editor/common/viewLayout/whitespaceComputer.ts +++ b/src/vs/editor/common/viewLayout/whitespaceComputer.ts @@ -18,27 +18,27 @@ export class WhitespaceComputer { /** * heights[i] is the height in pixels for whitespace at index i */ - private _heights: number[]; + private readonly _heights: number[]; /** * minWidths[i] is the min width in pixels for whitespace at index i */ - private _minWidths: number[]; + private readonly _minWidths: number[]; /** * afterLineNumbers[i] is the line number whitespace at index i is after */ - private _afterLineNumbers: number[]; + private readonly _afterLineNumbers: number[]; /** * ordinals[i] is the orinal of the whitespace at index i */ - private _ordinals: number[]; + private readonly _ordinals: number[]; /** * prefixSum[i] = SUM(heights[j]), 1 <= j <= i */ - private _prefixSum: number[]; + private readonly _prefixSum: number[]; /** * prefixSum[i], 1 <= i <= prefixSumValidIndex can be trusted @@ -48,12 +48,12 @@ export class WhitespaceComputer { /** * ids[i] is the whitespace id of whitespace at index i */ - private _ids: number[]; + private readonly _ids: number[]; /** * index at which a whitespace is positioned (inside heights, afterLineNumbers, prefixSum members) */ - private _whitespaceId2Index: { + private readonly _whitespaceId2Index: { [id: string]: number; }; diff --git a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts index 040c0108e56..9833c7c50c1 100644 --- a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts +++ b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts @@ -56,7 +56,7 @@ class WrappingCharacterClassifier extends CharacterClassifier { export class CharacterHardWrappingLineMapperFactory implements ILineMapperFactory { - private classifier: WrappingCharacterClassifier; + private readonly classifier: WrappingCharacterClassifier; constructor(breakBeforeChars: string, breakAfterChars: string, breakObtrusiveChars: string) { this.classifier = new WrappingCharacterClassifier(breakBeforeChars, breakAfterChars, breakObtrusiveChars); @@ -255,8 +255,8 @@ export class CharacterHardWrappingLineMapperFactory implements ILineMapperFactor export class CharacterHardWrappingLineMapping implements ILineMapping { - private _prefixSums: PrefixSumComputer; - private _wrappedLinesIndent: string; + private readonly _prefixSums: PrefixSumComputer; + private readonly _wrappedLinesIndent: string; constructor(prefixSums: PrefixSumComputer, wrappedLinesIndent: string) { this._prefixSums = prefixSums; diff --git a/src/vs/editor/common/viewModel/prefixSumComputer.ts b/src/vs/editor/common/viewModel/prefixSumComputer.ts index e3144d866dc..81e66ac5059 100644 --- a/src/vs/editor/common/viewModel/prefixSumComputer.ts +++ b/src/vs/editor/common/viewModel/prefixSumComputer.ts @@ -32,7 +32,7 @@ export class PrefixSumComputer { /** * prefixSum[i], 0 <= i <= prefixSumValidIndex can be trusted */ - private prefixSumValidIndex: Int32Array; + private readonly prefixSumValidIndex: Int32Array; constructor(values: Uint32Array) { this.values = values; diff --git a/src/vs/editor/common/viewModel/splitLinesCollection.ts b/src/vs/editor/common/viewModel/splitLinesCollection.ts index 13bf1b6f800..75b8b3739e5 100644 --- a/src/vs/editor/common/viewModel/splitLinesCollection.ts +++ b/src/vs/editor/common/viewModel/splitLinesCollection.ts @@ -149,7 +149,7 @@ const enum IndentGuideRepeatOption { export class SplitLinesCollection implements IViewModelLinesCollection { - private model: ITextModel; + private readonly model: ITextModel; private _validModelVersionId: number; private wrappingColumn: number; @@ -160,7 +160,7 @@ export class SplitLinesCollection implements IViewModelLinesCollection { private prefixSumComputer: PrefixSumComputerWithCache; - private linePositionMapperFactory: ILineMapperFactory; + private readonly linePositionMapperFactory: ILineMapperFactory; private hiddenAreasIds: string[]; @@ -1001,11 +1001,11 @@ class InvisibleIdentitySplitLine implements ISplitLine { export class SplitLine implements ISplitLine { - private positionMapper: ILineMapping; - private outputLineCount: number; + private readonly positionMapper: ILineMapping; + private readonly outputLineCount: number; - private wrappedIndent: string; - private wrappedIndentLength: number; + private readonly wrappedIndent: string; + private readonly wrappedIndentLength: number; private _isVisible: boolean; constructor(positionMapper: ILineMapping, isVisible: boolean) { diff --git a/src/vs/editor/contrib/bracketMatching/bracketMatching.ts b/src/vs/editor/contrib/bracketMatching/bracketMatching.ts index 62dea606561..672b156378a 100644 --- a/src/vs/editor/contrib/bracketMatching/bracketMatching.ts +++ b/src/vs/editor/contrib/bracketMatching/bracketMatching.ts @@ -92,7 +92,7 @@ export class BracketMatchingController extends Disposable implements editorCommo private _lastBracketsData: BracketsData[]; private _lastVersionId: number; private _decorations: string[]; - private _updateBracketsSoon: RunOnceScheduler; + private readonly _updateBracketsSoon: RunOnceScheduler; private _matchBrackets: boolean; constructor( diff --git a/src/vs/editor/contrib/caretOperations/caretOperations.ts b/src/vs/editor/contrib/caretOperations/caretOperations.ts index f3a753dbc7b..3b715b871e7 100644 --- a/src/vs/editor/contrib/caretOperations/caretOperations.ts +++ b/src/vs/editor/contrib/caretOperations/caretOperations.ts @@ -12,7 +12,7 @@ import { MoveCaretCommand } from 'vs/editor/contrib/caretOperations/moveCaretCom class MoveCaretAction extends EditorAction { - private left: boolean; + private readonly left: boolean; constructor(left: boolean, opts: IActionOptions) { super(opts); diff --git a/src/vs/editor/contrib/caretOperations/moveCaretCommand.ts b/src/vs/editor/contrib/caretOperations/moveCaretCommand.ts index 0c55d207603..d992856345c 100644 --- a/src/vs/editor/contrib/caretOperations/moveCaretCommand.ts +++ b/src/vs/editor/contrib/caretOperations/moveCaretCommand.ts @@ -10,8 +10,8 @@ import { ITextModel } from 'vs/editor/common/model'; export class MoveCaretCommand implements ICommand { - private _selection: Selection; - private _isMovingLeft: boolean; + private readonly _selection: Selection; + private readonly _isMovingLeft: boolean; private _cutStartIndex: number; private _cutEndIndex: number; diff --git a/src/vs/editor/contrib/clipboard/clipboard.ts b/src/vs/editor/contrib/clipboard/clipboard.ts index 5e43f1b39ef..990be3a2987 100644 --- a/src/vs/editor/contrib/clipboard/clipboard.ts +++ b/src/vs/editor/contrib/clipboard/clipboard.ts @@ -32,7 +32,7 @@ type ExecCommand = 'cut' | 'copy' | 'paste'; abstract class ExecCommandAction extends EditorAction { - private browserCommand: ExecCommand; + private readonly browserCommand: ExecCommand; constructor(browserCommand: ExecCommand, opts: IActionOptions) { super(opts); diff --git a/src/vs/editor/contrib/codeAction/codeActionCommands.ts b/src/vs/editor/contrib/codeAction/codeActionCommands.ts index 4c5ee4309ca..76a522e6a55 100644 --- a/src/vs/editor/contrib/codeAction/codeActionCommands.ts +++ b/src/vs/editor/contrib/codeAction/codeActionCommands.ts @@ -42,11 +42,11 @@ export class QuickFixController implements IEditorContribution { return editor.getContribution(QuickFixController.ID); } - private _editor: ICodeEditor; - private _model: CodeActionModel; - private _codeActionContextMenu: CodeActionContextMenu; - private _lightBulbWidget: LightBulbWidget; - private _disposables: IDisposable[] = []; + private readonly _editor: ICodeEditor; + private readonly _model: CodeActionModel; + private readonly _codeActionContextMenu: CodeActionContextMenu; + private readonly _lightBulbWidget: LightBulbWidget; + private readonly _disposables: IDisposable[] = []; private _activeRequest: CancelablePromise | undefined; diff --git a/src/vs/editor/contrib/codeAction/codeActionModel.ts b/src/vs/editor/contrib/codeAction/codeActionModel.ts index 5673569e465..f252c75ef8a 100644 --- a/src/vs/editor/contrib/codeAction/codeActionModel.ts +++ b/src/vs/editor/contrib/codeAction/codeActionModel.ts @@ -26,9 +26,9 @@ export class CodeActionOracle { private readonly _autoTriggerTimer = new TimeoutTimer(); constructor( - private _editor: ICodeEditor, + private readonly _editor: ICodeEditor, private readonly _markerService: IMarkerService, - private _signalChange: (newState: CodeActionsState.State) => void, + private readonly _signalChange: (newState: CodeActionsState.State) => void, private readonly _delay: number = 250, private readonly _progressService?: IProgressService, ) { diff --git a/src/vs/editor/contrib/codelens/codelensController.ts b/src/vs/editor/contrib/codelens/codelensController.ts index 924c840ea3f..f1681995636 100644 --- a/src/vs/editor/contrib/codelens/codelensController.ts +++ b/src/vs/editor/contrib/codelens/codelensController.ts @@ -33,7 +33,7 @@ export class CodeLensContribution implements editorCommon.IEditorContribution { private _detectVisibleLenses: RunOnceScheduler; constructor( - private _editor: editorBrowser.ICodeEditor, + private readonly _editor: editorBrowser.ICodeEditor, @ICommandService private readonly _commandService: ICommandService, @INotificationService private readonly _notificationService: INotificationService ) { diff --git a/src/vs/editor/contrib/codelens/codelensWidget.ts b/src/vs/editor/contrib/codelens/codelensWidget.ts index e9f8892a1d2..19265ebabc2 100644 --- a/src/vs/editor/contrib/codelens/codelensWidget.ts +++ b/src/vs/editor/contrib/codelens/codelensWidget.ts @@ -26,7 +26,7 @@ class CodeLensViewZone implements editorBrowser.IViewZone { afterLineNumber: number; private _lastHeight: number; - private _onHeight: Function; + private readonly _onHeight: Function; constructor(afterLineNumber: number, onHeight: Function) { this.afterLineNumber = afterLineNumber; @@ -164,9 +164,9 @@ export interface IDecorationIdCallback { export class CodeLensHelper { - private _removeDecorations: string[]; - private _addDecorations: IModelDeltaDecoration[]; - private _addDecorationsCallbacks: IDecorationIdCallback[]; + private readonly _removeDecorations: string[]; + private readonly _addDecorations: IModelDeltaDecoration[]; + private readonly _addDecorationsCallbacks: IDecorationIdCallback[]; constructor() { this._removeDecorations = []; diff --git a/src/vs/editor/contrib/colorPicker/colorDetector.ts b/src/vs/editor/contrib/colorPicker/colorDetector.ts index 02be8db0cae..1de0bda0312 100644 --- a/src/vs/editor/contrib/colorPicker/colorDetector.ts +++ b/src/vs/editor/contrib/colorPicker/colorDetector.ts @@ -37,11 +37,11 @@ export class ColorDetector implements IEditorContribution { private _colorDatas = new Map(); private _colorDecoratorIds: string[] = []; - private _decorationsTypes: { [key: string]: boolean } = {}; + private readonly _decorationsTypes: { [key: string]: boolean } = {}; private _isEnabled: boolean; - constructor(private _editor: ICodeEditor, + constructor(private readonly _editor: ICodeEditor, @ICodeEditorService private readonly _codeEditorService: ICodeEditorService, @IConfigurationService private readonly _configurationService: IConfigurationService ) { diff --git a/src/vs/editor/contrib/colorPicker/colorPickerWidget.ts b/src/vs/editor/contrib/colorPicker/colorPickerWidget.ts index 30536ea190f..e225e6a844e 100644 --- a/src/vs/editor/contrib/colorPicker/colorPickerWidget.ts +++ b/src/vs/editor/contrib/colorPicker/colorPickerWidget.ts @@ -19,11 +19,11 @@ const $ = dom.$; export class ColorPickerHeader extends Disposable { - private domNode: HTMLElement; - private pickedColorNode: HTMLElement; + private readonly domNode: HTMLElement; + private readonly pickedColorNode: HTMLElement; private backgroundColor: Color; - constructor(container: HTMLElement, private model: ColorPickerModel, themeService: IThemeService) { + constructor(container: HTMLElement, private readonly model: ColorPickerModel, themeService: IThemeService) { super(); this.domNode = $('.colorpicker-header'); @@ -63,12 +63,12 @@ export class ColorPickerHeader extends Disposable { export class ColorPickerBody extends Disposable { - private domNode: HTMLElement; - private saturationBox: SaturationBox; - private hueStrip: Strip; - private opacityStrip: Strip; + private readonly domNode: HTMLElement; + private readonly saturationBox: SaturationBox; + private readonly hueStrip: Strip; + private readonly opacityStrip: Strip; - constructor(container: HTMLElement, private model: ColorPickerModel, private pixelRatio: number) { + constructor(container: HTMLElement, private readonly model: ColorPickerModel, private pixelRatio: number) { super(); this.domNode = $('.colorpicker-body'); @@ -120,9 +120,9 @@ export class ColorPickerBody extends Disposable { class SaturationBox extends Disposable { - private domNode: HTMLElement; - private selection: HTMLElement; - private canvas: HTMLCanvasElement; + private readonly domNode: HTMLElement; + private readonly selection: HTMLElement; + private readonly canvas: HTMLCanvasElement; private width: number; private height: number; @@ -133,7 +133,7 @@ class SaturationBox extends Disposable { private _onColorFlushed = new Emitter(); readonly onColorFlushed: Event = this._onColorFlushed.event; - constructor(container: HTMLElement, private model: ColorPickerModel, private pixelRatio: number) { + constructor(container: HTMLElement, private readonly model: ColorPickerModel, private pixelRatio: number) { super(); this.domNode = $('.saturation-wrap'); @@ -335,7 +335,7 @@ export class ColorPickerWidget extends Widget { body: ColorPickerBody; - constructor(container: Node, private model: ColorPickerModel, private pixelRatio: number, themeService: IThemeService) { + constructor(container: Node, private readonly model: ColorPickerModel, private pixelRatio: number, themeService: IThemeService) { super(); this._register(onDidChangeZoomLevel(() => this.layout())); diff --git a/src/vs/editor/contrib/comment/blockCommentCommand.ts b/src/vs/editor/contrib/comment/blockCommentCommand.ts index 012b6243468..5c5273c9c35 100644 --- a/src/vs/editor/contrib/comment/blockCommentCommand.ts +++ b/src/vs/editor/contrib/comment/blockCommentCommand.ts @@ -14,7 +14,7 @@ import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageCo export class BlockCommentCommand implements editorCommon.ICommand { - private _selection: Selection; + private readonly _selection: Selection; private _usedEndToken: string | null; constructor(selection: Selection) { diff --git a/src/vs/editor/contrib/comment/comment.ts b/src/vs/editor/contrib/comment/comment.ts index 454e6073f51..5c6118a3f6f 100644 --- a/src/vs/editor/contrib/comment/comment.ts +++ b/src/vs/editor/contrib/comment/comment.ts @@ -16,7 +16,7 @@ import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegis abstract class CommentLineAction extends EditorAction { - private _type: Type; + private readonly _type: Type; constructor(type: Type, opts: IActionOptions) { super(opts); diff --git a/src/vs/editor/contrib/comment/lineCommentCommand.ts b/src/vs/editor/contrib/comment/lineCommentCommand.ts index 5bc743096c7..26b3f9e9111 100644 --- a/src/vs/editor/contrib/comment/lineCommentCommand.ts +++ b/src/vs/editor/contrib/comment/lineCommentCommand.ts @@ -48,12 +48,12 @@ export const enum Type { export class LineCommentCommand implements editorCommon.ICommand { - private _selection: Selection; + private readonly _selection: Selection; private _selectionId: string; private _deltaColumn: number; private _moveEndPositionDown: boolean; - private _tabSize: number; - private _type: Type; + private readonly _tabSize: number; + private readonly _type: Type; constructor(selection: Selection, tabSize: number, type: Type) { this._selection = selection; diff --git a/src/vs/editor/contrib/contextmenu/contextmenu.ts b/src/vs/editor/contrib/contextmenu/contextmenu.ts index 0b3261cdff3..8a27b553e5e 100644 --- a/src/vs/editor/contrib/contextmenu/contextmenu.ts +++ b/src/vs/editor/contrib/contextmenu/contextmenu.ts @@ -32,7 +32,7 @@ export class ContextMenuController implements IEditorContribution { private _toDispose: IDisposable[] = []; private _contextMenuIsBeingShownCount: number = 0; - private _editor: ICodeEditor; + private readonly _editor: ICodeEditor; constructor( editor: ICodeEditor, diff --git a/src/vs/editor/contrib/dnd/dnd.ts b/src/vs/editor/contrib/dnd/dnd.ts index a716d8fa10d..44a5dd539e2 100644 --- a/src/vs/editor/contrib/dnd/dnd.ts +++ b/src/vs/editor/contrib/dnd/dnd.ts @@ -32,7 +32,7 @@ export class DragAndDropController implements editorCommon.IEditorContribution { private static readonly ID = 'editor.contrib.dragAndDrop'; - private _editor: ICodeEditor; + private readonly _editor: ICodeEditor; private _toUnhook: IDisposable[]; private _dragSelection: Selection | null; private _dndDecorationIds: string[]; diff --git a/src/vs/editor/contrib/dnd/dragAndDropCommand.ts b/src/vs/editor/contrib/dnd/dragAndDropCommand.ts index 9942dc85260..12d405648c9 100644 --- a/src/vs/editor/contrib/dnd/dragAndDropCommand.ts +++ b/src/vs/editor/contrib/dnd/dragAndDropCommand.ts @@ -12,10 +12,10 @@ import { ITextModel } from 'vs/editor/common/model'; export class DragAndDropCommand implements editorCommon.ICommand { - private selection: Selection; - private targetPosition: Position; + private readonly selection: Selection; + private readonly targetPosition: Position; private targetSelection: Selection; - private copy: boolean; + private readonly copy: boolean; constructor(selection: Selection, targetPosition: Position, copy: boolean) { this.selection = selection; diff --git a/src/vs/editor/contrib/documentSymbols/outlineModel.ts b/src/vs/editor/contrib/documentSymbols/outlineModel.ts index 179b23f3a11..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; } @@ -266,13 +274,17 @@ export class OutlineModel extends TreeElement { static _create(textModel: ITextModel, token: CancellationToken): Promise { - let result = new OutlineModel(textModel); - let promises = DocumentSymbolProviderRegistry.ordered(textModel).map((provider, index) => { + const chainedCancellation = new CancellationTokenSource(); + token.onCancellationRequested(() => chainedCancellation.cancel()); + + 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, token)).then(result => { + return Promise.resolve(provider.provideDocumentSymbols(result.textModel, chainedCancellation.token)).then(result => { for (const info of result || []) { OutlineModel._makeOutlineElement(info, group); } @@ -289,7 +301,22 @@ export class OutlineModel extends TreeElement { }); }); - return Promise.all(promises).then(() => result._compact()); + 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 { @@ -394,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/find/findController.ts b/src/vs/editor/contrib/find/findController.ts index 06d8c1bf07a..d51962cdfef 100644 --- a/src/vs/editor/contrib/find/findController.ts +++ b/src/vs/editor/contrib/find/findController.ts @@ -71,12 +71,12 @@ export class CommonFindController extends Disposable implements editorCommon.IEd private static readonly ID = 'editor.contrib.findController'; protected _editor: ICodeEditor; - private _findWidgetVisible: IContextKey; + private readonly _findWidgetVisible: IContextKey; protected _state: FindReplaceState; protected _updateHistoryDelayer: Delayer; private _model: FindModelBoundToEditorModel | null; - private _storageService: IStorageService; - private _clipboardService: IClipboardService; + private readonly _storageService: IStorageService; + private readonly _clipboardService: IClipboardService; protected readonly _contextKeyService: IContextKeyService; public static get(editor: ICodeEditor): CommonFindController { diff --git a/src/vs/editor/contrib/find/findDecorations.ts b/src/vs/editor/contrib/find/findDecorations.ts index 6c90044a369..bb115977387 100644 --- a/src/vs/editor/contrib/find/findDecorations.ts +++ b/src/vs/editor/contrib/find/findDecorations.ts @@ -14,7 +14,7 @@ import { themeColorFromId } from 'vs/platform/theme/common/themeService'; export class FindDecorations implements IDisposable { - private _editor: IActiveCodeEditor; + private readonly _editor: IActiveCodeEditor; private _decorations: string[]; private _overviewRulerApproximateDecorations: string[]; private _findScopeDecorationId: string | null; diff --git a/src/vs/editor/contrib/find/findModel.ts b/src/vs/editor/contrib/find/findModel.ts index 59ab1deb7e9..9264aa9b734 100644 --- a/src/vs/editor/contrib/find/findModel.ts +++ b/src/vs/editor/contrib/find/findModel.ts @@ -69,14 +69,14 @@ const RESEARCH_DELAY = 240; export class FindModelBoundToEditorModel { - private _editor: IActiveCodeEditor; - private _state: FindReplaceState; + private readonly _editor: IActiveCodeEditor; + private readonly _state: FindReplaceState; private _toDispose: IDisposable[]; - private _decorations: FindDecorations; + private readonly _decorations: FindDecorations; private _ignoreModelContentChanged: boolean; - private _startSearchingTimer: TimeoutTimer; + private readonly _startSearchingTimer: TimeoutTimer; - private _updateDecorationsScheduler: RunOnceScheduler; + private readonly _updateDecorationsScheduler: RunOnceScheduler; private _isDisposed: boolean; constructor(editor: IActiveCodeEditor, state: FindReplaceState) { diff --git a/src/vs/editor/contrib/find/findOptionsWidget.ts b/src/vs/editor/contrib/find/findOptionsWidget.ts index 768bb6adaeb..8a61aabd766 100644 --- a/src/vs/editor/contrib/find/findOptionsWidget.ts +++ b/src/vs/editor/contrib/find/findOptionsWidget.ts @@ -18,14 +18,14 @@ export class FindOptionsWidget extends Widget implements IOverlayWidget { private static readonly ID = 'editor.contrib.findOptionsWidget'; - private _editor: ICodeEditor; - private _state: FindReplaceState; - private _keybindingService: IKeybindingService; + private readonly _editor: ICodeEditor; + private readonly _state: FindReplaceState; + private readonly _keybindingService: IKeybindingService; - private _domNode: HTMLElement; - private regex: RegexCheckbox; - private wholeWords: WholeWordsCheckbox; - private caseSensitive: CaseSensitiveCheckbox; + private readonly _domNode: HTMLElement; + private readonly regex: RegexCheckbox; + private readonly wholeWords: WholeWordsCheckbox; + private readonly caseSensitive: CaseSensitiveCheckbox; constructor( editor: ICodeEditor, diff --git a/src/vs/editor/contrib/find/findWidget.ts b/src/vs/editor/contrib/find/findWidget.ts index 15b4b0bf4d3..2c9046b5298 100644 --- a/src/vs/editor/contrib/find/findWidget.ts +++ b/src/vs/editor/contrib/find/findWidget.ts @@ -84,8 +84,8 @@ export class FindWidgetViewZone implements IViewZone { export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSashLayoutProvider { private static readonly ID = 'editor.contrib.findWidget'; private readonly _codeEditor: ICodeEditor; - private _state: FindReplaceState; - private _controller: IFindController; + private readonly _state: FindReplaceState; + private readonly _controller: IFindController; private readonly _contextViewProvider: IContextViewProvider; private readonly _keybindingService: IKeybindingService; private readonly _contextKeyService: IContextKeyService; @@ -107,16 +107,16 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas private _isReplaceVisible: boolean; private _ignoreChangeEvent: boolean; - private _findFocusTracker: dom.IFocusTracker; - private _findInputFocused: IContextKey; - private _replaceFocusTracker: dom.IFocusTracker; - private _replaceInputFocused: IContextKey; + private readonly _findFocusTracker: dom.IFocusTracker; + private readonly _findInputFocused: IContextKey; + private readonly _replaceFocusTracker: dom.IFocusTracker; + private readonly _replaceInputFocused: IContextKey; private _viewZone?: FindWidgetViewZone; private _viewZoneId?: number; private _resizeSash: Sash; private _resized: boolean; - private _updateHistoryDelayer: Delayer; + private readonly _updateHistoryDelayer: Delayer; constructor( codeEditor: ICodeEditor, diff --git a/src/vs/editor/contrib/find/replaceAllCommand.ts b/src/vs/editor/contrib/find/replaceAllCommand.ts index 304fbcb4c6a..1b542d6cb0b 100644 --- a/src/vs/editor/contrib/find/replaceAllCommand.ts +++ b/src/vs/editor/contrib/find/replaceAllCommand.ts @@ -15,10 +15,10 @@ interface IEditOperation { export class ReplaceAllCommand implements editorCommon.ICommand { - private _editorSelection: Selection; + private readonly _editorSelection: Selection; private _trackedEditorSelectionId: string; - private _ranges: Range[]; - private _replaceStrings: string[]; + private readonly _ranges: Range[]; + private readonly _replaceStrings: string[]; constructor(editorSelection: Selection, ranges: Range[], replaceStrings: string[]) { this._editorSelection = editorSelection; diff --git a/src/vs/editor/contrib/find/simpleFindWidget.ts b/src/vs/editor/contrib/find/simpleFindWidget.ts index 3334c2fa6bf..40371f2c9fe 100644 --- a/src/vs/editor/contrib/find/simpleFindWidget.ts +++ b/src/vs/editor/contrib/find/simpleFindWidget.ts @@ -25,13 +25,13 @@ const NLS_NEXT_MATCH_BTN_LABEL = nls.localize('label.nextMatchButton', "Next mat const NLS_CLOSE_BTN_LABEL = nls.localize('label.closeButton', "Close"); export abstract class SimpleFindWidget extends Widget { - private _findInput: FindInput; - private _domNode: HTMLElement; - private _innerDomNode: HTMLElement; + private readonly _findInput: FindInput; + private readonly _domNode: HTMLElement; + private readonly _innerDomNode: HTMLElement; private _isVisible: boolean = false; - private _focusTracker: dom.IFocusTracker; - private _findInputFocusTracker: dom.IFocusTracker; - private _updateHistoryDelayer: Delayer; + private readonly _focusTracker: dom.IFocusTracker; + private readonly _findInputFocusTracker: dom.IFocusTracker; + private readonly _updateHistoryDelayer: Delayer; constructor( @IContextViewService private readonly _contextViewService: IContextViewService, diff --git a/src/vs/editor/contrib/folding/folding.ts b/src/vs/editor/contrib/folding/folding.ts index e8393a98ce2..24d64b50225 100644 --- a/src/vs/editor/contrib/folding/folding.ts +++ b/src/vs/editor/contrib/folding/folding.ts @@ -55,12 +55,12 @@ export class FoldingController implements IEditorContribution { return editor.getContribution(ID); } - private editor: ICodeEditor; + private readonly editor: ICodeEditor; private _isEnabled: boolean; private _autoHideFoldingControls: boolean; private _useFoldingProviders: boolean; - private foldingDecorationProvider: FoldingDecorationProvider; + private readonly foldingDecorationProvider: FoldingDecorationProvider; private foldingModel: FoldingModel | null; private hiddenRangeModel: HiddenRangeModel | null; diff --git a/src/vs/editor/contrib/folding/foldingDecorations.ts b/src/vs/editor/contrib/folding/foldingDecorations.ts index 17359b411fb..4b55e86f6fa 100644 --- a/src/vs/editor/contrib/folding/foldingDecorations.ts +++ b/src/vs/editor/contrib/folding/foldingDecorations.ts @@ -28,7 +28,7 @@ export class FoldingDecorationProvider implements IDecorationProvider { public autoHideFoldingControls: boolean = true; - constructor(private editor: ICodeEditor) { + constructor(private readonly editor: ICodeEditor) { } getDecorationOption(isCollapsed: boolean): ModelDecorationOptions { diff --git a/src/vs/editor/contrib/folding/foldingModel.ts b/src/vs/editor/contrib/folding/foldingModel.ts index da25d5193dd..eb0db3e5b3a 100644 --- a/src/vs/editor/contrib/folding/foldingModel.ts +++ b/src/vs/editor/contrib/folding/foldingModel.ts @@ -21,8 +21,8 @@ export interface FoldingModelChangeEvent { export type CollapseMemento = ILineRange[]; export class FoldingModel { - private _textModel: ITextModel; - private _decorationProvider: IDecorationProvider; + private readonly _textModel: ITextModel; + private readonly _decorationProvider: IDecorationProvider; private _regions: FoldingRegions; private _editorDecorationIds: string[]; diff --git a/src/vs/editor/contrib/folding/foldingRanges.ts b/src/vs/editor/contrib/folding/foldingRanges.ts index bdd2558322d..e8a7cd7adef 100644 --- a/src/vs/editor/contrib/folding/foldingRanges.ts +++ b/src/vs/editor/contrib/folding/foldingRanges.ts @@ -14,11 +14,11 @@ export const MAX_LINE_NUMBER = 0xFFFFFF; const MASK_INDENT = 0xFF000000; export class FoldingRegions { - private _startIndexes: Uint32Array; - private _endIndexes: Uint32Array; - private _collapseStates: Uint32Array; + private readonly _startIndexes: Uint32Array; + private readonly _endIndexes: Uint32Array; + private readonly _collapseStates: Uint32Array; private _parentsComputed: boolean; - private _types: Array | undefined; + private readonly _types: Array | undefined; constructor(startIndexes: Uint32Array, endIndexes: Uint32Array, types?: Array) { if (startIndexes.length !== endIndexes.length || startIndexes.length > MAX_FOLDING_REGIONS) { @@ -154,7 +154,7 @@ export class FoldingRegions { export class FoldingRegion { - constructor(private ranges: FoldingRegions, private index: number) { + constructor(private readonly ranges: FoldingRegions, private index: number) { } public get startLineNumber() { diff --git a/src/vs/editor/contrib/folding/hiddenRangeModel.ts b/src/vs/editor/contrib/folding/hiddenRangeModel.ts index d93e3c6b823..96205183819 100644 --- a/src/vs/editor/contrib/folding/hiddenRangeModel.ts +++ b/src/vs/editor/contrib/folding/hiddenRangeModel.ts @@ -11,7 +11,7 @@ import { Selection } from 'vs/editor/common/core/selection'; import { findFirstInSorted } from 'vs/base/common/arrays'; export class HiddenRangeModel { - private _foldingModel: FoldingModel; + private readonly _foldingModel: FoldingModel; private _hiddenRanges: IRange[]; private _foldingModelListener: IDisposable | null; private _updateEventEmitter = new Emitter(); diff --git a/src/vs/editor/contrib/folding/indentRangeProvider.ts b/src/vs/editor/contrib/folding/indentRangeProvider.ts index 894482f017d..266a104d30a 100644 --- a/src/vs/editor/contrib/folding/indentRangeProvider.ts +++ b/src/vs/editor/contrib/folding/indentRangeProvider.ts @@ -20,7 +20,7 @@ export class IndentRangeProvider implements RangeProvider { readonly decorations; - constructor(private editorModel: ITextModel) { + constructor(private readonly editorModel: ITextModel) { } dispose() { @@ -36,11 +36,11 @@ export class IndentRangeProvider implements RangeProvider { // public only for testing export class RangesCollector { - private _startIndexes: number[]; - private _endIndexes: number[]; - private _indentOccurrences: number[]; + private readonly _startIndexes: number[]; + private readonly _endIndexes: number[]; + private readonly _indentOccurrences: number[]; private _length: number; - private _foldingRangesLimit: number; + private readonly _foldingRangesLimit: number; constructor(foldingRangesLimit: number) { this._startIndexes = []; diff --git a/src/vs/editor/contrib/folding/intializingRangeProvider.ts b/src/vs/editor/contrib/folding/intializingRangeProvider.ts index 35e4703a853..754ac39f048 100644 --- a/src/vs/editor/contrib/folding/intializingRangeProvider.ts +++ b/src/vs/editor/contrib/folding/intializingRangeProvider.ts @@ -17,7 +17,7 @@ export class InitializingRangeProvider implements RangeProvider { private decorationIds: string[] | undefined; private timeout: any; - constructor(private editorModel: ITextModel, initialRanges: ILineRange[], onTimeout: () => void, timeoutTime: number) { + constructor(private readonly editorModel: ITextModel, initialRanges: ILineRange[], onTimeout: () => void, timeoutTime: number) { if (initialRanges.length) { let toDecorationRange = (range: ILineRange): IModelDeltaDecoration => { return { diff --git a/src/vs/editor/contrib/folding/syntaxRangeProvider.ts b/src/vs/editor/contrib/folding/syntaxRangeProvider.ts index 745d3896403..4e66801be36 100644 --- a/src/vs/editor/contrib/folding/syntaxRangeProvider.ts +++ b/src/vs/editor/contrib/folding/syntaxRangeProvider.ts @@ -25,7 +25,7 @@ export class SyntaxRangeProvider implements RangeProvider { readonly id = ID_SYNTAX_PROVIDER; - constructor(private editorModel: ITextModel, private providers: FoldingRangeProvider[], private limit = MAX_FOLDING_REGIONS) { + constructor(private readonly editorModel: ITextModel, private providers: FoldingRangeProvider[], private limit = MAX_FOLDING_REGIONS) { } compute(cancellationToken: CancellationToken): Promise { @@ -69,13 +69,13 @@ function collectSyntaxRanges(providers: FoldingRangeProvider[], model: ITextMode } export class RangesCollector { - private _startIndexes: number[]; - private _endIndexes: number[]; - private _nestingLevels: number[]; - private _nestingLevelCounts: number[]; - private _types: Array; + private readonly _startIndexes: number[]; + private readonly _endIndexes: number[]; + private readonly _nestingLevels: number[]; + private readonly _nestingLevelCounts: number[]; + private readonly _types: Array; private _length: number; - private _foldingRangesLimit: number; + private readonly _foldingRangesLimit: number; constructor(foldingRangesLimit: number) { this._startIndexes = []; 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/goToDefinition/goToDefinitionMouse.ts b/src/vs/editor/contrib/goToDefinition/goToDefinitionMouse.ts index 339cc8c9076..f62e4b125d4 100644 --- a/src/vs/editor/contrib/goToDefinition/goToDefinitionMouse.ts +++ b/src/vs/editor/contrib/goToDefinition/goToDefinitionMouse.ts @@ -31,7 +31,7 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC private static readonly ID = 'editor.contrib.gotodefinitionwithmouse'; static MAX_SOURCE_PREVIEW_LINES = 8; - private editor: ICodeEditor; + private readonly editor: ICodeEditor; private toUnhook: IDisposable[]; private decorations: string[]; private currentWordUnderMouse: IWordAtPosition | null; diff --git a/src/vs/editor/contrib/gotoError/gotoError.ts b/src/vs/editor/contrib/gotoError/gotoError.ts index 8b9cc09fd3c..27fb71091ed 100644 --- a/src/vs/editor/contrib/gotoError/gotoError.ts +++ b/src/vs/editor/contrib/gotoError/gotoError.ts @@ -29,7 +29,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; class MarkerModel { - private _editor: ICodeEditor; + private readonly _editor: ICodeEditor; private _markers: IMarker[]; private _nextIdx: number; private _toUnbind: IDisposable[]; @@ -202,10 +202,10 @@ export class MarkerController implements editorCommon.IEditorContribution { return editor.getContribution(MarkerController.ID); } - private _editor: ICodeEditor; + private readonly _editor: ICodeEditor; private _model: MarkerModel | null; private _widget: MarkerNavigationWidget | null; - private _widgetVisible: IContextKey; + private readonly _widgetVisible: IContextKey; private _disposeOnClose: IDisposable[] = []; constructor( @@ -337,9 +337,9 @@ export class MarkerController implements editorCommon.IEditorContribution { class MarkerNavigationAction extends EditorAction { - private _isNext: boolean; + private readonly _isNext: boolean; - private _multiFile: boolean; + private readonly _multiFile: boolean; constructor(next: boolean, multiFile: boolean, opts: IActionOptions) { super(opts); diff --git a/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts b/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts index f841a39d5e8..2729e4aab8a 100644 --- a/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts +++ b/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts @@ -178,8 +178,8 @@ export class MarkerNavigationWidget extends PeekViewWidget { constructor( editor: ICodeEditor, - private actions: IAction[], - private _themeService: IThemeService + private readonly actions: IAction[], + private readonly _themeService: IThemeService ) { super(editor, { showArrow: true, showFrame: true, isAccessible: true }); this._severity = MarkerSeverity.Warning; diff --git a/src/vs/editor/contrib/hover/hover.ts b/src/vs/editor/contrib/hover/hover.ts index 12598460131..8c7f8bd5250 100644 --- a/src/vs/editor/contrib/hover/hover.ts +++ b/src/vs/editor/contrib/hover/hover.ts @@ -35,7 +35,7 @@ export class ModesHoverController implements IEditorContribution { private static readonly ID = 'editor.contrib.hover'; private _toUnhook: IDisposable[]; - private _didChangeConfigurationHandler: IDisposable; + private readonly _didChangeConfigurationHandler: IDisposable; private _contentWidget: ModesContentHoverWidget; private _glyphWidget: ModesGlyphHoverWidget; diff --git a/src/vs/editor/contrib/hover/hoverOperation.ts b/src/vs/editor/contrib/hover/hoverOperation.ts index 59c0b9f4366..46195dbeb5b 100644 --- a/src/vs/editor/contrib/hover/hoverOperation.ts +++ b/src/vs/editor/contrib/hover/hoverOperation.ts @@ -47,19 +47,19 @@ export const enum HoverStartMode { export class HoverOperation { - private _computer: IHoverComputer; + private readonly _computer: IHoverComputer; private _state: ComputeHoverOperationState; private _hoverTime: number; - private _firstWaitScheduler: RunOnceScheduler; - private _secondWaitScheduler: RunOnceScheduler; - private _loadingMessageScheduler: RunOnceScheduler; + private readonly _firstWaitScheduler: RunOnceScheduler; + private readonly _secondWaitScheduler: RunOnceScheduler; + private readonly _loadingMessageScheduler: RunOnceScheduler; private _asyncComputationPromise: CancelablePromise | null; private _asyncComputationPromiseDone: boolean; - private _completeCallback: (r: Result) => void; - private _errorCallback: ((err: any) => void) | null | undefined; - private _progressCallback: (progress: any) => void; + private readonly _completeCallback: (r: Result) => void; + private readonly _errorCallback: ((err: any) => void) | null | undefined; + private readonly _progressCallback: (progress: any) => void; constructor(computer: IHoverComputer, success: (r: Result) => void, error: ((err: any) => void) | null | undefined, progress: (progress: any) => void, hoverTime: number) { this._computer = computer; diff --git a/src/vs/editor/contrib/hover/hoverWidgets.ts b/src/vs/editor/contrib/hover/hoverWidgets.ts index 845999b7bf0..71452506714 100644 --- a/src/vs/editor/contrib/hover/hoverWidgets.ts +++ b/src/vs/editor/contrib/hover/hoverWidgets.ts @@ -16,15 +16,15 @@ import { Range } from 'vs/editor/common/core/range'; export class ContentHoverWidget extends Widget implements editorBrowser.IContentWidget { - private _id: string; + private readonly _id: string; protected _editor: editorBrowser.ICodeEditor; private _isVisible: boolean; - private _containerDomNode: HTMLElement; - private _domNode: HTMLElement; + private readonly _containerDomNode: HTMLElement; + private readonly _domNode: HTMLElement; protected _showAtPosition: Position | null; protected _showAtRange: Range | null; private _stoleFocus: boolean; - private scrollbar: DomScrollableElement; + private readonly scrollbar: DomScrollableElement; private disposables: IDisposable[] = []; // Editor.IContentWidget.allowEditorOverflow @@ -164,10 +164,10 @@ export class ContentHoverWidget extends Widget implements editorBrowser.IContent export class GlyphHoverWidget extends Widget implements editorBrowser.IOverlayWidget { - private _id: string; + private readonly _id: string; protected _editor: editorBrowser.ICodeEditor; private _isVisible: boolean; - private _domNode: HTMLElement; + private readonly _domNode: HTMLElement; protected _showAtLineNumber: number; constructor(id: string, editor: editorBrowser.ICodeEditor) { diff --git a/src/vs/editor/contrib/hover/modesContentHover.ts b/src/vs/editor/contrib/hover/modesContentHover.ts index bf6bab44890..d0f32f542eb 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'; @@ -64,13 +64,13 @@ type HoverPart = MarkdownHover | ColorHover | MarkerHover; class ModesContentComputer implements IHoverComputer { - private _editor: ICodeEditor; + private readonly _editor: ICodeEditor; private _result: HoverPart[]; private _range: Range | null; constructor( editor: ICodeEditor, - private _markerDecorationsService: IMarkerDecorationsService + private readonly _markerDecorationsService: IMarkerDecorationsService ) { this._editor = editor; this._range = null; @@ -202,8 +202,8 @@ export class ModesContentHoverWidget extends ContentHoverWidget { private _messages: HoverPart[]; private _lastRange: Range | null; - private _computer: ModesContentComputer; - private _hoverOperation: HoverOperation; + private readonly _computer: ModesContentComputer; + private readonly _hoverOperation: HoverOperation; private _highlightDecorations: string[]; private _isChangingDecorations: boolean; private _shouldFocus: boolean; @@ -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/hover/modesGlyphHover.ts b/src/vs/editor/contrib/hover/modesGlyphHover.ts index b5d3da484e9..27af478ad79 100644 --- a/src/vs/editor/contrib/hover/modesGlyphHover.ts +++ b/src/vs/editor/contrib/hover/modesGlyphHover.ts @@ -19,7 +19,7 @@ export interface IHoverMessage { class MarginComputer implements IHoverComputer { - private _editor: ICodeEditor; + private readonly _editor: ICodeEditor; private _lineNumber: number; private _result: IHoverMessage[]; @@ -91,9 +91,9 @@ export class ModesGlyphHoverWidget extends GlyphHoverWidget { private _messages: IHoverMessage[]; private _lastLineNumber: number; - private _markdownRenderer: MarkdownRenderer; - private _computer: MarginComputer; - private _hoverOperation: HoverOperation; + private readonly _markdownRenderer: MarkdownRenderer; + private readonly _computer: MarginComputer; + private readonly _hoverOperation: HoverOperation; private _renderDisposeables: IDisposable[]; constructor( diff --git a/src/vs/editor/contrib/inPlaceReplace/inPlaceReplaceCommand.ts b/src/vs/editor/contrib/inPlaceReplace/inPlaceReplaceCommand.ts index f092044a68c..a64be8e0109 100644 --- a/src/vs/editor/contrib/inPlaceReplace/inPlaceReplaceCommand.ts +++ b/src/vs/editor/contrib/inPlaceReplace/inPlaceReplaceCommand.ts @@ -10,9 +10,9 @@ import { ITextModel } from 'vs/editor/common/model'; export class InPlaceReplaceCommand implements editorCommon.ICommand { - private _editRange: Range; - private _originalSelection: Selection; - private _text: string; + private readonly _editRange: Range; + private readonly _originalSelection: Selection; + private readonly _text: string; constructor(editRange: Range, originalSelection: Selection, text: string) { this._editRange = editRange; diff --git a/src/vs/editor/contrib/indentation/indentation.ts b/src/vs/editor/contrib/indentation/indentation.ts index 826f53ed2d4..54edce87b07 100644 --- a/src/vs/editor/contrib/indentation/indentation.ts +++ b/src/vs/editor/contrib/indentation/indentation.ts @@ -205,7 +205,7 @@ export class IndentationToTabsAction extends EditorAction { export class ChangeIndentationSizeAction extends EditorAction { - constructor(private insertSpaces: boolean, opts: IActionOptions) { + constructor(private readonly insertSpaces: boolean, opts: IActionOptions) { super(opts); } @@ -375,9 +375,9 @@ export class ReindentSelectedLinesAction extends EditorAction { export class AutoIndentOnPasteCommand implements ICommand { - private _edits: { range: IRange; text: string; eol?: EndOfLineSequence; }[]; + private readonly _edits: { range: IRange; text: string; eol?: EndOfLineSequence; }[]; - private _initialSelection: Selection; + private readonly _initialSelection: Selection; private _selectionId: string; constructor(edits: TextEdit[], initialSelection: Selection) { @@ -422,7 +422,7 @@ export class AutoIndentOnPasteCommand implements ICommand { export class AutoIndentOnPaste implements IEditorContribution { private static readonly ID = 'editor.contrib.autoIndentOnPaste'; - private editor: ICodeEditor; + private readonly editor: ICodeEditor; private callOnDispose: IDisposable[]; private callOnModel: IDisposable[]; @@ -653,7 +653,7 @@ export class IndentationToSpacesCommand implements ICommand { private selectionId: string; - constructor(private selection: Selection, private tabSize: number) { } + constructor(private readonly selection: Selection, private tabSize: number) { } public getEditOperations(model: ITextModel, builder: IEditOperationBuilder): void { this.selectionId = builder.trackSelection(this.selection); @@ -669,7 +669,7 @@ export class IndentationToTabsCommand implements ICommand { private selectionId: string; - constructor(private selection: Selection, private tabSize: number) { } + constructor(private readonly selection: Selection, private tabSize: number) { } public getEditOperations(model: ITextModel, builder: IEditOperationBuilder): void { this.selectionId = builder.trackSelection(this.selection); diff --git a/src/vs/editor/contrib/linesOperations/copyLinesCommand.ts b/src/vs/editor/contrib/linesOperations/copyLinesCommand.ts index 37f4fb6381c..799b61088f2 100644 --- a/src/vs/editor/contrib/linesOperations/copyLinesCommand.ts +++ b/src/vs/editor/contrib/linesOperations/copyLinesCommand.ts @@ -10,8 +10,8 @@ import { ITextModel } from 'vs/editor/common/model'; export class CopyLinesCommand implements editorCommon.ICommand { - private _selection: Selection; - private _isCopyingDown: boolean; + private readonly _selection: Selection; + private readonly _isCopyingDown: boolean; private _selectionDirection: SelectionDirection; private _selectionId: string; diff --git a/src/vs/editor/contrib/linesOperations/linesOperations.ts b/src/vs/editor/contrib/linesOperations/linesOperations.ts index d1559b13f0c..ffba57fea6a 100644 --- a/src/vs/editor/contrib/linesOperations/linesOperations.ts +++ b/src/vs/editor/contrib/linesOperations/linesOperations.ts @@ -28,7 +28,7 @@ import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegis abstract class AbstractCopyLinesAction extends EditorAction { - private down: boolean; + private readonly down: boolean; constructor(down: boolean, opts: IActionOptions) { super(opts); @@ -100,7 +100,7 @@ class CopyLinesDownAction extends AbstractCopyLinesAction { abstract class AbstractMoveLinesAction extends EditorAction { - private down: boolean; + private readonly down: boolean; constructor(down: boolean, opts: IActionOptions) { super(opts); @@ -170,7 +170,7 @@ class MoveLinesDownAction extends AbstractMoveLinesAction { } export abstract class AbstractSortLinesAction extends EditorAction { - private descending: boolean; + private readonly descending: boolean; constructor(descending: boolean, opts: IActionOptions) { super(opts); diff --git a/src/vs/editor/contrib/linesOperations/moveLinesCommand.ts b/src/vs/editor/contrib/linesOperations/moveLinesCommand.ts index d237c21621b..839dee48043 100644 --- a/src/vs/editor/contrib/linesOperations/moveLinesCommand.ts +++ b/src/vs/editor/contrib/linesOperations/moveLinesCommand.ts @@ -16,9 +16,9 @@ import * as indentUtils from 'vs/editor/contrib/indentation/indentUtils'; export class MoveLinesCommand implements ICommand { - private _selection: Selection; - private _isMovingDown: boolean; - private _autoIndent: boolean; + private readonly _selection: Selection; + private readonly _isMovingDown: boolean; + private readonly _autoIndent: boolean; private _selectionId: string; private _moveEndPositionDown: boolean; diff --git a/src/vs/editor/contrib/linesOperations/sortLinesCommand.ts b/src/vs/editor/contrib/linesOperations/sortLinesCommand.ts index 779062027c4..304084db5f2 100644 --- a/src/vs/editor/contrib/linesOperations/sortLinesCommand.ts +++ b/src/vs/editor/contrib/linesOperations/sortLinesCommand.ts @@ -11,9 +11,9 @@ import { IIdentifiedSingleEditOperation, ITextModel } from 'vs/editor/common/mod export class SortLinesCommand implements editorCommon.ICommand { - private selection: Selection; + private readonly selection: Selection; private selectionId: string; - private descending: boolean; + private readonly descending: boolean; constructor(selection: Selection, descending: boolean) { this.selection = selection; diff --git a/src/vs/editor/contrib/links/getLinks.ts b/src/vs/editor/contrib/links/getLinks.ts index 933fd3f3121..91aac6c28a2 100644 --- a/src/vs/editor/contrib/links/getLinks.ts +++ b/src/vs/editor/contrib/links/getLinks.ts @@ -15,7 +15,7 @@ import { CommandsRegistry } from 'vs/platform/commands/common/commands'; export class Link implements ILink { private _link: ILink; - private _provider: LinkProvider; + private readonly _provider: LinkProvider; constructor(link: ILink, provider: LinkProvider) { this._link = link; diff --git a/src/vs/editor/contrib/links/links.ts b/src/vs/editor/contrib/links/links.ts index 972a60cd4c5..b3c5f8d0cb6 100644 --- a/src/vs/editor/contrib/links/links.ts +++ b/src/vs/editor/contrib/links/links.ts @@ -153,14 +153,14 @@ class LinkDetector implements editorCommon.IEditorContribution { static RECOMPUTE_TIME = 1000; // ms - private editor: ICodeEditor; + private readonly editor: ICodeEditor; private enabled: boolean; private listenersToRemove: IDisposable[]; - private timeout: async.TimeoutTimer; + private readonly timeout: async.TimeoutTimer; private computePromise: async.CancelablePromise | null; private activeLinkDecorationId: string | null; - private openerService: IOpenerService; - private notificationService: INotificationService; + private readonly openerService: IOpenerService; + private readonly notificationService: INotificationService; private currentOccurrences: { [decorationId: string]: LinkOccurrence; }; constructor( diff --git a/src/vs/editor/contrib/message/messageController.ts b/src/vs/editor/contrib/message/messageController.ts index 9ec3dfaad90..e522a12f8dc 100644 --- a/src/vs/editor/contrib/message/messageController.ts +++ b/src/vs/editor/contrib/message/messageController.ts @@ -33,8 +33,8 @@ export class MessageController extends Disposable implements editorCommon.IEdito return MessageController._id; } - private _editor: ICodeEditor; - private _visible: IContextKey; + private readonly _editor: ICodeEditor; + private readonly _visible: IContextKey; private _messageWidget: MessageWidget; private _messageListeners: IDisposable[] = []; @@ -125,9 +125,9 @@ class MessageWidget implements IContentWidget { readonly allowEditorOverflow = true; readonly suppressMouseDown = false; - private _editor: ICodeEditor; - private _position: IPosition; - private _domNode: HTMLDivElement; + private readonly _editor: ICodeEditor; + private readonly _position: IPosition; + private readonly _domNode: HTMLDivElement; static fadeOut(messageWidget: MessageWidget): IDisposable { let handle: any; diff --git a/src/vs/editor/contrib/multicursor/multicursor.ts b/src/vs/editor/contrib/multicursor/multicursor.ts index da531f7a83d..330d6811342 100644 --- a/src/vs/editor/contrib/multicursor/multicursor.ts +++ b/src/vs/editor/contrib/multicursor/multicursor.ts @@ -802,10 +802,10 @@ class SelectionHighlighterState { export class SelectionHighlighter extends Disposable implements IEditorContribution { private static readonly ID = 'editor.contrib.selectionHighlighter'; - private editor: ICodeEditor; + private readonly editor: ICodeEditor; private _isEnabled: boolean; private decorations: string[]; - private updateSoon: RunOnceScheduler; + private readonly updateSoon: RunOnceScheduler; private state: SelectionHighlighterState | null; constructor(editor: ICodeEditor) { diff --git a/src/vs/editor/contrib/parameterHints/parameterHintsModel.ts b/src/vs/editor/contrib/parameterHints/parameterHintsModel.ts index 3635b5c8948..cc34a7ec265 100644 --- a/src/vs/editor/contrib/parameterHints/parameterHintsModel.ts +++ b/src/vs/editor/contrib/parameterHints/parameterHintsModel.ts @@ -45,13 +45,13 @@ export class ParameterHintsModel extends Disposable { private readonly _onChangedHints = this._register(new Emitter()); public readonly onChangedHints = this._onChangedHints.event; - private editor: ICodeEditor; + private readonly editor: ICodeEditor; private enabled: boolean; private state: ParameterHintState.State = ParameterHintState.Default; private triggerChars = new CharacterSet(); private retriggerChars = new CharacterSet(); - private throttledDelayer: Delayer; + private readonly throttledDelayer: Delayer; private provideSignatureHelpRequest?: CancelablePromise; private triggerId = 0; diff --git a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts index d20abceae6a..5c365daebac 100644 --- a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts +++ b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts @@ -47,7 +47,7 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable { allowEditorOverflow = true; constructor( - private editor: ICodeEditor, + private readonly editor: ICodeEditor, @IContextKeyService contextKeyService: IContextKeyService, @IOpenerService openerService: IOpenerService, @IModeService modeService: IModeService, diff --git a/src/vs/editor/contrib/referenceSearch/referencesController.ts b/src/vs/editor/contrib/referenceSearch/referencesController.ts index 6cd7323f6c6..363e8c2cf0f 100644 --- a/src/vs/editor/contrib/referenceSearch/referencesController.ts +++ b/src/vs/editor/contrib/referenceSearch/referencesController.ts @@ -32,21 +32,21 @@ export abstract class ReferencesController implements editorCommon.IEditorContri private static readonly ID = 'editor.contrib.referencesController'; - private _editor: ICodeEditor; + private readonly _editor: ICodeEditor; private _widget: ReferenceWidget | null; private _model: ReferencesModel | null; private _requestIdPool = 0; private _disposables: IDisposable[] = []; private _ignoreModelChangeEvent = false; - private _referenceSearchVisible: IContextKey; + private readonly _referenceSearchVisible: IContextKey; public static get(editor: ICodeEditor): ReferencesController { return editor.getContribution(ReferencesController.ID); } public constructor( - private _defaultTreeKeyboardSupport: boolean, + private readonly _defaultTreeKeyboardSupport: boolean, editor: ICodeEditor, @IContextKeyService contextKeyService: IContextKeyService, @ICodeEditorService private readonly _editorService: ICodeEditorService, diff --git a/src/vs/editor/contrib/referenceSearch/referencesModel.ts b/src/vs/editor/contrib/referenceSearch/referencesModel.ts index 4243491a8b4..780e1421cf3 100644 --- a/src/vs/editor/contrib/referenceSearch/referencesModel.ts +++ b/src/vs/editor/contrib/referenceSearch/referencesModel.ts @@ -89,7 +89,7 @@ export class FileReferences implements IDisposable { private _resolved: boolean; private _loadFailure: any; - constructor(private readonly _parent: ReferencesModel, private _uri: URI) { + constructor(private readonly _parent: ReferencesModel, private readonly _uri: URI) { this._children = []; } 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 53cb0ed3c15..31cfa72a41c 100644 --- a/src/vs/editor/contrib/snippet/snippetSession.ts +++ b/src/vs/editor/contrib/snippet/snippetSession.ts @@ -197,10 +197,6 @@ export class OneSnippet { let ranges: Range[] | undefined; for (const placeholder of placeholdersWithEqualIndex) { - if (placeholder.isFinalTabstop) { - // ignore those - break; - } if (!ranges) { ranges = []; @@ -574,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/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/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..ac1323fe028 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -244,10 +244,10 @@ class SuggestionDetails { constructor( container: HTMLElement, - private widget: SuggestWidget, - private editor: ICodeEditor, - private markdownRenderer: MarkdownRenderer, - private triggerKeybindingLabel: string + private readonly widget: SuggestWidget, + private readonly editor: ICodeEditor, + private readonly markdownRenderer: MarkdownRenderer, + private readonly triggerKeybindingLabel: string ) { this.disposables = []; @@ -417,8 +417,8 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate; private listHeight: number; - private suggestWidgetVisible: IContextKey; - private suggestWidgetMultipleSuggestions: IContextKey; + private readonly suggestWidgetVisible: IContextKey; + private readonly suggestWidgetMultipleSuggestions: IContextKey; private readonly editorBlurTimeout = new TimeoutTimer(); private readonly showTimeout = new TimeoutTimer(); @@ -437,7 +437,7 @@ 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/contrib/wordHighlighter/wordHighlighter.ts b/src/vs/editor/contrib/wordHighlighter/wordHighlighter.ts index 2215346fca3..6b918096810 100644 --- a/src/vs/editor/contrib/wordHighlighter/wordHighlighter.ts +++ b/src/vs/editor/contrib/wordHighlighter/wordHighlighter.ts @@ -112,7 +112,7 @@ class SemanticOccurenceAtPositionRequest extends OccurenceAtPositionRequest { class TextualOccurenceAtPositionRequest extends OccurenceAtPositionRequest { - private _selectionIsEmpty: boolean; + private readonly _selectionIsEmpty: boolean; constructor(model: ITextModel, selection: Selection, wordSeparators: string) { super(model, selection, wordSeparators); @@ -160,9 +160,9 @@ registerDefaultLanguageCommand('_executeDocumentHighlights', (model, position) = class WordHighlighter { - private editor: IActiveCodeEditor; + private readonly editor: IActiveCodeEditor; private occurrencesHighlight: boolean; - private model: ITextModel; + private readonly model: ITextModel; private _decorationIds: string[]; private toUnhook: IDisposable[]; @@ -174,7 +174,7 @@ class WordHighlighter { private lastCursorPositionChangeTime: number = 0; private renderDecorationsTimer: any = -1; - private _hasWordHighlights: IContextKey; + private readonly _hasWordHighlights: IContextKey; private _ignorePositionChangeEvent: boolean; constructor(editor: IActiveCodeEditor, contextKeyService: IContextKeyService) { @@ -526,7 +526,7 @@ class WordHighlighterContribution extends Disposable implements editorCommon.IEd class WordHighlightNavigationAction extends EditorAction { - private _isNext: boolean; + private readonly _isNext: boolean; constructor(next: boolean, opts: IActionOptions) { super(opts); diff --git a/src/vs/editor/contrib/zoneWidget/zoneWidget.ts b/src/vs/editor/contrib/zoneWidget/zoneWidget.ts index 23fc9214c5a..8afb01b965e 100644 --- a/src/vs/editor/contrib/zoneWidget/zoneWidget.ts +++ b/src/vs/editor/contrib/zoneWidget/zoneWidget.ts @@ -56,8 +56,8 @@ export class ViewZoneDelegate implements IViewZone { public afterColumn: number; public heightInLines: number; - private _onDomNodeTop: (top: number) => void; - private _onComputedHeight: (height: number) => void; + private readonly _onDomNodeTop: (top: number) => void; + private readonly _onComputedHeight: (height: number) => void; constructor(domNode: HTMLElement, afterLineNumber: number, afterColumn: number, heightInLines: number, onDomNodeTop: (top: number) => void, @@ -82,8 +82,8 @@ export class ViewZoneDelegate implements IViewZone { export class OverlayWidgetDelegate implements IOverlayWidget { - private _id: string; - private _domNode: HTMLElement; + private readonly _id: string; + private readonly _domNode: HTMLElement; constructor(id: string, domNode: HTMLElement) { this._id = id; diff --git a/src/vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp.ts b/src/vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp.ts index cacc646f626..de148460fcd 100644 --- a/src/vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp.ts +++ b/src/vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp.ts @@ -44,8 +44,8 @@ class AccessibilityHelpController extends Disposable ); } - private _editor: ICodeEditor; - private _widget: AccessibilityHelpWidget; + private readonly _editor: ICodeEditor; + private readonly _widget: AccessibilityHelpWidget; constructor( editor: ICodeEditor, @@ -107,11 +107,11 @@ class AccessibilityHelpWidget extends Widget implements IOverlayWidget { private static readonly WIDTH = 500; private static readonly HEIGHT = 300; - private _editor: ICodeEditor; - private _domNode: FastDomNode; - private _contentDomNode: FastDomNode; + private readonly _editor: ICodeEditor; + private readonly _domNode: FastDomNode; + private readonly _contentDomNode: FastDomNode; private _isVisible: boolean; - private _isVisibleKey: IContextKey; + private readonly _isVisibleKey: IContextKey; constructor( editor: ICodeEditor, diff --git a/src/vs/editor/standalone/browser/iPadShowKeyboard/iPadShowKeyboard.ts b/src/vs/editor/standalone/browser/iPadShowKeyboard/iPadShowKeyboard.ts index 722105f2cf7..e48dcba6535 100644 --- a/src/vs/editor/standalone/browser/iPadShowKeyboard/iPadShowKeyboard.ts +++ b/src/vs/editor/standalone/browser/iPadShowKeyboard/iPadShowKeyboard.ts @@ -15,7 +15,7 @@ export class IPadShowKeyboard implements IEditorContribution { private static readonly ID = 'editor.contrib.iPadShowKeyboard'; - private editor: ICodeEditor; + private readonly editor: ICodeEditor; private widget: ShowKeyboardWidget | null; private toDispose: IDisposable[]; @@ -60,9 +60,9 @@ class ShowKeyboardWidget implements IOverlayWidget { private static readonly ID = 'editor.contrib.ShowKeyboardWidget'; - private editor: ICodeEditor; + private readonly editor: ICodeEditor; - private _domNode: HTMLElement; + private readonly _domNode: HTMLElement; private _toDispose: IDisposable[]; constructor(editor: ICodeEditor) { diff --git a/src/vs/editor/standalone/browser/inspectTokens/inspectTokens.ts b/src/vs/editor/standalone/browser/inspectTokens/inspectTokens.ts index 7369afaad96..d81cdd22f70 100644 --- a/src/vs/editor/standalone/browser/inspectTokens/inspectTokens.ts +++ b/src/vs/editor/standalone/browser/inspectTokens/inspectTokens.ts @@ -31,8 +31,8 @@ class InspectTokensController extends Disposable implements IEditorContribution return editor.getContribution(InspectTokensController.ID); } - private _editor: ICodeEditor; - private _modeService: IModeService; + private readonly _editor: ICodeEditor; + private readonly _modeService: IModeService; private _widget: InspectTokensWidget | null; constructor( @@ -162,11 +162,11 @@ class InspectTokensWidget extends Disposable implements IContentWidget { // Editor.IContentWidget.allowEditorOverflow public allowEditorOverflow = true; - private _editor: IActiveCodeEditor; - private _modeService: IModeService; - private _tokenizationSupport: ITokenizationSupport; - private _model: ITextModel; - private _domNode: HTMLElement; + private readonly _editor: IActiveCodeEditor; + private readonly _modeService: IModeService; + private readonly _tokenizationSupport: ITokenizationSupport; + private readonly _model: ITextModel; + private readonly _domNode: HTMLElement; constructor( editor: IActiveCodeEditor, diff --git a/src/vs/editor/standalone/browser/quickOpen/editorQuickOpen.ts b/src/vs/editor/standalone/browser/quickOpen/editorQuickOpen.ts index 0a77b555d7d..c8fb32757b4 100644 --- a/src/vs/editor/standalone/browser/quickOpen/editorQuickOpen.ts +++ b/src/vs/editor/standalone/browser/quickOpen/editorQuickOpen.ts @@ -30,7 +30,7 @@ export class QuickOpenController implements editorCommon.IEditorContribution, ID return editor.getContribution(QuickOpenController.ID); } - private editor: ICodeEditor; + private readonly editor: ICodeEditor; private widget: QuickOpenEditorWidget | null; private rangeHighlightDecorationId: string | null; private lastKnownEditorSelection: Selection | null; @@ -148,7 +148,7 @@ export interface IQuickOpenOpts { */ export abstract class BaseEditorQuickOpenAction extends EditorAction { - private _inputAriaLabel: string; + private readonly _inputAriaLabel: string; constructor(inputAriaLabel: string, opts: IActionOptions) { super(opts); diff --git a/src/vs/editor/standalone/browser/quickOpen/gotoLine.ts b/src/vs/editor/standalone/browser/quickOpen/gotoLine.ts index 47cc1ea540d..d2d1269bafa 100644 --- a/src/vs/editor/standalone/browser/quickOpen/gotoLine.ts +++ b/src/vs/editor/standalone/browser/quickOpen/gotoLine.ts @@ -25,9 +25,9 @@ interface ParseResult { } export class GotoLineEntry extends QuickOpenEntry { - private parseResult: ParseResult; - private decorator: IDecorator; - private editor: editorCommon.IEditor; + private readonly parseResult: ParseResult; + private readonly decorator: IDecorator; + private readonly editor: editorCommon.IEditor; constructor(line: string, editor: editorCommon.IEditor, decorator: IDecorator) { super(); diff --git a/src/vs/editor/standalone/browser/quickOpen/quickCommand.ts b/src/vs/editor/standalone/browser/quickOpen/quickCommand.ts index fdbf17a7685..cf910c605d4 100644 --- a/src/vs/editor/standalone/browser/quickOpen/quickCommand.ts +++ b/src/vs/editor/standalone/browser/quickOpen/quickCommand.ts @@ -19,10 +19,10 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; export class EditorActionCommandEntry extends QuickOpenEntryGroup { - private key: string; - private action: IEditorAction; - private editor: IEditor; - private keyAriaLabel: string; + private readonly key: string; + private readonly action: IEditorAction; + private readonly editor: IEditor; + private readonly keyAriaLabel: string; constructor(key: string, keyAriaLabel: string, highlights: IHighlight[], action: IEditorAction, editor: IEditor) { super(); diff --git a/src/vs/editor/standalone/browser/quickOpen/quickOpenEditorWidget.ts b/src/vs/editor/standalone/browser/quickOpen/quickOpenEditorWidget.ts index 859e8cca21b..d97591fd2ec 100644 --- a/src/vs/editor/standalone/browser/quickOpen/quickOpenEditorWidget.ts +++ b/src/vs/editor/standalone/browser/quickOpen/quickOpenEditorWidget.ts @@ -21,8 +21,8 @@ export class QuickOpenEditorWidget implements IOverlayWidget { private static readonly ID = 'editor.contrib.quickOpenEditorWidget'; - private codeEditor: ICodeEditor; - private themeService: IThemeService; + private readonly codeEditor: ICodeEditor; + private readonly themeService: IThemeService; private visible: boolean; private quickOpenWidget: QuickOpenWidget; private domNode: HTMLElement; diff --git a/src/vs/editor/standalone/browser/quickOpen/quickOutline.ts b/src/vs/editor/standalone/browser/quickOpen/quickOutline.ts index 7a303f61636..443f884f25a 100644 --- a/src/vs/editor/standalone/browser/quickOpen/quickOutline.ts +++ b/src/vs/editor/standalone/browser/quickOpen/quickOutline.ts @@ -24,12 +24,12 @@ import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegis let SCOPE_PREFIX = ':'; export class SymbolEntry extends QuickOpenEntryGroup { - private name: string; - private type: string; - private description: string | null; - private range: Range; - private editor: ICodeEditor; - private decorator: IDecorator; + private readonly name: string; + private readonly type: string; + private readonly description: string | null; + private readonly range: Range; + private readonly editor: ICodeEditor; + private readonly decorator: IDecorator; constructor(name: string, type: string, description: string | null, range: Range, highlights: IHighlight[], editor: ICodeEditor, decorator: IDecorator) { super(); diff --git a/src/vs/editor/standalone/browser/simpleServices.ts b/src/vs/editor/standalone/browser/simpleServices.ts index 7c6487339f2..66f68e5bc26 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 { IActiveTextEditorModel, 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'; @@ -43,10 +43,11 @@ import { ITelemetryInfo, ITelemetryService } from 'vs/platform/telemetry/common/ import { IWorkspace, IWorkspaceContextService, IWorkspaceFolder, IWorkspaceFoldersChangeEvent, WorkbenchState, WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; +import { ILayoutService, IDimension } from 'vs/platform/layout/browser/layoutService'; -export class SimpleModel implements IActiveTextEditorModel { +export class SimpleModel implements IResolvedTextEditorModel { - private model: ITextModel; + private readonly model: ITextModel; private readonly _onDispose: Emitter; constructor(model: ITextModel) { @@ -98,7 +99,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) @@ -246,7 +247,7 @@ export class StandaloneCommandService implements ICommandService { _serviceBrand: any; private readonly _instantiationService: IInstantiationService; - private _dynamicCommands: { [id: string]: ICommand; }; + private readonly _dynamicCommands: { [id: string]: ICommand; }; private readonly _onWillExecuteCommand = new Emitter(); public readonly onWillExecuteCommand: Event = this._onWillExecuteCommand.event; @@ -282,7 +283,7 @@ export class StandaloneCommandService implements ICommandService { export class StandaloneKeybindingService extends AbstractKeybindingService { private _cachedResolver: KeybindingResolver | null; - private _dynamicKeybindings: IKeybindingItem[]; + private readonly _dynamicKeybindings: IKeybindingItem[]; constructor( contextKeyService: IContextKeyService, @@ -422,7 +423,7 @@ export class SimpleConfigurationService implements IConfigurationService { private _onDidChangeConfiguration = new Emitter(); public readonly onDidChangeConfiguration: Event = this._onDidChangeConfiguration.event; - private _configuration: Configuration; + private readonly _configuration: Configuration; constructor() { this._configuration = new Configuration(new DefaultConfigurationModel(), new ConfigurationModel()); @@ -477,7 +478,7 @@ export class SimpleResourceConfigurationService implements ITextResourceConfigur public readonly onDidChangeConfiguration: Event; private readonly _onDidChangeConfigurationEmitter = new Emitter(); - constructor(private configurationService: SimpleConfigurationService) { + constructor(private readonly configurationService: SimpleConfigurationService) { this.configurationService.onDidChangeConfiguration((e) => { this._onDidChangeConfigurationEmitter.fire(e); }); @@ -671,3 +672,18 @@ export class SimpleUriLabelService implements ILabelService { return ''; } } + +export class SimpleLayoutService implements ILayoutService { + _serviceBrand: any; + + public onLayout = Event.None; + + private _dimension: IDimension; + get dimension(): IDimension { + if (!this._dimension) { + this._dimension = dom.getClientArea(window.document.body); + } + + return this._dimension; + } +} diff --git a/src/vs/editor/standalone/browser/standaloneCodeEditor.ts b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts index d7c016cae9d..2eced8d0f6b 100644 --- a/src/vs/editor/standalone/browser/standaloneCodeEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts @@ -153,7 +153,7 @@ function createAriaDomNode() { */ export class StandaloneCodeEditor extends CodeEditorWidget implements IStandaloneCodeEditor { - private _standaloneKeybindingService: StandaloneKeybindingService; + private readonly _standaloneKeybindingService: StandaloneKeybindingService; constructor( domElement: HTMLElement, @@ -280,7 +280,7 @@ export class StandaloneCodeEditor extends CodeEditorWidget implements IStandalon export class StandaloneEditor extends StandaloneCodeEditor implements IStandaloneCodeEditor { - private _contextViewService: ContextViewService; + private readonly _contextViewService: ContextViewService; private readonly _configurationService: IConfigurationService; private _ownsModel: boolean; @@ -358,7 +358,7 @@ export class StandaloneEditor extends StandaloneCodeEditor implements IStandalon export class StandaloneDiffEditor extends DiffEditorWidget implements IStandaloneDiffEditor { - private _contextViewService: ContextViewService; + private readonly _contextViewService: ContextViewService; private readonly _configurationService: IConfigurationService; constructor( diff --git a/src/vs/editor/standalone/browser/standaloneServices.ts b/src/vs/editor/standalone/browser/standaloneServices.ts index df278c89b82..01f45b769c4 100644 --- a/src/vs/editor/standalone/browser/standaloneServices.ts +++ b/src/vs/editor/standalone/browser/standaloneServices.ts @@ -13,7 +13,7 @@ import { ModeServiceImpl } from 'vs/editor/common/services/modeServiceImpl'; import { IModelService } from 'vs/editor/common/services/modelService'; import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; import { ITextResourceConfigurationService, ITextResourcePropertiesService } from 'vs/editor/common/services/resourceConfiguration'; -import { SimpleBulkEditService, SimpleConfigurationService, SimpleDialogService, SimpleNotificationService, SimpleProgressService, SimpleResourceConfigurationService, SimpleResourcePropertiesService, SimpleUriLabelService, SimpleWorkspaceContextService, StandaloneCommandService, StandaloneKeybindingService, StandaloneTelemetryService, BrowserAccessibilityService } from 'vs/editor/standalone/browser/simpleServices'; +import { SimpleBulkEditService, SimpleConfigurationService, SimpleDialogService, SimpleNotificationService, SimpleProgressService, SimpleResourceConfigurationService, SimpleResourcePropertiesService, SimpleUriLabelService, SimpleWorkspaceContextService, StandaloneCommandService, StandaloneKeybindingService, StandaloneTelemetryService, BrowserAccessibilityService, SimpleLayoutService } from 'vs/editor/standalone/browser/simpleServices'; import { StandaloneCodeEditorServiceImpl } from 'vs/editor/standalone/browser/standaloneCodeServiceImpl'; import { StandaloneThemeServiceImpl } from 'vs/editor/standalone/browser/standaloneThemeServiceImpl'; import { IStandaloneThemeService } from 'vs/editor/standalone/common/standaloneThemeService'; @@ -46,6 +46,7 @@ import { IMarkerDecorationsService } from 'vs/editor/common/services/markersDeco import { MarkerDecorationsService } from 'vs/editor/common/services/markerDecorationsServiceImpl'; import { ISuggestMemoryService, SuggestMemoryService } from 'vs/editor/contrib/suggest/suggestMemory'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; +import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; export interface IEditorOverrideServices { [index: string]: any; @@ -56,8 +57,8 @@ export module StaticServices { const _serviceCollection = new ServiceCollection(); export class LazyStaticService { - private _serviceId: ServiceIdentifier; - private _factory: (overrides?: IEditorOverrideServices) => T; + private readonly _serviceId: ServiceIdentifier; + private readonly _factory: (overrides?: IEditorOverrideServices) => T; private _value: T | null; public get id() { return this._serviceId; } @@ -161,8 +162,8 @@ export module StaticServices { export class DynamicStandaloneServices extends Disposable { - private _serviceCollection: ServiceCollection; - private _instantiationService: IInstantiationService; + private readonly _serviceCollection: ServiceCollection; + private readonly _instantiationService: IInstantiationService; constructor(domElement: HTMLElement, overrides: IEditorOverrideServices) { super(); @@ -196,7 +197,9 @@ export class DynamicStandaloneServices extends Disposable { let keybindingService = ensure(IKeybindingService, () => this._register(new StandaloneKeybindingService(contextKeyService, commandService, telemetryService, notificationService, domElement))); - let contextViewService = ensure(IContextViewService, () => this._register(new ContextViewService(domElement, telemetryService, new NullLogService()))); + let layoutService = ensure(ILayoutService, () => new SimpleLayoutService()); + + let contextViewService = ensure(IContextViewService, () => this._register(new ContextViewService(domElement, telemetryService, new NullLogService(), layoutService))); ensure(IContextMenuService, () => this._register(new ContextMenuService(domElement, telemetryService, notificationService, contextViewService, keybindingService, themeService))); diff --git a/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts b/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts index 7efa2c38ba7..5e9074577a3 100644 --- a/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts +++ b/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts @@ -26,9 +26,9 @@ class StandaloneTheme implements IStandaloneTheme { public readonly id: string; public readonly themeName: string; - private themeData: IStandaloneThemeData; + private readonly themeData: IStandaloneThemeData; private colors: { [colorId: string]: Color } | null; - private defaultColors: { [colorId: string]: Color | undefined; }; + private readonly defaultColors: { [colorId: string]: Color | undefined; }; private _tokenTheme: TokenTheme | null; constructor(name: string, standaloneThemeData: IStandaloneThemeData) { @@ -159,12 +159,12 @@ export class StandaloneThemeServiceImpl implements IStandaloneThemeService { _serviceBrand: any; - private _knownThemes: Map; - private _styleElement: HTMLStyleElement; + private readonly _knownThemes: Map; + private readonly _styleElement: HTMLStyleElement; private _theme: IStandaloneTheme; private readonly _onThemeChange: Emitter; private readonly _onIconThemeChange: Emitter; - private environment: IEnvironmentService = Object.create(null); + private readonly environment: IEnvironmentService = Object.create(null); constructor() { this._onThemeChange = new Emitter(); diff --git a/src/vs/editor/standalone/common/monarch/monarchLexer.ts b/src/vs/editor/standalone/common/monarch/monarchLexer.ts index 6d8b55352ab..2567c561f7c 100644 --- a/src/vs/editor/standalone/common/monarch/monarchLexer.ts +++ b/src/vs/editor/standalone/common/monarch/monarchLexer.ts @@ -289,8 +289,8 @@ class MonarchClassicTokensCollector implements IMonarchTokensCollector { class MonarchModernTokensCollector implements IMonarchTokensCollector { - private _modeService: IModeService; - private _theme: TokenTheme; + private readonly _modeService: IModeService; + private readonly _theme: TokenTheme; private _prependTokens: Uint32Array | null; private _tokens: number[]; private _currentLanguageId: modes.LanguageId; @@ -382,9 +382,9 @@ export class MonarchTokenizer implements modes.ITokenizationSupport { private readonly _standaloneThemeService: IStandaloneThemeService; private readonly _modeId: string; private readonly _lexer: monarchCommon.ILexer; - private _embeddedModes: { [modeId: string]: boolean; }; + private readonly _embeddedModes: { [modeId: string]: boolean; }; public embeddedLoaded: Promise; - private _tokenizationRegistryListener: IDisposable; + private readonly _tokenizationRegistryListener: IDisposable; constructor(modeService: IModeService, standaloneThemeService: IStandaloneThemeService, modeId: string, lexer: monarchCommon.ILexer) { this._modeService = modeService; diff --git a/src/vs/editor/test/browser/controller/imeTester.ts b/src/vs/editor/test/browser/controller/imeTester.ts index 4ea8935cd20..1dd06565c23 100644 --- a/src/vs/editor/test/browser/controller/imeTester.ts +++ b/src/vs/editor/test/browser/controller/imeTester.ts @@ -44,7 +44,7 @@ class SingleLineTestModel implements ISimpleModel { class TestView { - private _model: SingleLineTestModel; + private readonly _model: SingleLineTestModel; constructor(model: SingleLineTestModel) { this._model = model; diff --git a/src/vs/editor/test/common/mocks/mockMode.ts b/src/vs/editor/test/common/mocks/mockMode.ts index bf24cc35ceb..703a760c7ad 100644 --- a/src/vs/editor/test/common/mocks/mockMode.ts +++ b/src/vs/editor/test/common/mocks/mockMode.ts @@ -9,7 +9,7 @@ import { IMode, LanguageIdentifier } from 'vs/editor/common/modes'; import { ILanguageSelection } from 'vs/editor/common/services/modeService'; export class MockMode extends Disposable implements IMode { - private _languageIdentifier: LanguageIdentifier; + private readonly _languageIdentifier: LanguageIdentifier; constructor(languageIdentifier: LanguageIdentifier) { super(); diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 3f279a4527c..af4d3f7f0d6 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -1387,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. 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 e615685b30b..b3bc081c9c5 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -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; } 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/contextview/browser/contextViewService.ts b/src/vs/platform/contextview/browser/contextViewService.ts index 48b0f51eb86..0bcada24ea8 100644 --- a/src/vs/platform/contextview/browser/contextViewService.ts +++ b/src/vs/platform/contextview/browser/contextViewService.ts @@ -8,6 +8,7 @@ import { ContextView } from 'vs/base/browser/ui/contextview/contextview'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ILogService } from 'vs/platform/log/common/log'; import { Disposable } from 'vs/base/common/lifecycle'; +import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; export class ContextViewService extends Disposable implements IContextViewService { _serviceBrand: any; @@ -17,11 +18,15 @@ export class ContextViewService extends Disposable implements IContextViewServic constructor( container: HTMLElement, @ITelemetryService telemetryService: ITelemetryService, - @ILogService private readonly logService: ILogService + @ILogService private readonly logService: ILogService, + @ILayoutService readonly layoutService: ILayoutService ) { super(); this.contextView = this._register(new ContextView(container)); + this.layout(); + + this._register(layoutService.onLayout(() => this.layout())); } // ContextView 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/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/layout/browser/layoutService.ts b/src/vs/platform/layout/browser/layoutService.ts new file mode 100644 index 00000000000..d9cc51f1a2d --- /dev/null +++ b/src/vs/platform/layout/browser/layoutService.ts @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Event } from 'vs/base/common/event'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; + +export const ILayoutService = createDecorator('layoutService'); + +export interface IDimension { + width: number; + height: number; +} + +export interface ILayoutService { + + _serviceBrand: any; + + /** + * The dimensions of the container. + */ + readonly dimension: IDimension; + + /** + * An event that is emitted when the container is layed out. The + * event carries the dimensions of the container as part of it. + */ + readonly onLayout: Event; +} \ No newline at end of file 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/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/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index e28ca55477a..1be5b5e4a63 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -117,7 +117,7 @@ export interface IWindowsService { enterWorkspace(windowId: number, path: URI): Promise; toggleFullScreen(windowId: number): Promise; setRepresentedFilename(windowId: number, fileName: string): Promise; - addRecentlyOpened(files: URI[]): Promise; + addRecentlyOpened(workspaces: URI[], folders: URI[], files: URI[]): Promise; removeFromRecentlyOpened(paths: Array): Promise; clearRecentlyOpened(): Promise; getRecentlyOpened(windowId: number): Promise; @@ -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 016c59a3f20..d832fe25a4a 100644 --- a/src/vs/platform/windows/electron-main/windowsService.ts +++ b/src/vs/platform/windows/electron-main/windowsService.ts @@ -17,7 +17,7 @@ import { IURLService, IURLHandler } from 'vs/platform/url/common/url'; import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain'; import { IWindowsMainService, ISharedProcess, ICodeWindow } from 'vs/platform/windows/electron-main/windows'; import { IHistoryMainService, IRecentlyOpened } from 'vs/platform/history/common/history'; -import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, IWorkspacesMainService } from 'vs/platform/workspaces/common/workspaces'; import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; import { Schemas } from 'vs/base/common/network'; import { mnemonicButtonLabel } from 'vs/base/common/labels'; @@ -50,7 +50,8 @@ export class WindowsService implements IWindowsService, IURLHandler, IDisposable @IURLService urlService: IURLService, @ILifecycleService private readonly lifecycleService: ILifecycleService, @IHistoryMainService private readonly historyService: IHistoryMainService, - @ILogService private readonly logService: ILogService + @ILogService private readonly logService: ILogService, + @IWorkspacesMainService private readonly workspacesMainService: IWorkspacesMainService, ) { urlService.registerHandler(this); @@ -156,10 +157,11 @@ export class WindowsService implements IWindowsService, IURLHandler, IDisposable return this.withWindow(windowId, codeWindow => codeWindow.setRepresentedFilename(fileName)); } - async addRecentlyOpened(files: URI[]): Promise { + async addRecentlyOpened(workspaces: URI[], folders: URI[], files: URI[]): Promise { this.logService.trace('windowsService#addRecentlyOpened'); - this.historyService.addRecentlyOpened(undefined, files); + const workspaceIdentifiers = workspaces.map(w => this.workspacesMainService.getWorkspaceIdentifier(w)); + this.historyService.addRecentlyOpened([...workspaceIdentifiers, ...folders], files); } async removeFromRecentlyOpened(paths: Array): Promise { @@ -324,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 12b741138c0..81314da6c66 100644 --- a/src/vs/platform/windows/node/windowsIpc.ts +++ b/src/vs/platform/windows/node/windowsIpc.ts @@ -59,7 +59,7 @@ export class WindowsChannel implements IServerChannel { case 'enterWorkspace': return this.service.enterWorkspace(arg[0], URI.revive(arg[1])); case 'toggleFullScreen': return this.service.toggleFullScreen(arg); case 'setRepresentedFilename': return this.service.setRepresentedFilename(arg[0], arg[1]); - case 'addRecentlyOpened': return this.service.addRecentlyOpened(arg.map(URI.revive)); + case 'addRecentlyOpened': return this.service.addRecentlyOpened(arg[0].map(URI.revive), arg[1].map(URI.revive), arg[2].map(URI.revive)); case 'removeFromRecentlyOpened': { let paths: Array = arg; if (Array.isArray(paths)) { @@ -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); @@ -178,8 +178,8 @@ export class WindowsChannelClient implements IWindowsService { return this.channel.call('setRepresentedFilename', [windowId, fileName]); } - addRecentlyOpened(files: URI[]): Promise { - return this.channel.call('addRecentlyOpened', files); + addRecentlyOpened(workspaces: URI[], folders: URI[], files: URI[]): Promise { + return this.channel.call('addRecentlyOpened', [workspaces, folders, files]); } removeFromRecentlyOpened(paths: Array): Promise { @@ -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 890e9f55f13..0f745aea0da 100644 --- a/src/vs/platform/workspaces/common/workspaces.ts +++ b/src/vs/platform/workspaces/common/workspaces.ts @@ -97,8 +97,6 @@ export interface IWorkspacesMainService extends IWorkspacesService { onUntitledWorkspaceDeleted: Event; - saveWorkspaceAs(workspace: IWorkspaceIdentifier, target: string): Promise; - createUntitledWorkspaceSync(folders?: IWorkspaceFolderCreationData[]): IWorkspaceIdentifier; resolveLocalWorkspaceSync(path: URI): IResolvedWorkspace | null; @@ -116,6 +114,8 @@ export interface IWorkspacesService { _serviceBrand: any; createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[], remoteAuthority?: string): Promise; + + deleteUntitledWorkspace(workspace: IWorkspaceIdentifier): Promise; } export function isSingleFolderWorkspaceIdentifier(obj: any): obj is ISingleFolderWorkspaceIdentifier { diff --git a/src/vs/platform/workspaces/electron-main/workspacesMainService.ts b/src/vs/platform/workspaces/electron-main/workspacesMainService.ts index 70bb98c8626..b257f0720c5 100644 --- a/src/vs/platform/workspaces/electron-main/workspacesMainService.ts +++ b/src/vs/platform/workspaces/electron-main/workspacesMainService.ts @@ -3,16 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IWorkspacesMainService, IWorkspaceIdentifier, hasWorkspaceFileExtension, UNTITLED_WORKSPACE_NAME, IResolvedWorkspace, IStoredWorkspaceFolder, isStoredWorkspaceFolder, IWorkspaceFolderCreationData, rewriteWorkspaceFileForNewLocation, IUntitledWorkspaceInfo, getStoredWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspacesMainService, IWorkspaceIdentifier, hasWorkspaceFileExtension, UNTITLED_WORKSPACE_NAME, IResolvedWorkspace, IStoredWorkspaceFolder, isStoredWorkspaceFolder, IWorkspaceFolderCreationData, IUntitledWorkspaceInfo, getStoredWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { join, dirname } from 'vs/base/common/path'; -import { mkdirp, writeFile, readFile } from 'vs/base/node/pfs'; +import { mkdirp, writeFile } from 'vs/base/node/pfs'; import { readFileSync, existsSync, mkdirSync, writeFileSync } from 'fs'; import { isLinux } from 'vs/base/common/platform'; import { delSync, readdirSync, writeFileAndFlushSync } from 'vs/base/node/extfs'; import { Event, Emitter } from 'vs/base/common/event'; import { ILogService } from 'vs/platform/log/common/log'; -import { isEqual } from 'vs/base/common/extpath'; import { createHash } from 'crypto'; import * as json from 'vs/base/common/json'; import { toWorkspaceFolders } from 'vs/platform/workspace/common/workspace'; @@ -169,30 +168,6 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain return this.isInsideWorkspacesHome(workspace.configPath); } - saveWorkspaceAs(workspace: IWorkspaceIdentifier, targetConfigPath: string): Promise { - - if (workspace.configPath.scheme !== Schemas.file) { - throw new Error('Only local workspaces can be saved with this API. Use WorkspaceEditingService.saveWorkspaceAs on the renderer instead.'); - } - - const configPath = originalFSPath(workspace.configPath); - - // Return early if target is same as source - if (isEqual(configPath, targetConfigPath, !isLinux)) { - return Promise.resolve(workspace); - } - - // Read the contents of the workspace file and resolve it - return readFile(configPath).then(raw => { - const targetConfigPathURI = URI.file(targetConfigPath); - const newRawWorkspaceContents = rewriteWorkspaceFileForNewLocation(raw.toString(), workspace.configPath, targetConfigPathURI); - - return writeFile(targetConfigPath, newRawWorkspaceContents).then(() => { - return this.getWorkspaceIdentifier(targetConfigPathURI); - }); - }); - } - deleteUntitledWorkspaceSync(workspace: IWorkspaceIdentifier): void { if (!this.isUntitledWorkspace(workspace)) { return; // only supported for untitled workspaces @@ -205,6 +180,11 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain this._onUntitledWorkspaceDeleted.fire(workspace); } + deleteUntitledWorkspace(workspace: IWorkspaceIdentifier): Promise { + this.deleteUntitledWorkspaceSync(workspace); + return Promise.resolve(); + } + private doDeleteUntitledWorkspaceSync(workspace: IWorkspaceIdentifier): void { const configPath = originalFSPath(workspace.configPath); try { diff --git a/src/vs/platform/workspaces/node/workspacesIpc.ts b/src/vs/platform/workspaces/node/workspacesIpc.ts index 59caecf0560..ba831722e79 100644 --- a/src/vs/platform/workspaces/node/workspacesIpc.ts +++ b/src/vs/platform/workspaces/node/workspacesIpc.ts @@ -33,6 +33,10 @@ export class WorkspacesChannel implements IServerChannel { return this.service.createUntitledWorkspace(folders, remoteAuthority); } + case 'deleteUntitledWorkspace': { + const w: IWorkspaceIdentifier = arg; + return this.service.deleteUntitledWorkspace({ id: w.id, configPath: URI.revive(w.configPath) }); + } } throw new Error(`Call not found: ${command}`); @@ -48,4 +52,8 @@ export class WorkspacesChannelClient implements IWorkspacesService { createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[], remoteAuthority?: string): Promise { return this.channel.call('createUntitledWorkspace', [folders, remoteAuthority]).then(reviveWorkspaceIdentifier); } + + deleteUntitledWorkspace(workspaceIdentifier: IWorkspaceIdentifier): Promise { + return this.channel.call('deleteUntitledWorkspace', workspaceIdentifier); + } } 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 31c50d16b58..a74f525c7f3 100644 --- a/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts +++ b/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts @@ -7,12 +7,11 @@ import * as assert from 'assert'; import * as fs from 'fs'; import * as os from 'os'; import * as path from 'vs/base/common/path'; -import * as extfs from 'vs/base/node/extfs'; import * as pfs from 'vs/base/node/pfs'; import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; import { parseArgs } from 'vs/platform/environment/node/argv'; import { WorkspacesMainService, IStoredWorkspace } from 'vs/platform/workspaces/electron-main/workspacesMainService'; -import { WORKSPACE_EXTENSION, IWorkspaceIdentifier, IRawFileWorkspaceFolder, IWorkspaceFolderCreationData, IRawUriWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces'; +import { WORKSPACE_EXTENSION, IWorkspaceIdentifier, IRawFileWorkspaceFolder, IWorkspaceFolderCreationData, IRawUriWorkspaceFolder, rewriteWorkspaceFileForNewLocation } from 'vs/platform/workspaces/common/workspaces'; import { NullLogService } from 'vs/platform/log/common/log'; import { URI } from 'vs/base/common/uri'; import { getRandomTestPath } from 'vs/base/test/node/testUtils'; @@ -235,95 +234,85 @@ suite('WorkspacesMainService', () => { }); }); - test('saveWorkspace (untitled)', () => { - return createWorkspace([process.cwd(), os.tmpdir(), path.join(os.tmpdir(), 'somefolder')]).then(workspace => { - const workspaceConfigPath = path.join(os.tmpdir(), `myworkspace.${Date.now()}.${WORKSPACE_EXTENSION}`); + test('rewriteWorkspaceFileForNewLocation', () => { + const folder1 = process.cwd(); // absolute path because outside of tmpDir + const tmpDir = os.tmpdir(); + const tmpInsideDir = path.join(os.tmpdir(), 'inside'); - return service.saveWorkspaceAs(workspace, workspaceConfigPath).then(savedWorkspace => { - assert.ok(savedWorkspace.id); - assert.notEqual(savedWorkspace.id, workspace.id); - assertPathEquals(savedWorkspace.configPath.fsPath, workspaceConfigPath); + return createWorkspace([folder1, tmpInsideDir, path.join(tmpInsideDir, 'somefolder')]).then(workspace => { + const origContent = fs.readFileSync(workspace.configPath.fsPath).toString(); - const ws = JSON.parse(fs.readFileSync(savedWorkspace.configPath.fsPath).toString()) as IStoredWorkspace; - assert.equal(ws.folders.length, 3); - assertPathEquals((ws.folders[0]).path, process.cwd()); // absolute - assertPathEquals((ws.folders[1]).path, '.'); // relative - assertPathEquals((ws.folders[2]).path, path.relative(path.dirname(workspaceConfigPath), path.join(os.tmpdir(), 'somefolder'))); // relative + let origConfigPath = workspace.configPath; + let workspaceConfigPath = URI.file(path.join(tmpDir, 'inside', 'myworkspace1.code-workspace')); + let newContent = rewriteWorkspaceFileForNewLocation(origContent, origConfigPath, workspaceConfigPath); - extfs.delSync(workspaceConfigPath); - }); + let ws = JSON.parse(newContent) as IStoredWorkspace; + assert.equal(ws.folders.length, 3); + assertPathEquals((ws.folders[0]).path, folder1); // absolute path because outside of tmpdir + assertPathEquals((ws.folders[1]).path, '.'); + assertPathEquals((ws.folders[2]).path, 'somefolder'); + + origConfigPath = workspaceConfigPath; + workspaceConfigPath = URI.file(path.join(tmpDir, 'myworkspace2.code-workspace')); + newContent = rewriteWorkspaceFileForNewLocation(newContent, origConfigPath, workspaceConfigPath); + + ws = JSON.parse(newContent) as IStoredWorkspace; + assert.equal(ws.folders.length, 3); + assertPathEquals((ws.folders[0]).path, folder1); + assertPathEquals((ws.folders[1]).path, 'inside'); + assertPathEquals((ws.folders[2]).path, isWindows ? 'inside\\somefolder' : 'inside/somefolder'); + + origConfigPath = workspaceConfigPath; + workspaceConfigPath = URI.file(path.join(tmpDir, 'other', 'myworkspace2.code-workspace')); + newContent = rewriteWorkspaceFileForNewLocation(newContent, origConfigPath, workspaceConfigPath); + + ws = JSON.parse(newContent) as IStoredWorkspace; + assert.equal(ws.folders.length, 3); + assertPathEquals((ws.folders[0]).path, folder1); + assertPathEquals((ws.folders[1]).path, tmpInsideDir); + assertPathEquals((ws.folders[2]).path, path.join(tmpInsideDir, 'somefolder')); + + origConfigPath = workspaceConfigPath; + workspaceConfigPath = URI.parse('foo://foo/bar/myworkspace2.code-workspace'); + newContent = rewriteWorkspaceFileForNewLocation(newContent, origConfigPath, workspaceConfigPath); + + ws = JSON.parse(newContent) as IStoredWorkspace; + assert.equal(ws.folders.length, 3); + assert.equal((ws.folders[0]).uri, URI.file(folder1).toString(true)); + assert.equal((ws.folders[1]).uri, URI.file(tmpInsideDir).toString(true)); + assert.equal((ws.folders[2]).uri, URI.file(path.join(tmpInsideDir, 'somefolder')).toString(true)); + + service.deleteUntitledWorkspaceSync(workspace); }); }); - test('saveWorkspace (saved workspace)', () => { + test('rewriteWorkspaceFileForNewLocation (preserves comments)', () => { return createWorkspace([process.cwd(), os.tmpdir(), path.join(os.tmpdir(), 'somefolder')]).then(workspace => { - const workspaceConfigPath = path.join(os.tmpdir(), `myworkspace.${Date.now()}.${WORKSPACE_EXTENSION}`); - const newWorkspaceConfigPath = path.join(os.tmpdir(), `mySavedWorkspace.${Date.now()}.${WORKSPACE_EXTENSION}`); + const workspaceConfigPath = URI.file(path.join(os.tmpdir(), `myworkspace.${Date.now()}.${WORKSPACE_EXTENSION}`)); - return service.saveWorkspaceAs(workspace, workspaceConfigPath).then(savedWorkspace => { - return service.saveWorkspaceAs(savedWorkspace, newWorkspaceConfigPath).then(newSavedWorkspace => { - assert.ok(newSavedWorkspace.id); - assert.notEqual(newSavedWorkspace.id, workspace.id); - assertPathEquals(newSavedWorkspace.configPath.fsPath, newWorkspaceConfigPath); + let origContent = fs.readFileSync(workspace.configPath.fsPath).toString(); + origContent = `// this is a comment\n${origContent}`; - const ws = JSON.parse(fs.readFileSync(newSavedWorkspace.configPath.fsPath).toString()) as IStoredWorkspace; - assert.equal(ws.folders.length, 3); - assertPathEquals((ws.folders[0]).path, process.cwd()); // absolute path because outside of tmpdir - assertPathEquals((ws.folders[1]).path, '.'); // relative path because inside of tmpdir - assertPathEquals((ws.folders[2]).path, 'somefolder'); // relative + let newContent = rewriteWorkspaceFileForNewLocation(origContent, workspace.configPath, workspaceConfigPath); - extfs.delSync(workspaceConfigPath); - extfs.delSync(newWorkspaceConfigPath); - }); - }); + assert.equal(0, newContent.indexOf('// this is a comment')); + + service.deleteUntitledWorkspaceSync(workspace); }); }); - test('saveWorkspace (saved workspace, preserves comments)', () => { + test('rewriteWorkspaceFileForNewLocation (preserves forward slashes)', () => { return createWorkspace([process.cwd(), os.tmpdir(), path.join(os.tmpdir(), 'somefolder')]).then(workspace => { - const workspaceConfigPath = path.join(os.tmpdir(), `myworkspace.${Date.now()}.${WORKSPACE_EXTENSION}`); - const newWorkspaceConfigPath = path.join(os.tmpdir(), `mySavedWorkspace.${Date.now()}.${WORKSPACE_EXTENSION}`); + const workspaceConfigPath = URI.file(path.join(os.tmpdir(), `myworkspace.${Date.now()}.${WORKSPACE_EXTENSION}`)); + let origContent = fs.readFileSync(workspace.configPath.fsPath).toString(); + origContent = origContent.replace(/[\\]/g, '/'); // convert backslash to slash - return service.saveWorkspaceAs(workspace, workspaceConfigPath).then(savedWorkspace => { - const contents = fs.readFileSync(savedWorkspace.configPath.fsPath).toString(); - fs.writeFileSync(savedWorkspace.configPath.fsPath, `// this is a comment\n${contents}`); + const newContent = rewriteWorkspaceFileForNewLocation(origContent, workspace.configPath, workspaceConfigPath); - return service.saveWorkspaceAs(savedWorkspace, newWorkspaceConfigPath).then(newSavedWorkspace => { - assert.ok(newSavedWorkspace.id); - assert.notEqual(newSavedWorkspace.id, workspace.id); - assertPathEquals(newSavedWorkspace.configPath.fsPath, newWorkspaceConfigPath); + const ws = JSON.parse(newContent) as IStoredWorkspace; + assert.ok(ws.folders.every(f => (f).path.indexOf('\\') < 0)); - const savedContents = fs.readFileSync(newSavedWorkspace.configPath.fsPath).toString(); - assert.equal(0, savedContents.indexOf('// this is a comment')); - - extfs.delSync(workspaceConfigPath); - extfs.delSync(newWorkspaceConfigPath); - }); - }); - }); - }); - - test('saveWorkspace (saved workspace, preserves forward slashes)', () => { - return createWorkspace([process.cwd(), os.tmpdir(), path.join(os.tmpdir(), 'somefolder')]).then(workspace => { - const workspaceConfigPath = path.join(os.tmpdir(), `myworkspace.${Date.now()}.${WORKSPACE_EXTENSION}`); - const newWorkspaceConfigPath = path.join(os.tmpdir(), `mySavedWorkspace.${Date.now()}.${WORKSPACE_EXTENSION}`); - - return service.saveWorkspaceAs(workspace, workspaceConfigPath).then(savedWorkspace => { - const contents = fs.readFileSync(savedWorkspace.configPath.fsPath).toString(); - fs.writeFileSync(savedWorkspace.configPath.fsPath, contents.replace(/[\\]/g, '/')); // convert backslash to slash - - return service.saveWorkspaceAs(savedWorkspace, newWorkspaceConfigPath).then(newSavedWorkspace => { - assert.ok(newSavedWorkspace.id); - assert.notEqual(newSavedWorkspace.id, workspace.id); - assertPathEquals(newSavedWorkspace.configPath.fsPath, newWorkspaceConfigPath); - - const ws = JSON.parse(fs.readFileSync(newSavedWorkspace.configPath.fsPath).toString()) as IStoredWorkspace; - assert.ok(ws.folders.every(f => (f).path.indexOf('\\') < 0)); - - extfs.delSync(workspaceConfigPath); - extfs.delSync(newWorkspaceConfigPath); - }); - }); + service.deleteUntitledWorkspaceSync(workspace); }); }); @@ -339,15 +328,7 @@ suite('WorkspacesMainService', () => { test('deleteUntitledWorkspaceSync (saved)', () => { return createWorkspace([process.cwd(), os.tmpdir()]).then(workspace => { - const workspaceConfigPath = path.join(os.tmpdir(), `myworkspace.${Date.now()}.${WORKSPACE_EXTENSION}`); - - return service.saveWorkspaceAs(workspace, workspaceConfigPath).then(savedWorkspace => { - assert.ok(fs.existsSync(savedWorkspace.configPath.fsPath)); - - service.deleteUntitledWorkspaceSync(savedWorkspace); - - assert.ok(fs.existsSync(savedWorkspace.configPath.fsPath)); - }); + service.deleteUntitledWorkspaceSync(workspace); }); }); 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 502db2075d0..fddf576759d 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; } @@ -198,14 +198,14 @@ export class MainThreadCommentController { return this._label; } - private _threads: Map = new Map(); + private readonly _threads: Map = new Map(); public activeCommentThread?: MainThreadCommentThread; 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 { @@ -332,7 +332,7 @@ export class MainThreadCommentController { 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..97ff3f6f4a6 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) { @@ -114,7 +114,7 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape { // --- from extension host process - $tryShowTextDocument(resource: UriComponents, options: ITextDocumentShowOptions): Promise { + $tryShowTextDocument(resource: UriComponents, options: ITextDocumentShowOptions): Promise { const uri = URI.revive(resource); const editorOptions: ITextEditorOptions = { @@ -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 9665fd33072..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; 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 2c4068005f8..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 @@ -234,9 +234,9 @@ class FormatOnSaveParticipant implements ISaveParticipantParticipant { 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, model.getFormattingOptions(), 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)); @@ -257,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); @@ -276,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), @@ -293,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; } @@ -367,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 @@ -424,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/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/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index 412f081812b..f96b61c1450 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -92,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); @@ -188,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; @@ -209,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; @@ -527,7 +527,7 @@ export function createApiFactory( onDidChangeWorkspaceFolders: function (listener, thisArgs?, disposables?) { return extHostWorkspace.onDidChangeWorkspace(listener, thisArgs, disposables); }, - asRelativePath: (pathOrUri, includeWorkspace) => { + asRelativePath: (pathOrUri, includeWorkspace?) => { return extHostWorkspace.getRelativePath(pathOrUri, includeWorkspace); }, findFiles: (include, exclude, maxResults?, token?) => { @@ -566,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 5b6c038376c..e0f4f8588e9 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -141,7 +141,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; } @@ -223,7 +223,7 @@ export interface ITextDocumentShowOptions { } export interface MainThreadTextEditorsShape extends IDisposable { - $tryShowTextDocument(resource: UriComponents, options: ITextDocumentShowOptions): Promise; + $tryShowTextDocument(resource: UriComponents, options: ITextDocumentShowOptions): Promise; $registerTextEditorDecorationType(key: string, options: editorCommon.IDecorationRenderOptions): void; $removeTextEditorDecorationType(key: string): void; $tryShowEditor(id: string, position: EditorViewColumn): Promise; @@ -367,7 +367,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; @@ -459,7 +459,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; } @@ -526,7 +526,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; @@ -581,7 +581,7 @@ export interface SCMProviderFeatures { count?: number; commitTemplate?: string; acceptInputCommand?: modes.Command; - statusBarCommands?: modes.Command[]; + statusBarCommands?: CommandDto[]; } export interface SCMGroupFeatures { @@ -859,9 +859,9 @@ export interface WorkspaceSymbolsDto extends IdObject { } export interface ResourceFileEditDto { - oldUri: UriComponents; - newUri: UriComponents; - options: IFileOperationOptions; + oldUri?: UriComponents; + newUri?: UriComponents; + options?: IFileOperationOptions; } export interface ResourceTextEditDto { @@ -964,7 +964,7 @@ export interface ShellLaunchConfigDto { name?: string; executable?: string; args?: string[] | string; - cwd?: string | URI; + cwd?: string | UriComponents; env?: { [key: string]: string | null }; } @@ -977,7 +977,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 8a54f2e10d4..ffd3502e0b7 100644 --- a/src/vs/workbench/api/node/extHostApiCommands.ts +++ b/src/vs/workbench/api/node/extHostApiCommands.ts @@ -263,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); } @@ -408,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], @@ -443,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 index 89f6c02d448..a2311b7f69e 100644 --- a/src/vs/workbench/api/node/extHostCLIServer.ts +++ b/src/vs/workbench/api/node/extHostCLIServer.ts @@ -39,7 +39,7 @@ export class CLIServer { console.error('Could not start open from terminal server.'); } - return this.ipcHandlePath; + return this._ipcHandlePath; } private collectURIToOpen(strs: string[], typeHint: URIType, result: IURIToOpen[]): void { if (Array.isArray(strs)) { 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 4138ce9f911..6b2f275329e 100644 --- a/src/vs/workbench/api/node/extHostComments.ts +++ b/src/vs/workbench/api/node/extHostComments.ts @@ -374,7 +374,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]; diff --git a/src/vs/workbench/api/node/extHostDebugService.ts b/src/vs/workbench/api/node/extHostDebugService.ts index c593cb4bf65..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')); } diff --git a/src/vs/workbench/api/node/extHostDiagnostics.ts b/src/vs/workbench/api/node/extHostDiagnostics.ts index e88ae6266c9..8a54b7e405c 100644 --- a/src/vs/workbench/api/node/extHostDiagnostics.ts +++ b/src/vs/workbench/api/node/extHostDiagnostics.ts @@ -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; } } @@ -85,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; @@ -95,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); + } } } } @@ -108,8 +114,8 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection { // 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 @@ -137,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)); } } @@ -164,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 { @@ -218,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)) { @@ -279,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/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 a053696d1cf..2383a6eb83a 100644 --- a/src/vs/workbench/api/node/extHostDocuments.ts +++ b/src/vs/workbench/api/node/extHostDocuments.ts @@ -77,7 +77,7 @@ export class ExtHostDocuments implements ExtHostDocumentsShape { public ensureDocumentData(uri: URI): Promise { - let cached = this._documentsAndEditors.getDocument(uri); + const cached = this._documentsAndEditors.getDocument(uri); if (cached) { return Promise.resolve(cached); } @@ -103,8 +103,10 @@ export class ExtHostDocuments implements ExtHostDocumentsShape { public $acceptModelModeChanged(uriComponents: UriComponents, oldModeId: string, newModeId: string): void { const uri = URI.revive(uriComponents); - let data = this._documentsAndEditors.getDocument(uri); - + 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); @@ -114,14 +116,20 @@ export class ExtHostDocuments implements ExtHostDocumentsShape { public $acceptModelSaved(uriComponents: UriComponents): void { const uri = URI.revive(uriComponents); - let data = this._documentsAndEditors.getDocument(uri); + 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); - let data = this._documentsAndEditors.getDocument(uri); + const data = this._documentsAndEditors.getDocument(uri); + if (!data) { + throw new Error('unknown document'); + } data._acceptIsDirty(isDirty); this._onDidChangeDocument.fire({ document: data.document, @@ -131,7 +139,10 @@ export class ExtHostDocuments implements ExtHostDocumentsShape { public $acceptModelChanged(uriComponents: UriComponents, events: IModelChangedEvent, isDirty: boolean): void { const uri = URI.revive(uriComponents); - let data = this._documentsAndEditors.getDocument(uri); + 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 57868da0161..c0e26886a58 100644 --- a/src/vs/workbench/api/node/extHostDocumentsAndEditors.ts +++ b/src/vs/workbench/api/node/extHostDocumentsAndEditors.ts @@ -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,7 +131,7 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha } } - getDocument(uri: URI): ExtHostDocumentData { + getDocument(uri: URI): ExtHostDocumentData | undefined { return this._documents.get(uri.toString()); } @@ -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 58ba2e568ef..8114ceb234a 100644 --- a/src/vs/workbench/api/node/extHostExtensionService.ts +++ b/src/vs/workbench/api/node/extHostExtensionService.ts @@ -116,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); @@ -222,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(); } @@ -342,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; } @@ -379,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; @@ -391,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}": [ @@ -409,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}": [ @@ -436,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([ @@ -760,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; } @@ -796,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 20f4f98a453..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(_handle: number, session: number): void { - let subscription = this._watches.get(session); + 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/extHostOutputService.ts b/src/vs/workbench/api/node/extHostOutputService.ts index e009ffb6dcb..25ebdd8137d 100644 --- a/src/vs/workbench/api/node/extHostOutputService.ts +++ b/src/vs/workbench/api/node/extHostOutputService.ts @@ -7,7 +7,7 @@ import { MainContext, MainThreadOutputServiceShape, IMainContext, ExtHostOutputS import * as vscode from 'vscode'; import { URI } from 'vs/base/common/uri'; import { join } from 'vs/base/common/path'; -import { OutputAppender } from 'vs/workbench/contrib/output/node/outputAppender'; +import { OutputAppender } from 'vs/workbench/services/output/node/outputAppender'; import { toLocalISOString } from 'vs/base/common/date'; import { Event, Emitter } from 'vs/base/common/event'; import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; 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 4e10c68feb2..45d953bb808 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -4,17 +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 { sanitizeProcessEnvironment } from 'vs/base/node/processes'; +import { sanitizeProcessEnvironment } from 'vs/base/common/processes'; const RENDERER_NO_PROCESS_ID = -1; @@ -409,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(); @@ -429,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 @@ -450,7 +460,7 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { // 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')); + 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); diff --git a/src/vs/workbench/api/node/extHostTextEditor.ts b/src/vs/workbench/api/node/extHostTextEditor.ts index 3cb2231f1b8..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; @@ -317,7 +317,7 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions { } // if (typeof newOptions.indentSize !== 'undefined') { - // let indentSize = this._validateIndentSize(newOptions.indentSize); + // const indentSize = this._validateIndentSize(newOptions.indentSize); // if (indentSize === 'tabSize') { // hasUpdate = true; // bulkConfigurationUpdate.indentSize = indentSize; @@ -330,7 +330,7 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions { // } 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..ce6b21dcc14 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 = id && 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 54006bf3829..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 { 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/extHostWorkspace.ts b/src/vs/workbench/api/node/extHostWorkspace.ts index edcbfaee62e..ad7f3b3097a 100644 --- a/src/vs/workbench/api/node/extHostWorkspace.ts +++ b/src/vs/workbench/api/node/extHostWorkspace.ts @@ -321,10 +321,10 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac return folders[0].uri.fsPath; } - getRelativePath(pathOrUri: string | vscode.Uri, includeWorkspace?: boolean): string | undefined { + getRelativePath(pathOrUri: string | vscode.Uri, includeWorkspace?: boolean): string { let resource: URI | undefined; - let path: string | undefined; + let path: string = ''; if (typeof pathOrUri === 'string') { resource = URI.file(pathOrUri); path = pathOrUri; @@ -354,7 +354,7 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac if (includeWorkspace && folder.name) { result = `${folder.name}/${result}`; } - return result; + return result!; } private trySetWorkspaceFolders(folders: vscode.WorkspaceFolder[]): void { @@ -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 fdff577ff72..c8be1e13669 100644 --- a/src/vs/workbench/browser/actions/workspaceActions.ts +++ b/src/vs/workbench/browser/actions/workspaceActions.ts @@ -9,12 +9,10 @@ import { IWindowService } from 'vs/platform/windows/common/windows'; import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry'; import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing'; -import { WORKSPACE_FILTER, IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; -import { mnemonicButtonLabel } from 'vs/base/common/labels'; +import { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { ADD_ROOT_FOLDER_COMMAND_ID, ADD_ROOT_FOLDER_LABEL, PICK_WORKSPACE_FOLDER_COMMAND_ID } from 'vs/workbench/browser/actions/workspaceCommands'; -import { URI } from 'vs/base/common/uri'; import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; import { INotificationService } from 'vs/platform/notification/common/notification'; @@ -132,15 +130,14 @@ export class SaveWorkspaceAsAction extends Action { id: string, label: string, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, - @IWorkspaceEditingService private readonly workspaceEditingService: IWorkspaceEditingService, - @IFileDialogService private readonly dialogService: IFileDialogService + @IWorkspaceEditingService private readonly workspaceEditingService: IWorkspaceEditingService ) { super(id, label); } run(): Promise { - return this.getNewWorkspaceConfigPath().then((configPathUri): Promise | void => { + return this.workspaceEditingService.pickNewWorkspacePath().then((configPathUri): Promise | void => { if (configPathUri) { switch (this.contextService.getWorkbenchState()) { case WorkbenchState.EMPTY: @@ -154,15 +151,6 @@ export class SaveWorkspaceAsAction extends Action { } }); } - - private getNewWorkspaceConfigPath(): Promise { - return this.dialogService.showSaveDialog({ - saveLabel: mnemonicButtonLabel(nls.localize({ key: 'save', comment: ['&& denotes a mnemonic'] }, "&&Save")), - title: nls.localize('saveWorkspace', "Save Workspace"), - filters: WORKSPACE_FILTER, - defaultUri: this.dialogService.defaultWorkspacePath() - }); - } } export class OpenWorkspaceAction extends Action { diff --git a/src/vs/workbench/browser/contextkeys.ts b/src/vs/workbench/browser/contextkeys.ts index 79e2037b343..e5e53f47ef6 100644 --- a/src/vs/workbench/browser/contextkeys.ts +++ b/src/vs/workbench/browser/contextkeys.ts @@ -17,7 +17,7 @@ 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 } from 'vs/workbench/common/viewlet'; +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'; @@ -39,6 +39,8 @@ export class WorkbenchContextKeysHandler extends Disposable { private inZenModeContext: IContextKey; private sideBarVisibleContext: IContextKey; + //TODO@Isidor remove in May + private sidebarVisibleContext: IContextKey; constructor( @IContextKeyService private contextKeyService: IContextKeyService, @@ -127,7 +129,8 @@ export class WorkbenchContextKeysHandler extends Disposable { this.inZenModeContext = InEditorZenModeContext.bindTo(this.contextKeyService); // Sidebar - this.sideBarVisibleContext = SidebarVisibleContext.bindTo(this.contextKeyService); + this.sideBarVisibleContext = SideBarVisibleContext.bindTo(this.contextKeyService); + this.sidebarVisibleContext = SidebarVisibleContext.bindTo(this.contextKeyService); } private updateEditorContextKeys(): void { @@ -204,5 +207,6 @@ export class WorkbenchContextKeysHandler extends Disposable { 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 9b036521f2e..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 | undefined, 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; @@ -180,7 +180,7 @@ export class ResourcesDropHandler { // Add external ones to recently open list unless dropped resource is a workspace const filesToAddToHistory = untitledOrFileResources.filter(d => d.isExternal && d.resource.scheme === Schemas.file).map(d => d.resource); if (filesToAddToHistory.length) { - this.windowsService.addRecentlyOpened(filesToAddToHistory); + this.windowsService.addRecentlyOpened([], [], filesToAddToHistory); } const editors: IResourceEditor[] = untitledOrFileResources.map(untitledOrFileResource => ({ @@ -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 */); } diff --git a/src/vs/workbench/browser/legacyLayout.ts b/src/vs/workbench/browser/legacyLayout.ts index 1d91550fcab..24b179fe2ad 100644 --- a/src/vs/workbench/browser/legacyLayout.ts +++ b/src/vs/workbench/browser/legacyLayout.ts @@ -3,8 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { QuickOpenController } from 'vs/workbench/browser/parts/quickopen/quickOpenController'; -import { QuickInputService } from 'vs/workbench/browser/parts/quickinput/quickInput'; import { Sash, ISashEvent, IVerticalSashLayoutProvider, IHorizontalSashLayoutProvider, Orientation } from 'vs/base/browser/ui/sash/sash'; import { IPartService, Position, ILayoutOptions, Parts } from 'vs/workbench/services/part/common/partService'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; @@ -14,8 +12,6 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { isMacintosh } from 'vs/base/common/platform'; import { memoize } from 'vs/base/common/decorators'; -import { NotificationsCenter } from 'vs/workbench/browser/parts/notifications/notificationsCenter'; -import { NotificationsToasts } from 'vs/workbench/browser/parts/notifications/notificationsToasts'; import { Dimension, getClientArea, size, position, hide, show } from 'vs/base/browser/dom'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { EditorPart } from 'vs/workbench/browser/parts/editor/editorPart'; @@ -78,10 +74,6 @@ export class WorkbenchLegacyLayout extends Disposable implements IVerticalSashLa panel: PanelPart, statusbar: StatusbarPart }, - private quickopen: QuickOpenController, - private quickInput: QuickInputService, - private notificationsCenter: NotificationsCenter, - private notificationsToasts: NotificationsToasts, @IStorageService private readonly storageService: IStorageService, @IContextViewService private readonly contextViewService: IContextViewService, @IPartService private readonly partService: IPartService, @@ -388,9 +380,9 @@ export class WorkbenchLegacyLayout extends Disposable implements IVerticalSashLa })); this._register(this.sashXOne.onDidReset(() => { - let activeViewlet = this.viewletService.getActiveViewlet(); - let optimalWidth = activeViewlet && activeViewlet.getOptimalWidth(); - this.sidebarWidth = Math.max(optimalWidth, DEFAULT_SIDEBAR_PART_WIDTH); + 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); @@ -596,10 +588,10 @@ export class WorkbenchLegacyLayout extends Disposable implements IVerticalSashLa 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); @@ -626,16 +618,6 @@ export class WorkbenchLegacyLayout extends Disposable implements IVerticalSashLa show(statusbarContainer); } - // Quick open - this.quickopen.layout(this.workbenchSize); - - // Quick input - this.quickInput.layout(this.workbenchSize); - - // Notifications - this.notificationsCenter.layout(this.workbenchSize); - this.notificationsToasts.layout(this.workbenchSize); - // Sashes this.sashXOne.layout(); if (panelPosition === Position.BOTTOM) { diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts index f54dadfb511..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 { @@ -161,14 +161,19 @@ 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) { - this.removeComposite(viewletDescriptor.id, true); // Update the composite bar by hiding + 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 + } } } } @@ -241,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, }; } @@ -311,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 */; } @@ -371,7 +376,7 @@ 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]; @@ -428,7 +433,7 @@ export class ActivitybarPart extends Part implements ISerializableView { 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) { @@ -471,7 +476,7 @@ export class ActivitybarPart extends Part implements ISerializableView { return result; } - private _cachedViewletsValue: string; + private _cachedViewletsValue: string | null; private get cachedViewletsValue(): string { if (!this._cachedViewletsValue) { this._cachedViewletsValue = this.getStoredCachedViewletsValue(); @@ -498,7 +503,7 @@ 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); 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/editor/breadcrumbsControl.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts index 1b9aad2e2fb..84acd81c48f 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts @@ -451,13 +451,15 @@ export class BreadcrumbsControl { } else if (element instanceof OutlineElement) { // open symbol in code editor const model = OutlineModel.get(element); - this._codeEditorService.openCodeEditor({ - resource: model.textModel.uri, - options: { - selection: Range.collapseToStart(element.symbol.selectionRange), - revealInCenterIfOutsideViewport: true - } - }, this._getActiveCodeEditor() || null, group === SIDE_GROUP); + 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 b0d991ac9eb..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 @@ -156,7 +157,7 @@ export abstract class BreadcrumbsPicker { 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 @@ -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/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/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index 4bcee05cf65..11037593d72 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -101,6 +101,9 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor private readonly _onDidActiveGroupChange: Emitter = this._register(new Emitter()); get onDidActiveGroupChange(): Event { return this._onDidActiveGroupChange.event; } + private readonly _onDidActivateGroup: Emitter = this._register(new Emitter()); + get onDidActivateGroup(): Event { return this._onDidActivateGroup.event; } + private readonly _onDidAddGroup: Emitter = this._register(new Emitter()); get onDidAddGroup(): Event { return this._onDidAddGroup.event; } @@ -117,9 +120,6 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor private readonly _onDidPreferredSizeChange: Emitter = this._register(new Emitter()); get onDidPreferredSizeChange(): Event { return this._onDidPreferredSizeChange.event; } - private readonly _onDidActivateGroup: Emitter = this._register(new Emitter()); - get onDidActivateGroup(): Event { return this._onDidActivateGroup.event; } - //#endregion private dimension: Dimension; 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/quickinput/quickInput.ts b/src/vs/workbench/browser/parts/quickinput/quickInput.ts index 8860f8046b7..7fa1be9a216 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInput.ts +++ b/src/vs/workbench/browser/parts/quickinput/quickInput.ts @@ -43,6 +43,8 @@ import { getIconClass } from 'vs/workbench/browser/parts/quickinput/quickInputUt import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; const $ = dom.$; @@ -816,7 +818,6 @@ export class QuickInputService extends Component implements IQuickInputService { private static readonly MAX_WIDTH = 600; // Max total width of quick open widget private idPrefix = 'quickInput_'; // Constant since there is still only one. - private layoutDimensions: dom.Dimension; private titleBar: HTMLElement; private filterContainer: HTMLElement; private visibleCountContainer: HTMLElement; @@ -846,12 +847,14 @@ export class QuickInputService extends Component implements IQuickInputService { @IContextKeyService private readonly contextKeyService: IContextKeyService, @IThemeService themeService: IThemeService, @IStorageService storageService: IStorageService, - @IAccessibilityService private readonly accessibilityService: IAccessibilityService + @IAccessibilityService private readonly accessibilityService: IAccessibilityService, + @ILayoutService private readonly layoutService: ILayoutService ) { super(QuickInputService.ID, themeService, storageService); this.inQuickOpenContext = InQuickOpenContextKey.bindTo(contextKeyService); this._register(this.quickOpenService.onShow(() => this.inQuickOpen('quickOpen', true))); this._register(this.quickOpenService.onHide(() => this.inQuickOpen('quickOpen', false))); + this._register(this.layoutService.onLayout(dimension => this.layout(dimension))); this.registerKeyModsListeners(); } @@ -1400,17 +1403,16 @@ export class QuickInputService extends Component implements IQuickInputService { } layout(dimension: dom.Dimension): void { - this.layoutDimensions = dimension; this.updateLayout(); } private updateLayout() { - if (this.layoutDimensions && this.ui) { + if (this.ui) { const titlebarOffset = this.partService.getTitleBarOffset(); this.ui.container.style.top = `${titlebarOffset}px`; const style = this.ui.container.style; - const width = Math.min(this.layoutDimensions.width * 0.62 /* golden cut */, QuickInputService.MAX_WIDTH); + const width = Math.min(this.layoutService.dimension.width * 0.62 /* golden cut */, QuickInputService.MAX_WIDTH); style.width = width + 'px'; style.marginLeft = '-' + (width / 2) + 'px'; @@ -1467,3 +1469,5 @@ export class BackAction extends Action { return Promise.resolve(); } } + +registerSingleton(IQuickInputService, QuickInputService, true); \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts b/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts index 3eb74651206..0e4c31da45f 100644 --- a/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts +++ b/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts @@ -50,6 +50,8 @@ import { timeout } from 'vs/base/common/async'; import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation'; import { IStorageService } from 'vs/platform/storage/common/storage'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; const HELP_PREFIX = '?'; @@ -73,7 +75,6 @@ export class QuickOpenController extends Component implements IQuickOpenService private lastInputValue: string; private lastSubmittedInputValue: string; private quickOpenWidget: QuickOpenWidget; - private dimension: Dimension; private mapResolvedHandlersToPrefix: { [prefix: string]: Promise; } = Object.create(null); private mapContextKeyToContext: { [id: string]: IContextKey; } = Object.create(null); private handlerOnOpenCalled: { [prefix: string]: boolean; } = Object.create(null); @@ -94,7 +95,8 @@ export class QuickOpenController extends Component implements IQuickOpenService @IPartService private readonly partService: IPartService, @IEnvironmentService private readonly environmentService: IEnvironmentService, @IThemeService themeService: IThemeService, - @IStorageService storageService: IStorageService + @IStorageService storageService: IStorageService, + @ILayoutService private readonly layoutService: ILayoutService ) { super(QuickOpenController.ID, themeService, storageService); @@ -109,6 +111,7 @@ export class QuickOpenController extends Component implements IQuickOpenService this._register(this.configurationService.onDidChangeConfiguration(() => this.updateConfiguration())); this._register(this.partService.onTitleBarVisibilityChange(() => this.positionQuickOpenWidget())); this._register(browser.onDidChangeZoomLevel(() => this.positionQuickOpenWidget())); + this._register(this.layoutService.onLayout(dimension => this.layout(dimension))); } private updateConfiguration(): void { @@ -195,9 +198,7 @@ export class QuickOpenController extends Component implements IQuickOpenService } // Layout - if (this.dimension) { - this.quickOpenWidget.layout(this.dimension); - } + this.quickOpenWidget.layout(this.layoutService.dimension); // Show quick open with prefix or editor history if (!this.quickOpenWidget.isVisible() || quickNavigateConfiguration) { @@ -624,9 +625,8 @@ export class QuickOpenController extends Component implements IQuickOpenService } layout(dimension: Dimension): void { - this.dimension = dimension; if (this.quickOpenWidget) { - this.quickOpenWidget.layout(this.dimension); + this.quickOpenWidget.layout(dimension); } } } @@ -863,3 +863,5 @@ export class RemoveFromEditorHistoryAction extends Action { }); } } + +registerSingleton(IQuickOpenService, QuickOpenController, true); \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts index d1d90205df5..f17cef29eb2 100644 --- a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts +++ b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts @@ -199,14 +199,14 @@ 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); } 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 768cbe3ef10..d4fa3b061d8 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -180,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 @@ -283,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) : ''; @@ -473,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'); @@ -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 55b19d00586..804e934a8ce 100644 --- a/src/vs/workbench/browser/parts/views/viewsViewlet.ts +++ b/src/vs/workbench/browser/parts/views/viewsViewlet.ts @@ -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/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 bd9740edb23..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; } @@ -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 b8279be7d5c..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, IActiveTextEditorModel } 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'; @@ -135,7 +135,7 @@ export abstract class BaseTextEditorModel extends EditorModel implements ITextEd return null; } - isResolved(): this is IActiveTextEditorModel { + 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/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 b213967decd..09836ae5e8a 100644 --- a/src/vs/workbench/common/viewlet.ts +++ b/src/vs/workbench/common/viewlet.ts @@ -7,6 +7,7 @@ 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', ''); 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/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index 89c6088288c..000458a22fc 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -112,7 +112,7 @@ export interface IExpression extends IReplElement, IExpressionContainer { export interface IDebugger { createDebugAdapter(session: IDebugSession, outputService: IOutputService): Promise; runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments): Promise; - getCustomTelemetryService(): Promise; + getCustomTelemetryService(): Promise; } export const enum State { @@ -152,7 +152,7 @@ export interface IDebugSession extends ITreeElement { getLabel(): string; getSourceForUri(modelUri: uri): Source; - getSource(raw: DebugProtocol.Source): Source; + getSource(raw?: DebugProtocol.Source): Source; setConfiguration(configuration: { resolved: IConfig, unresolved: IConfig }): void; rawUpdate(data: IRawModelUpdate): void; @@ -199,7 +199,7 @@ export interface IDebugSession extends ITreeElement { stackTrace(threadId: number, startFrame: number, levels: number): Promise; exceptionInfo(threadId: number): Promise; scopes(frameId: number): Promise; - variables(variablesReference: number, filter: 'indexed' | 'named', start: number, count: number): Promise; + variables(variablesReference: number | undefined, filter: 'indexed' | 'named' | undefined, start: number | undefined, count: number | undefined): Promise; evaluate(expression: string, frameId?: number, context?: string): Promise; customRequest(request: string, args: any): Promise; @@ -214,7 +214,7 @@ export interface IDebugSession extends ITreeElement { terminateThreads(threadIds: number[]): Promise; completions(frameId: number, text: string, position: Position, overwriteBefore: number): Promise; - setVariable(variablesReference: number, name: string, value: string): Promise; + setVariable(variablesReference: number | undefined, name: string, value: string): Promise; loadSource(resource: uri): Promise; getLoadedSources(): Promise; } @@ -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. @@ -282,7 +284,7 @@ export interface IScope extends IExpressionContainer { export interface IStackFrame extends ITreeElement { readonly thread: IThread; readonly name: string; - readonly presentationHint: string; + readonly presentationHint: string | undefined; readonly frameId: number; readonly range: IRange; readonly source: Source; @@ -317,9 +319,9 @@ export interface IBreakpointUpdateData { } export interface IBaseBreakpoint extends IEnablement { - readonly condition: string; - readonly hitCondition: string; - readonly logMessage: string; + readonly condition?: string; + readonly hitCondition?: string; + readonly logMessage?: string; readonly verified: boolean; readonly idFromAdapter: number | undefined; } @@ -328,7 +330,7 @@ export interface IBreakpoint extends IBaseBreakpoint { readonly uri: uri; readonly lineNumber: number; readonly endLineNumber?: number; - readonly column: number; + readonly column?: number; readonly endColumn?: number; readonly message?: string; readonly adapterData: any; @@ -392,7 +394,7 @@ export interface IDebugModel extends ITreeElement { getExceptionBreakpoints(): ReadonlyArray; getWatchExpressions(): ReadonlyArray; - onDidChangeBreakpoints: Event; + onDidChangeBreakpoints: Event; onDidChangeCallStack: Event; onDidChangeWatchExpressions: Event; } @@ -514,7 +516,7 @@ export interface IPlatformSpecificAdapterContribution { } export interface IDebuggerContribution extends IPlatformSpecificAdapterContribution { - type?: string; + type: string; label?: string; // debug adapter executable adapterExecutableCommand?: string; diff --git a/src/vs/workbench/contrib/debug/common/debugModel.ts b/src/vs/workbench/contrib/debug/common/debugModel.ts index 36f80abb7f8..0f833db72af 100644 --- a/src/vs/workbench/contrib/debug/common/debugModel.ts +++ b/src/vs/workbench/contrib/debug/common/debugModel.ts @@ -29,7 +29,7 @@ export class SimpleReplElement implements IReplElement { private id: string, public value: string, public severity: severity, - public sourceData: IReplElementSource, + public sourceData?: IReplElementSource, ) { } toString(): string { @@ -98,19 +98,19 @@ export class ExpressionContainer implements IExpressionContainer { protected children?: Promise; constructor( - protected session: IDebugSession, - private _reference: number, + protected session: IDebugSession | undefined, + private _reference: number | undefined, private id: string, - public namedVariables = 0, - public indexedVariables = 0, - private startOfVariables = 0 + public namedVariables: number | undefined = 0, + public indexedVariables: number | undefined = 0, + private startOfVariables: number | undefined = 0 ) { } - get reference(): number { + get reference(): number | undefined { return this._reference; } - set reference(value: number) { + set reference(value: number | undefined) { this._reference = value; this.children = undefined; // invalidate children cache } @@ -137,17 +137,17 @@ export class ExpressionContainer implements IExpressionContainer { return childrenThenable.then(childrenArray => { // Use a dynamic chunk size based on the number of elements #9774 let chunkSize = ExpressionContainer.BASE_CHUNK_SIZE; - while (this.indexedVariables > chunkSize * ExpressionContainer.BASE_CHUNK_SIZE) { + while (!!this.indexedVariables && this.indexedVariables > chunkSize * ExpressionContainer.BASE_CHUNK_SIZE) { chunkSize *= ExpressionContainer.BASE_CHUNK_SIZE; } - if (this.indexedVariables > chunkSize) { + if (!!this.indexedVariables && this.indexedVariables > chunkSize) { // There are a lot of children, create fake intermediate values that represent chunks #9537 const numberOfChunks = Math.ceil(this.indexedVariables / chunkSize); for (let i = 0; i < numberOfChunks; i++) { - const start = this.startOfVariables + i * chunkSize; + const start = (this.startOfVariables || 0) + i * chunkSize; const count = Math.min(chunkSize, this.indexedVariables - i * chunkSize); - childrenArray.push(new Variable(this.session, this, this.reference, `[${start}..${start + count - 1}]`, '', '', null, count, { kind: 'virtual' }, null, true, start)); + childrenArray.push(new Variable(this.session, this, this.reference, `[${start}..${start + count - 1}]`, '', '', undefined, count, { kind: 'virtual' }, undefined, true, start)); } return childrenArray; @@ -168,16 +168,16 @@ export class ExpressionContainer implements IExpressionContainer { get hasChildren(): boolean { // only variables with reference > 0 have children. - return this.reference > 0; + return !!this.reference && this.reference > 0; } - private fetchVariables(start: number, count: number, filter: 'indexed' | 'named'): Promise { - return this.session.variables(this.reference, filter, start, count).then(response => { + private fetchVariables(start: number | undefined, count: number | undefined, filter: 'indexed' | 'named' | undefined): Promise { + return this.session!.variables(this.reference, filter, start, count).then(response => { return response && response.body && response.body.variables - ? distinct(response.body.variables.filter(v => !!v && isString(v.name)), v => v.name).map( - v => new Variable(this.session, this, v.variablesReference, v.name, v.evaluateName, v.value, v.namedVariables, v.indexedVariables, v.presentationHint, v.type)) + ? distinct(response.body.variables.filter(v => !!v && isString(v.name)), (v: DebugProtocol.Variable) => v.name).map((v: DebugProtocol.Variable) => + new Variable(this.session, this, v.variablesReference, v.name, v.evaluateName, v.value, v.namedVariables, v.indexedVariables, v.presentationHint, v.type)) : []; - }, (e: Error) => [new Variable(this.session, this, 0, e.message, e.message, '', 0, 0, { kind: 'virtual' }, null, false)]); + }, (e: Error) => [new Variable(this.session, this, 0, e.message, e.message, '', 0, 0, { kind: 'virtual' }, undefined, false)]); } // The adapter explicitly sents the children count of an expression only if there are lots of children which should be chunked. @@ -204,7 +204,7 @@ export class Expression extends ExpressionContainer implements IExpression { public type: string; constructor(public name: string, id = generateUuid()) { - super(null, 0, id); + super(undefined, 0, id); this.available = false; // name is not set if the expression is just being added // in that case do not set default value to prevent flashing #14499 @@ -230,7 +230,7 @@ export class Expression extends ExpressionContainer implements IExpression { this.reference = response.body.variablesReference; this.namedVariables = response.body.namedVariables; this.indexedVariables = response.body.indexedVariables; - this.type = response.body.type; + this.type = response.body.type || this.type; } }, err => { this.value = err.message; @@ -250,15 +250,15 @@ export class Variable extends ExpressionContainer implements IExpression { public errorMessage: string; constructor( - session: IDebugSession, + session: IDebugSession | undefined, public parent: IExpressionContainer, - reference: number, + reference: number | undefined, public name: string, - public evaluateName: string, + public evaluateName: string | undefined, value: string, - namedVariables: number, - indexedVariables: number, - public presentationHint: DebugProtocol.VariablePresentationHint, + namedVariables: number | undefined, + indexedVariables: number | undefined, + public presentationHint: DebugProtocol.VariablePresentationHint | undefined, public type: string | undefined = undefined, public available = true, startOfVariables = 0 @@ -268,6 +268,10 @@ export class Variable extends ExpressionContainer implements IExpression { } setVariable(value: string): Promise { + if (!this.session) { + return Promise.resolve(undefined); + } + return this.session.setVariable((this.parent).reference, this.name, value).then(response => { if (response && response.body) { this.value = response.body.value; @@ -294,8 +298,8 @@ export class Scope extends ExpressionContainer implements IScope { public name: string, reference: number, public expensive: boolean, - namedVariables: number, - indexedVariables: number, + namedVariables?: number, + indexedVariables?: number, public range?: IRange ) { super(stackFrame.thread.session, reference, `scope:${stackFrame.getId()}:${name}:${index}`, namedVariables, indexedVariables); @@ -315,7 +319,7 @@ export class StackFrame implements IStackFrame { public frameId: number, public source: Source, public name: string, - public presentationHint: string, + public presentationHint: string | undefined, public range: IRange, private index: number ) { @@ -331,7 +335,7 @@ export class StackFrame implements IStackFrame { this.scopes = this.thread.session.scopes(this.frameId).then(response => { return response && response.body && response.body.scopes ? response.body.scopes.map((rs, index) => new Scope(this, index, rs.name, rs.variablesReference, rs.expensive, rs.namedVariables, rs.indexedVariables, - rs.line && rs.column && rs.endLine && rs.endColumn ? new Range(rs.line, rs.column, rs.endLine, rs.endColumn) : null)) : []; + rs.line && rs.column && rs.endLine && rs.endColumn ? new Range(rs.line, rs.column, rs.endLine, rs.endColumn) : undefined)) : []; }, err => []); } @@ -393,7 +397,6 @@ export class Thread implements IThread { public stopped: boolean; constructor(public session: IDebugSession, public name: string, public threadId: number) { - this.stoppedDetails = null; this.callStack = []; this.staleCallStack = []; this.stopped = false; @@ -418,6 +421,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. @@ -456,8 +468,8 @@ export class Thread implements IThread { return new StackFrame(this, rsf.id, source, rsf.name, rsf.presentationHint, new Range( rsf.line, rsf.column, - rsf.endLine, - rsf.endColumn + rsf.endLine || rsf.line, + rsf.endColumn || rsf.column ), startFrame + index); }); }, (err: Error) => { @@ -536,9 +548,9 @@ export class BaseBreakpoint extends Enablement implements IBaseBreakpoint { constructor( enabled: boolean, - public hitCondition: string, - public condition: string, - public logMessage: string, + public hitCondition: string | undefined, + public condition: string | undefined, + public logMessage: string | undefined, id: string ) { super(enabled, id); @@ -585,11 +597,11 @@ export class Breakpoint extends BaseBreakpoint implements IBreakpoint { constructor( public uri: uri, private _lineNumber: number, - private _column: number, + private _column: number | undefined, enabled: boolean, - condition: string, - hitCondition: string, - logMessage: string, + condition: string | undefined, + hitCondition: string | undefined, + logMessage: string | undefined, private _adapterData: any, private textFileService: ITextFileService, id = generateUuid() @@ -611,7 +623,7 @@ export class Breakpoint extends BaseBreakpoint implements IBreakpoint { return true; } - get column(): number { + get column(): number | undefined { const data = this.getSessionData(); // Only respect the column if the user explictly set the column to have an inline breakpoint return data && typeof data.column === 'number' && typeof this._column === 'number' ? data.column : this._column; @@ -689,9 +701,9 @@ export class FunctionBreakpoint extends BaseBreakpoint implements IFunctionBreak constructor( public name: string, enabled: boolean, - hitCondition: string, - condition: string, - logMessage: string, + hitCondition: string | undefined, + condition: string | undefined, + logMessage: string | undefined, id = generateUuid() ) { super(enabled, hitCondition, condition, logMessage, id); @@ -789,7 +801,7 @@ export class DebugModel implements IDebugModel { this._onDidChangeCallStack.fire(undefined); } - get onDidChangeBreakpoints(): Event { + get onDidChangeBreakpoints(): Event { return this._onDidChangeBreakpoints.event; } @@ -823,7 +835,7 @@ export class DebugModel implements IDebugModel { fetchCallStack(thread: Thread): { topCallStack: Promise, wholeCallStack: Promise } { if (thread.session.capabilities.supportsDelayedStackTraceLoading) { // For improved performance load the first stack frame and then load the rest async. - let topCallStack: Promise; + let topCallStack = Promise.resolve(); const wholeCallStack = new Promise((c, e) => { topCallStack = thread.fetchCallStack(1).then(() => { if (!this.schedulers.has(thread.getId())) { @@ -904,7 +916,7 @@ export class DebugModel implements IDebugModel { } addBreakpoints(uri: uri, rawData: IBreakpointData[], fireEvent = true): IBreakpoint[] { - const newBreakpoints = rawData.map(rawBp => new Breakpoint(uri, rawBp.lineNumber, rawBp.column, rawBp.enabled, rawBp.condition, rawBp.hitCondition, rawBp.logMessage, undefined, this.textFileService, rawBp.id)); + const newBreakpoints = rawData.map(rawBp => new Breakpoint(uri, rawBp.lineNumber, rawBp.column, !!rawBp.enabled, rawBp.condition, rawBp.hitCondition, rawBp.logMessage, undefined, this.textFileService, rawBp.id)); newBreakpoints.forEach(bp => bp.setSessionId(this.breakpointsSessionId)); this.breakpoints = this.breakpoints.concat(newBreakpoints); this.breakpointsActivated = true; @@ -970,7 +982,10 @@ export class DebugModel implements IDebugModel { return resources.basenameOrAuthority(first.uri).localeCompare(resources.basenameOrAuthority(second.uri)); } if (first.lineNumber === second.lineNumber) { - return first.column - second.column; + if (first.column && second.column) { + return first.column - second.column; + } + return -1; } return first.lineNumber - second.lineNumber; @@ -1028,7 +1043,7 @@ export class DebugModel implements IDebugModel { removeFunctionBreakpoints(id?: string): void { - let removed: IFunctionBreakpoint[]; + let removed: FunctionBreakpoint[]; if (id) { removed = this.functionBreakpoints.filter(fbp => fbp.getId() === id); this.functionBreakpoints = this.functionBreakpoints.filter(fbp => fbp.getId() !== id); @@ -1066,10 +1081,11 @@ export class DebugModel implements IDebugModel { moveWatchExpression(id: string, position: number): void { const we = this.watchExpressions.filter(we => we.getId() === id).pop(); - this.watchExpressions = this.watchExpressions.filter(we => we.getId() !== id); - this.watchExpressions = this.watchExpressions.slice(0, position).concat(we, this.watchExpressions.slice(position)); - - this._onDidChangeWatchExpressions.fire(undefined); + if (we) { + this.watchExpressions = this.watchExpressions.filter(we => we.getId() !== id); + this.watchExpressions = this.watchExpressions.slice(0, position).concat(we, this.watchExpressions.slice(position)); + this._onDidChangeWatchExpressions.fire(undefined); + } } sourceIsNotAvailable(uri: uri): void { diff --git a/src/vs/workbench/contrib/debug/common/debugSource.ts b/src/vs/workbench/contrib/debug/common/debugSource.ts index 8efc84301ea..d9b9e1d1a6d 100644 --- a/src/vs/workbench/contrib/debug/common/debugSource.ts +++ b/src/vs/workbench/contrib/debug/common/debugSource.ts @@ -33,10 +33,12 @@ export class Source { public readonly uri: uri; public available: boolean; + public raw: DebugProtocol.Source; - constructor(public raw: DebugProtocol.Source, sessionId: string) { + constructor(raw_: DebugProtocol.Source | undefined, sessionId: string) { let path: string; - if (raw) { + if (raw_) { + this.raw = raw_; path = this.raw.path || this.raw.name || ''; this.available = true; } else { diff --git a/src/vs/workbench/contrib/debug/common/replModel.ts b/src/vs/workbench/contrib/debug/common/replModel.ts index c4556ec3eca..e8c1cd2580f 100644 --- a/src/vs/workbench/contrib/debug/common/replModel.ts +++ b/src/vs/workbench/contrib/debug/common/replModel.ts @@ -46,7 +46,7 @@ export class ReplModel { // remove potential empty lines between different repl types this.replElements.pop(); } else if (previousElement instanceof SimpleReplElement && sev === previousElement.severity && toAdd.length && toAdd[0].sourceData === previousElement.sourceData) { - previousElement.value += toAdd.shift().value; + previousElement.value += toAdd.shift()!.value; } this.addReplElements(toAdd); } else { 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/debugSession.ts b/src/vs/workbench/contrib/debug/electron-browser/debugSession.ts index 3582ba6a3f1..cd82b55507d 100644 --- a/src/vs/workbench/contrib/debug/electron-browser/debugSession.ts +++ b/src/vs/workbench/contrib/debug/electron-browser/debugSession.ts @@ -785,7 +785,7 @@ export class DebugSession implements IDebugSession { return this.sources.get(this.getUriKey(uri)); } - getSource(raw: DebugProtocol.Source): Source { + getSource(raw?: DebugProtocol.Source): Source { let source = new Source(raw, this.getId()); const uriKey = this.getUriKey(source.uri); const found = this.sources.get(uriKey); diff --git a/src/vs/workbench/contrib/debug/node/debugAdapter.ts b/src/vs/workbench/contrib/debug/node/debugAdapter.ts index 1f0a6b7e6dc..2f3845662d4 100644 --- a/src/vs/workbench/contrib/debug/node/debugAdapter.ts +++ b/src/vs/workbench/contrib/debug/node/debugAdapter.ts @@ -435,33 +435,35 @@ export class ExecutableDebugAdapter extends StreamDebugAdapter { } } - private static extract(contribution: IDebuggerContribution, extensionFolderPath: string): IDebuggerContribution | undefined { - if (!contribution) { + private static extract(platformContribution: IPlatformSpecificAdapterContribution, extensionFolderPath: string): IDebuggerContribution | undefined { + if (!platformContribution) { return undefined; } const result: IDebuggerContribution = Object.create(null); - if (contribution.runtime) { - if (contribution.runtime.indexOf('./') === 0) { // TODO - result.runtime = path.join(extensionFolderPath, contribution.runtime); + if (platformContribution.runtime) { + if (platformContribution.runtime.indexOf('./') === 0) { // TODO + result.runtime = path.join(extensionFolderPath, platformContribution.runtime); } else { - result.runtime = contribution.runtime; + result.runtime = platformContribution.runtime; } } - if (contribution.runtimeArgs) { - result.runtimeArgs = contribution.runtimeArgs; + if (platformContribution.runtimeArgs) { + result.runtimeArgs = platformContribution.runtimeArgs; } - if (contribution.program) { - if (!path.isAbsolute(contribution.program)) { - result.program = path.join(extensionFolderPath, contribution.program); + if (platformContribution.program) { + if (!path.isAbsolute(platformContribution.program)) { + result.program = path.join(extensionFolderPath, platformContribution.program); } else { - result.program = contribution.program; + result.program = platformContribution.program; } } - if (contribution.args) { - result.args = contribution.args; + if (platformContribution.args) { + result.args = platformContribution.args; } + const contribution = platformContribution as IDebuggerContribution; + if (contribution.win) { result.win = ExecutableDebugAdapter.extract(contribution.win, extensionFolderPath); } @@ -490,7 +492,7 @@ export class ExecutableDebugAdapter extends StreamDebugAdapter { const debuggers = ed.contributes['debuggers']; if (debuggers && debuggers.length > 0) { debuggers.filter(dbg => typeof dbg.type === 'string' && strings.equalsIgnoreCase(dbg.type, debugType)).forEach(dbg => { - // extract relevant attributes and make then absolute where needed + // extract relevant attributes and make them absolute where needed const extractedDbg = ExecutableDebugAdapter.extract(dbg, ed.extensionLocation.fsPath); // merge diff --git a/src/vs/workbench/contrib/debug/node/debugger.ts b/src/vs/workbench/contrib/debug/node/debugger.ts index a8072f51aab..0b858507e55 100644 --- a/src/vs/workbench/contrib/debug/node/debugger.ts +++ b/src/vs/workbench/contrib/debug/node/debugger.ts @@ -31,7 +31,7 @@ import { isDebuggerMainContribution } from 'vs/workbench/contrib/debug/common/de export class Debugger implements IDebugger { - private debuggerContribution: IDebuggerContribution = {}; + private debuggerContribution: IDebuggerContribution; private mergedExtensionDescriptions: IExtensionDescription[] = []; private mainExtensionDescription: IExtensionDescription | undefined; @@ -42,6 +42,7 @@ export class Debugger implements IDebugger { @IConfigurationResolverService private readonly configurationResolverService: IConfigurationResolverService, @ITelemetryService private readonly telemetryService: ITelemetryService, ) { + this.debuggerContribution = { type: dbgContribution.type }; this.merge(dbgContribution, extensionDescription); } @@ -149,12 +150,15 @@ export class Debugger implements IDebugger { if (this.debuggerContribution.adapterExecutableCommand) { console.info('debugAdapterExecutable attribute in package.json is deprecated and support for it will be removed soon; please use DebugAdapterDescriptorFactory.createDebugAdapterDescriptor instead.'); const rootFolder = session.root ? session.root.uri.toString() : undefined; - return this.commandService.executeCommand(this.debuggerContribution.adapterExecutableCommand, rootFolder).then((ae: { command: string, args: string[] }) => { - return { - type: 'executable', - command: ae.command, - args: ae.args || [] - }; + return this.commandService.executeCommand(this.debuggerContribution.adapterExecutableCommand, rootFolder).then(ae => { + if (ae) { + return { + type: 'executable', + command: ae.command, + args: ae.args || [] + }; + } + throw new Error('command adapterExecutableCommand did not return proper command.'); }); } @@ -197,15 +201,15 @@ export class Debugger implements IDebugger { return this.debuggerContribution.type; } - get variables(): { [key: string]: string } { + get variables(): { [key: string]: string } | undefined { return this.debuggerContribution.variables; } - get configurationSnippets(): IJSONSchemaSnippet[] { + get configurationSnippets(): IJSONSchemaSnippet[] | undefined { return this.debuggerContribution.configurationSnippets; } - get languages(): string[] { + get languages(): string[] | undefined { return this.debuggerContribution.languages; } @@ -254,8 +258,11 @@ export class Debugger implements IDebugger { } @memoize - getCustomTelemetryService(): Promise { - if (!this.debuggerContribution.aiKey) { + getCustomTelemetryService(): Promise { + + const aiKey = this.debuggerContribution.aiKey; + + if (!aiKey) { return Promise.resolve(undefined); } @@ -270,7 +277,7 @@ export class Debugger implements IDebugger { { serverName: 'Debug Telemetry', timeout: 1000 * 60 * 5, - args: [`${this.getMainExtensionDescriptor().publisher}.${this.type}`, JSON.stringify(data), this.debuggerContribution.aiKey], + args: [`${this.getMainExtensionDescriptor().publisher}.${this.type}`, JSON.stringify(data), aiKey], env: { ELECTRON_RUN_AS_NODE: 1, PIPE_LOGGING: 'true', @@ -286,10 +293,12 @@ export class Debugger implements IDebugger { }); } - getSchemaAttributes(): IJSONSchema[] { + getSchemaAttributes(): IJSONSchema[] | null { + if (!this.debuggerContribution.configurationAttributes) { return null; } + // fill in the default configuration attributes shared by all adapters. const taskSchema = TaskDefinitionRegistry.getJsonSchema(); return Object.keys(this.debuggerContribution.configurationAttributes).map(request => { @@ -339,9 +348,9 @@ export class Debugger implements IDebugger { }; properties['internalConsoleOptions'] = INTERNAL_CONSOLE_OPTIONS_SCHEMA; // Clear out windows, linux and osx fields to not have cycles inside the properties object - properties['windows'] = undefined; - properties['osx'] = undefined; - properties['linux'] = undefined; + delete properties['windows']; + delete properties['osx']; + delete properties['linux']; const osProperties = objects.deepClone(properties); properties['windows'] = { @@ -359,11 +368,10 @@ export class Debugger implements IDebugger { description: nls.localize('debugLinuxConfiguration', "Linux specific launch configuration attributes."), properties: osProperties }; - Object.keys(attributes.properties).forEach(name => { + Object.keys(properties).forEach(name => { // Use schema allOf property to get independent error reporting #21113 - ConfigurationResolverUtils.applyDeprecatedVariableMessage(attributes.properties[name]); + ConfigurationResolverUtils.applyDeprecatedVariableMessage(properties[name]); }); - return attributes; }); } diff --git a/src/vs/workbench/contrib/debug/node/terminals.ts b/src/vs/workbench/contrib/debug/node/terminals.ts index f399b6bb8d9..3bb371f4b5b 100644 --- a/src/vs/workbench/contrib/debug/node/terminals.ts +++ b/src/vs/workbench/contrib/debug/node/terminals.ts @@ -33,7 +33,7 @@ export function getDefaultTerminalLinuxReady(): Promise { if (!_DEFAULT_TERMINAL_LINUX_READY) { _DEFAULT_TERMINAL_LINUX_READY = new Promise(c => { if (env.isLinux) { - Promise.all([pfs.exists('/etc/debian_version'), process.lazyEnv]).then(([isDebian]) => { + Promise.all([pfs.exists('/etc/debian_version'), process.lazyEnv]).then(([isDebian]) => { if (isDebian) { c('x-terminal-emulator'); } else if (process.env.DESKTOP_SESSION === 'gnome' || process.env.DESKTOP_SESSION === 'gnome-classic') { @@ -67,19 +67,18 @@ export function getDefaultTerminalWindows(): string { } abstract class TerminalLauncher implements ITerminalLauncher { - public runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): Promise { - return this.runInTerminal0(args.title, args.cwd, args.args, args.env || {}, config); - } - runInTerminal0(title: string, dir: string, args: string[], envVars: env.IProcessEnvironment, config): Promise { - return undefined; + runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): Promise { + return this.runInTerminal0(args.title!, args.cwd, args.args, args.env || {}, config); } + + abstract runInTerminal0(title: string, dir: string, args: string[], envVars: env.IProcessEnvironment | {}, config): Promise; } class WinTerminalService extends TerminalLauncher { private static readonly CMD = 'cmd.exe'; - public runInTerminal0(title: string, dir: string, args: string[], envVars: env.IProcessEnvironment, configuration: ITerminalSettings): Promise { + runInTerminal0(title: string, dir: string, args: string[], envVars: env.IProcessEnvironment, configuration: ITerminalSettings): Promise { const exec = configuration.external.windowsExec || getDefaultTerminalWindows(); @@ -117,7 +116,7 @@ class MacTerminalService extends TerminalLauncher { private static readonly DEFAULT_TERMINAL_OSX = 'Terminal.app'; private static readonly OSASCRIPT = '/usr/bin/osascript'; // osascript is the AppleScript interpreter on OS X - public runInTerminal0(title: string, dir: string, args: string[], envVars: env.IProcessEnvironment, configuration: ITerminalSettings): Promise { + runInTerminal0(title: string, dir: string, args: string[], envVars: env.IProcessEnvironment, configuration: ITerminalSettings): Promise { const terminalApp = configuration.external.osxExec || MacTerminalService.DEFAULT_TERMINAL_OSX; @@ -184,7 +183,7 @@ class LinuxTerminalService extends TerminalLauncher { private static readonly WAIT_MESSAGE = nls.localize('press.any.key', "Press any key to continue..."); - public runInTerminal0(title: string, dir: string, args: string[], envVars: env.IProcessEnvironment, configuration: ITerminalSettings): Promise { + runInTerminal0(title: string, dir: string, args: string[], envVars: env.IProcessEnvironment, configuration: ITerminalSettings): Promise { const terminalConfig = configuration.external; const execThenable: Promise = terminalConfig.linuxExec ? Promise.resolve(terminalConfig.linuxExec) : getDefaultTerminalLinuxReady(); @@ -348,7 +347,7 @@ export function prepareCommand(args: DebugProtocol.RunInTerminalRequestArguments } } if (args.args && args.args.length > 0) { - const cmd = quote(args.args.shift()); + const cmd = quote(args.args.shift()!); command += (cmd[0] === '\'') ? `& ${cmd} ` : `${cmd} `; for (let a of args.args) { command += `${quote(a)} `; 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/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts index 9611f624c74..689e0509b46 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts @@ -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 51177b7ca33..696f13d9b52 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensionsViews.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensionsViews.ts @@ -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> { @@ -728,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/externalTerminal/electron-browser/externalTerminal.contribution.ts b/src/vs/workbench/contrib/externalTerminal/electron-browser/externalTerminal.contribution.ts index d72561ce0a0..34c5c845166 100644 --- a/src/vs/workbench/contrib/externalTerminal/electron-browser/externalTerminal.contribution.ts +++ b/src/vs/workbench/contrib/externalTerminal/electron-browser/externalTerminal.contribution.ts @@ -91,7 +91,7 @@ CommandsRegistry.registerCommand({ 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') { const instance = integratedTerminalService.createTerminal({ cwd: dir }, true); 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 56e4ac1979b..854da7251dc 100644 --- a/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor.ts +++ b/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor.ts @@ -57,7 +57,7 @@ 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; diff --git a/src/vs/workbench/contrib/files/browser/editors/fileEditorTracker.ts b/src/vs/workbench/contrib/files/browser/editors/fileEditorTracker.ts index c281e9d0bfb..cb15ee33d26 100644 --- a/src/vs/workbench/contrib/files/browser/editors/fileEditorTracker.ts +++ b/src/vs/workbench/contrib/files/browser/editors/fileEditorTracker.ts @@ -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); } @@ -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)); } diff --git a/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts b/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts index 5e1a9623ea0..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 { @@ -198,7 +198,7 @@ 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: [ @@ -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 c3861633546..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'; @@ -321,7 +321,7 @@ export const acceptLocalChangesCommand = (accessor: ServicesAccessor, resource: 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 diff --git a/src/vs/workbench/contrib/files/browser/views/explorerView.ts b/src/vs/workbench/contrib/files/browser/views/explorerView.ts index 69a8b7bb575..94c206c6ebf 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerView.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerView.ts @@ -176,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(); diff --git a/src/vs/workbench/contrib/files/common/explorerService.ts b/src/vs/workbench/contrib/files/common/explorerService.ts index 980c9b14035..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 { 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/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 86d3df7c5ea..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 { @@ -260,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, @@ -320,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, @@ -334,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) @@ -355,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; } })); @@ -426,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); } @@ -569,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)) { @@ -597,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/electron-browser/output.contribution.ts b/src/vs/workbench/contrib/output/browser/output.contribution.ts similarity index 99% rename from src/vs/workbench/contrib/output/electron-browser/output.contribution.ts rename to src/vs/workbench/contrib/output/browser/output.contribution.ts index 65e36ae6aab..273ac9f397d 100644 --- a/src/vs/workbench/contrib/output/electron-browser/output.contribution.ts +++ b/src/vs/workbench/contrib/output/browser/output.contribution.ts @@ -10,7 +10,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; 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 { OutputService, LogContentProvider } from 'vs/workbench/contrib/output/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'; diff --git a/src/vs/workbench/contrib/output/browser/outputServices.ts b/src/vs/workbench/contrib/output/browser/outputServices.ts new file mode 100644 index 00000000000..9f545ac8384 --- /dev/null +++ b/src/vs/workbench/contrib/output/browser/outputServices.ts @@ -0,0 +1,291 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { Event, Emitter } from 'vs/base/common/event'; +import { URI } from 'vs/base/common/uri'; +import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +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, LOG_SCHEME, CONTEXT_ACTIVE_LOG_OUTPUT, LOG_MIME, OUTPUT_MIME } 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 { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { OutputLinkProvider } from 'vs/workbench/contrib/output/common/outputLinkProvider'; +import { ITextModelService, ITextModelContentProvider } from 'vs/editor/common/services/resolverService'; +import { ITextModel } from 'vs/editor/common/model'; +import { IPanel } from 'vs/workbench/common/panel'; +import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IWindowService } from 'vs/platform/windows/common/windows'; +import { ILogService } from 'vs/platform/log/common/log'; +import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { IOutputChannelModel, IOutputChannelModelService } from 'vs/workbench/services/output/common/outputChannelModel'; + +const OUTPUT_ACTIVE_CHANNEL_KEY = 'output.activechannel'; + +class OutputChannel extends Disposable implements IOutputChannel { + + scrollLock: boolean = false; + readonly model: IOutputChannelModel; + readonly id: string; + readonly label: string; + + constructor( + readonly outputChannelDescriptor: IOutputChannelDescriptor, + @IOutputChannelModelService outputChannelModelService: IOutputChannelModelService + ) { + super(); + this.id = outputChannelDescriptor.id; + this.label = outputChannelDescriptor.label; + this.model = this._register(outputChannelModelService.createOutputChannelModel(this.id, URI.from({ scheme: OUTPUT_SCHEME, path: this.id }), outputChannelDescriptor.log ? LOG_MIME : OUTPUT_MIME, outputChannelDescriptor.file)); + } + + append(output: string): void { + this.model.append(output); + } + + update(): void { + this.model.update(); + } + + clear(till?: number): void { + this.model.clear(till); + } +} + +export class OutputService extends Disposable implements IOutputService, ITextModelContentProvider { + + public _serviceBrand: any; + + private channels: Map = new Map(); + private activeChannelIdInStorage: string; + private activeChannel: OutputChannel | null; + + private readonly _onActiveOutputChannel = new Emitter(); + readonly onActiveOutputChannel: Event = this._onActiveOutputChannel.event; + + private _outputPanel: OutputPanel; + + constructor( + @IStorageService private readonly storageService: IStorageService, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @IPanelService private readonly panelService: IPanelService, + @IWorkspaceContextService contextService: IWorkspaceContextService, + @ITextModelService textModelResolverService: ITextModelService, + @IEnvironmentService environmentService: IEnvironmentService, + @IWindowService windowService: IWindowService, + @ILogService private readonly logService: ILogService, + @ILifecycleService private readonly lifecycleService: ILifecycleService, + @IContextKeyService private readonly contextKeyService: IContextKeyService, + ) { + super(); + this.activeChannelIdInStorage = this.storageService.get(OUTPUT_ACTIVE_CHANNEL_KEY, StorageScope.WORKSPACE, ''); + + // Register as text model content provider for output + textModelResolverService.registerTextModelContentProvider(OUTPUT_SCHEME, this); + instantiationService.createInstance(OutputLinkProvider); + + // Create output channels for already registered channels + const registry = Registry.as(Extensions.OutputChannels); + for (const channelIdentifier of registry.getChannels()) { + this.onDidRegisterChannel(channelIdentifier.id); + } + this._register(registry.onDidRegisterChannel(this.onDidRegisterChannel, this)); + + this._register(panelService.onDidPanelOpen(({ panel, focus }) => this.onDidPanelOpen(panel, !focus), this)); + this._register(panelService.onDidPanelClose(this.onDidPanelClose, this)); + + // Set active channel to first channel if not set + if (!this.activeChannel) { + const channels = this.getChannelDescriptors(); + this.activeChannel = channels && channels.length > 0 ? this.getChannel(channels[0].id) : null; + } + + this._register(this.lifecycleService.onShutdown(() => this.dispose())); + this._register(this.storageService.onWillSaveState(() => this.saveState())); + } + + provideTextContent(resource: URI): Promise | null { + const channel = this.getChannel(resource.path); + if (channel) { + return channel.model.loadModel(); + } + return null; + } + + showChannel(id: string, preserveFocus?: boolean): Promise { + const channel = this.getChannel(id); + if (!channel || this.isChannelShown(channel)) { + if (this._outputPanel && !preserveFocus) { + this._outputPanel.focus(); + } + return Promise.resolve(undefined); + } + + this.activeChannel = channel; + let promise: Promise; + if (this.isPanelShown()) { + promise = this.doShowChannel(channel, !!preserveFocus); + } else { + this.panelService.openPanel(OUTPUT_PANEL_ID); + promise = this.doShowChannel(this.activeChannel, !!preserveFocus); + } + return promise.then(() => this._onActiveOutputChannel.fire(id)); + } + + getChannel(id: string): OutputChannel | null { + return this.channels.get(id) || null; + } + + getChannelDescriptors(): IOutputChannelDescriptor[] { + return Registry.as(Extensions.OutputChannels).getChannels(); + } + + getActiveChannel(): IOutputChannel | null { + return this.activeChannel; + } + + private onDidRegisterChannel(channelId: string): void { + const channel = this.createChannel(channelId); + this.channels.set(channelId, channel); + if (this.activeChannelIdInStorage === channelId) { + this.activeChannel = channel; + this.onDidPanelOpen(this.panelService.getActivePanel(), true) + .then(() => this._onActiveOutputChannel.fire(channelId)); + } + } + + private onDidPanelOpen(panel: IPanel | null, preserveFocus: boolean): Promise { + if (panel && panel.getId() === OUTPUT_PANEL_ID) { + this._outputPanel = this.panelService.getActivePanel(); + if (this.activeChannel) { + return this.doShowChannel(this.activeChannel, preserveFocus); + } + } + return Promise.resolve(undefined); + } + + private onDidPanelClose(panel: IPanel): void { + if (this._outputPanel && panel.getId() === OUTPUT_PANEL_ID) { + CONTEXT_ACTIVE_LOG_OUTPUT.bindTo(this.contextKeyService).set(false); + this._outputPanel.clearInput(); + } + } + + private createChannel(id: string): OutputChannel { + const channelDisposables: IDisposable[] = []; + const channel = this.instantiateChannel(id); + channel.model.onDidAppendedContent(() => { + if (!channel.scrollLock) { + const panel = this.panelService.getActivePanel(); + if (panel && panel.getId() === OUTPUT_PANEL_ID && this.isChannelShown(channel)) { + let outputPanel = panel; + outputPanel.revealLastLine(); + } + } + }, channelDisposables); + channel.model.onDispose(() => { + if (this.activeChannel === channel) { + const channels = this.getChannelDescriptors(); + const channel = channels.length ? this.getChannel(channels[0].id) : null; + if (channel && this.isPanelShown()) { + this.showChannel(channel.id, true); + } else { + this.activeChannel = channel; + if (this.activeChannel) { + this._onActiveOutputChannel.fire(this.activeChannel.id); + } + } + } + Registry.as(Extensions.OutputChannels).removeChannel(id); + dispose(channelDisposables); + }, channelDisposables); + + return channel; + } + + private instantiateChannel(id: string): OutputChannel { + const channelData = Registry.as(Extensions.OutputChannels).getChannel(id); + if (!channelData) { + this.logService.error(`Channel '${id}' is not registered yet`); + throw new Error(`Channel '${id}' is not registered yet`); + } + return this.instantiationService.createInstance(OutputChannel, channelData); + } + + private doShowChannel(channel: OutputChannel, preserveFocus: boolean): Promise { + if (this._outputPanel) { + CONTEXT_ACTIVE_LOG_OUTPUT.bindTo(this.contextKeyService).set(!!channel.outputChannelDescriptor.file && channel.outputChannelDescriptor.log); + return this._outputPanel.setInput(this.createInput(channel), EditorOptions.create({ preserveFocus }), CancellationToken.None) + .then(() => { + if (!preserveFocus) { + this._outputPanel.focus(); + } + }); + } + return Promise.resolve(undefined); + } + + private isChannelShown(channel: IOutputChannel): boolean { + return this.isPanelShown() && this.activeChannel === channel; + } + + private isPanelShown(): boolean { + const panel = this.panelService.getActivePanel(); + return !!panel && panel.getId() === OUTPUT_PANEL_ID; + } + + private createInput(channel: IOutputChannel): ResourceEditorInput { + const resource = URI.from({ scheme: OUTPUT_SCHEME, path: channel.id }); + return this.instantiationService.createInstance(ResourceEditorInput, nls.localize('output', "{0} - Output", channel.label), nls.localize('channel', "Output channel for '{0}'", channel.label), resource); + } + + private saveState(): void { + if (this.activeChannel) { + this.storageService.store(OUTPUT_ACTIVE_CHANNEL_KEY, this.activeChannel.id, StorageScope.WORKSPACE); + } + } +} + +export class LogContentProvider { + + private channelModels: Map = new Map(); + + constructor( + @IOutputService private readonly outputService: IOutputService, + @IOutputChannelModelService private readonly outputChannelModelService: IOutputChannelModelService + ) { + } + + provideTextContent(resource: URI): Promise | null { + if (resource.scheme === LOG_SCHEME) { + let channelModel = this.getChannelModel(resource); + if (channelModel) { + return channelModel.loadModel(); + } + } + return null; + } + + private getChannelModel(resource: URI): IOutputChannelModel | undefined { + const channelId = resource.path; + let channelModel = this.channelModels.get(channelId); + if (!channelModel) { + const channelDisposables: IDisposable[] = []; + const outputChannelDescriptor = this.outputService.getChannelDescriptors().filter(({ id }) => id === channelId)[0]; + if (outputChannelDescriptor && outputChannelDescriptor.file) { + channelModel = this.outputChannelModelService.createOutputChannelModel(channelId, resource, outputChannelDescriptor.log ? LOG_MIME : OUTPUT_MIME, outputChannelDescriptor.file); + channelModel.onDispose(() => dispose(channelDisposables), channelDisposables); + this.channelModels.set(channelId, channelModel); + } + } + return channelModel; + } +} \ No newline at end of file diff --git a/src/vs/workbench/contrib/output/electron-browser/outputServices.ts b/src/vs/workbench/contrib/output/electron-browser/outputServices.ts deleted file mode 100644 index 897620d45f8..00000000000 --- a/src/vs/workbench/contrib/output/electron-browser/outputServices.ts +++ /dev/null @@ -1,760 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as nls from 'vs/nls'; -import { join, dirname } from 'vs/base/common/path'; -import * as strings from 'vs/base/common/strings'; -import * as extfs from 'vs/base/node/extfs'; -import { Event, Emitter } from 'vs/base/common/event'; -import { URI } from 'vs/base/common/uri'; -import { IDisposable, dispose, Disposable, toDisposable } from 'vs/base/common/lifecycle'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -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, 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'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { OutputLinkProvider } from 'vs/workbench/contrib/output/common/outputLinkProvider'; -import { ITextModelService, ITextModelContentProvider } from 'vs/editor/common/services/resolverService'; -import { ITextModel } from 'vs/editor/common/model'; -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 { IPanel } from 'vs/workbench/common/panel'; -import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { toLocalISOString } from 'vs/base/common/date'; -import { IWindowService } from 'vs/platform/windows/common/windows'; -import { ILogService } from 'vs/platform/log/common/log'; -import { binarySearch } from 'vs/base/common/arrays'; -import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; -import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { CancellationToken } from 'vs/base/common/cancellation'; -import { OutputAppender } from 'vs/workbench/contrib/output/node/outputAppender'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { isNumber } from 'vs/base/common/types'; - -const OUTPUT_ACTIVE_CHANNEL_KEY = 'output.activechannel'; - -let watchingOutputDir = false; -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) { - const watcherDisposable = extfs.watch(outputDir, (eventType, fileName) => { - for (const callback of callbacks) { - callback(eventType, fileName); - } - }, (error: string) => { - logService.error(error); - }); - watchingOutputDir = true; - return toDisposable(() => { - callbacks = []; - watcherDisposable.dispose(); - }); - } - return toDisposable(() => { }); -} - -interface OutputChannel extends IOutputChannel { - readonly file: URI | null; - readonly onDidAppendedContent: Event; - readonly onDispose: Event; - loadModel(): Promise; -} - -abstract class AbstractFileOutputChannel extends Disposable implements OutputChannel { - - scrollLock: boolean = false; - - protected _onDidAppendedContent = new Emitter(); - readonly onDidAppendedContent: Event = this._onDidAppendedContent.event; - - protected _onDispose = new Emitter(); - readonly onDispose: Event = this._onDispose.event; - - private readonly mimeType: string; - protected modelUpdater: RunOnceScheduler; - protected model: ITextModel | null; - readonly file: URI; - - protected startOffset: number = 0; - protected endOffset: number = 0; - - constructor( - readonly outputChannelDescriptor: IFileOutputChannelDescriptor, - private readonly modelUri: URI, - protected fileService: IFileService, - protected modelService: IModelService, - protected modeService: IModeService, - ) { - super(); - this.mimeType = outputChannelDescriptor.log ? LOG_MIME : OUTPUT_MIME; - this.file = this.outputChannelDescriptor.file; - this.modelUpdater = new RunOnceScheduler(() => this.updateModel(), 300); - this._register(toDisposable(() => this.modelUpdater.cancel())); - } - - get id(): string { - return this.outputChannelDescriptor.id; - } - - get label(): string { - return this.outputChannelDescriptor.label; - } - - clear(till?: number): void { - if (this.modelUpdater.isScheduled()) { - this.modelUpdater.cancel(); - this.onUpdateModelCancelled(); - } - if (this.model) { - this.model.setValue(''); - } - this.endOffset = isNumber(till) ? till : this.endOffset; - this.startOffset = this.endOffset; - } - - update(): void { } - - protected createModel(content: string): ITextModel { - if (this.model) { - this.model.setValue(content); - } else { - this.model = this.modelService.createModel(content, this.modeService.create(this.mimeType), this.modelUri); - this.onModelCreated(this.model); - const disposables: IDisposable[] = []; - disposables.push(this.model.onWillDispose(() => { - this.onModelWillDispose(this.model); - this.model = null; - dispose(disposables); - })); - } - return this.model; - } - - appendToModel(content: string): void { - if (this.model && content) { - const lastLine = this.model.getLineCount(); - const lastLineMaxColumn = this.model.getLineMaxColumn(lastLine); - this.model.applyEdits([EditOperation.insert(new Position(lastLine, lastLineMaxColumn), content)]); - this._onDidAppendedContent.fire(); - } - } - - abstract loadModel(): Promise; - abstract append(message: string); - - protected onModelCreated(model: ITextModel) { } - protected onModelWillDispose(model: ITextModel | null) { } - protected onUpdateModelCancelled() { } - protected updateModel() { } - - dispose(): void { - this._onDispose.fire(); - super.dispose(); - } -} - -/** - * An output channel that stores appended messages in a backup file. - */ -class OutputChannelBackedByFile extends AbstractFileOutputChannel implements OutputChannel { - - private appender: OutputAppender; - private appendedMessage = ''; - private loadingFromFileInProgress: boolean = false; - private resettingDelayer: ThrottledDelayer; - private readonly rotatingFilePath: string; - - constructor( - outputChannelDescriptor: IFileOutputChannelDescriptor, - modelUri: URI, - @IFileService fileService: IFileService, - @IModelService modelService: IModelService, - @IModeService modeService: IModeService, - @ILogService logService: ILogService - ) { - 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); - this.rotatingFilePath = `${outputChannelDescriptor.id}.1.log`; - this._register(watchOutputDirectory(dirname(this.file.fsPath), logService, (eventType, file) => this.onFileChangedInOutputDirector(eventType, file))); - - this.resettingDelayer = new ThrottledDelayer(50); - } - - append(message: string): void { - // update end offset always as message is read - this.endOffset = this.endOffset + Buffer.from(message).byteLength; - if (this.loadingFromFileInProgress) { - this.appendedMessage += message; - } else { - this.write(message); - if (this.model) { - this.appendedMessage += message; - if (!this.modelUpdater.isScheduled()) { - this.modelUpdater.schedule(); - } - } - } - } - - clear(till?: number): void { - super.clear(till); - this.appendedMessage = ''; - } - - loadModel(): Promise { - this.loadingFromFileInProgress = true; - if (this.modelUpdater.isScheduled()) { - this.modelUpdater.cancel(); - } - this.appendedMessage = ''; - return this.loadFile() - .then(content => { - if (this.endOffset !== this.startOffset + Buffer.from(content).byteLength) { - // Queue content is not written into the file - // Flush it and load file again - this.flush(); - return this.loadFile(); - } - return content; - }) - .then(content => { - if (this.appendedMessage) { - this.write(this.appendedMessage); - this.appendedMessage = ''; - } - this.loadingFromFileInProgress = false; - return this.createModel(content); - }); - } - - private resetModel(): Promise { - this.startOffset = 0; - this.endOffset = 0; - if (this.model) { - return this.loadModel().then(() => undefined); - } - return Promise.resolve(undefined); - } - - private loadFile(): Promise { - return this.fileService.resolveContent(this.file, { position: this.startOffset, encoding: 'utf8' }) - .then(content => this.appendedMessage ? content.value + this.appendedMessage : content.value); - } - - protected updateModel(): void { - if (this.model && this.appendedMessage) { - this.appendToModel(this.appendedMessage); - this.appendedMessage = ''; - } - } - - 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()); - } - } - - private write(content: string): void { - this.appender.append(content); - } - - private flush(): void { - this.appender.flush(); - } -} - -/** - * An output channel driven by a file and does not support appending messages. - */ -class FileOutputChannel extends AbstractFileOutputChannel implements OutputChannel { - - private readonly fileHandler: FileListener; - - private updateInProgress: boolean = false; - private etag: string | undefined = ''; - private loadModelPromise: Promise | null = null; - - constructor( - outputChannelDescriptor: IFileOutputChannelDescriptor, - modelUri: URI, - @IFileService fileService: IFileService, - @IModelService modelService: IModelService, - @IModeService modeService: IModeService - ) { - 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._register(toDisposable(() => this.fileHandler.unwatch())); - } - - loadModel(): Promise { - this.loadModelPromise = this.fileService.resolveContent(this.file, { position: this.startOffset, encoding: 'utf8' }) - .then(content => { - this.endOffset = this.startOffset + Buffer.from(content.value).byteLength; - this.etag = content.etag; - return this.createModel(content.value); - }); - return this.loadModelPromise; - } - - clear(till?: number): void { - const loadModelPromise: Promise = this.loadModelPromise ? this.loadModelPromise : Promise.resolve(); - loadModelPromise.then(() => { - super.clear(till); - this.update(); - }); - } - - append(message: string): void { - throw new Error('Not supported'); - } - - protected updateModel(): void { - if (this.model) { - this.fileService.resolveContent(this.file, { position: this.endOffset, encoding: 'utf8' }) - .then(content => { - this.etag = content.etag; - if (content.value) { - this.endOffset = this.endOffset + Buffer.from(content.value).byteLength; - this.appendToModel(content.value); - } - this.updateInProgress = false; - }, () => this.updateInProgress = false); - } else { - this.updateInProgress = false; - } - } - - protected onModelCreated(model: ITextModel): void { - this.fileHandler.watch(this.etag); - } - - protected onModelWillDispose(model: ITextModel | null): void { - this.fileHandler.unwatch(); - } - - protected onUpdateModelCancelled(): void { - this.updateInProgress = false; - } - - update(size?: number): void { - 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(); - } - } - } -} - -export class OutputService extends Disposable implements IOutputService, ITextModelContentProvider { - - public _serviceBrand: any; - - private channels: Map = new Map(); - private activeChannelIdInStorage: string; - private activeChannel: IOutputChannel | null; - private readonly outputDir: string; - - private readonly _onActiveOutputChannel = new Emitter(); - readonly onActiveOutputChannel: Event = this._onActiveOutputChannel.event; - - private _outputPanel: OutputPanel; - - constructor( - @IStorageService private readonly storageService: IStorageService, - @IInstantiationService private readonly instantiationService: IInstantiationService, - @IPanelService private readonly panelService: IPanelService, - @IWorkspaceContextService contextService: IWorkspaceContextService, - @ITextModelService textModelResolverService: ITextModelService, - @IEnvironmentService environmentService: IEnvironmentService, - @IWindowService windowService: IWindowService, - @ILogService private readonly logService: ILogService, - @ITelemetryService private readonly telemetryService: ITelemetryService, - @ILifecycleService private readonly lifecycleService: ILifecycleService, - @IContextKeyService private readonly contextKeyService: IContextKeyService, - ) { - super(); - 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 - textModelResolverService.registerTextModelContentProvider(OUTPUT_SCHEME, this); - instantiationService.createInstance(OutputLinkProvider); - - // Create output channels for already registered channels - const registry = Registry.as(Extensions.OutputChannels); - for (const channelIdentifier of registry.getChannels()) { - this.onDidRegisterChannel(channelIdentifier.id); - } - this._register(registry.onDidRegisterChannel(this.onDidRegisterChannel, this)); - - this._register(panelService.onDidPanelOpen(({ panel, focus }) => this.onDidPanelOpen(panel, !focus), this)); - this._register(panelService.onDidPanelClose(this.onDidPanelClose, this)); - - // Set active channel to first channel if not set - if (!this.activeChannel) { - const channels = this.getChannelDescriptors(); - this.activeChannel = channels && channels.length > 0 ? this.getChannel(channels[0].id) : null; - } - - this._register(this.lifecycleService.onShutdown(() => this.dispose())); - this._register(this.storageService.onWillSaveState(() => this.saveState())); - } - - provideTextContent(resource: URI): Promise | null { - const channel = this.getChannel(resource.path); - if (channel) { - return channel.loadModel(); - } - return null; - } - - showChannel(id: string, preserveFocus?: boolean): Promise { - const channel = this.getChannel(id); - if (!channel || this.isChannelShown(channel)) { - if (this._outputPanel && !preserveFocus) { - this._outputPanel.focus(); - } - return Promise.resolve(undefined); - } - - this.activeChannel = channel; - let promise: Promise; - if (this.isPanelShown()) { - promise = this.doShowChannel(channel, !!preserveFocus); - } else { - this.panelService.openPanel(OUTPUT_PANEL_ID); - promise = this.doShowChannel(this.activeChannel, !!preserveFocus); - } - return promise.then(() => this._onActiveOutputChannel.fire(id)); - } - - getChannel(id: string): IOutputChannel | null { - return this.channels.get(id) || null; - } - - getChannelDescriptors(): IOutputChannelDescriptor[] { - return Registry.as(Extensions.OutputChannels).getChannels(); - } - - getActiveChannel(): IOutputChannel | null { - return this.activeChannel; - } - - private onDidRegisterChannel(channelId: string): void { - const channel = this.createChannel(channelId); - this.channels.set(channelId, channel); - if (this.activeChannelIdInStorage === channelId) { - this.activeChannel = channel; - this.onDidPanelOpen(this.panelService.getActivePanel(), true) - .then(() => this._onActiveOutputChannel.fire(channelId)); - } - } - - private onDidPanelOpen(panel: IPanel | null, preserveFocus: boolean): Promise { - if (panel && panel.getId() === OUTPUT_PANEL_ID) { - this._outputPanel = this.panelService.getActivePanel(); - if (this.activeChannel) { - return this.doShowChannel(this.activeChannel, preserveFocus); - } - } - return Promise.resolve(undefined); - } - - private onDidPanelClose(panel: IPanel): void { - if (this._outputPanel && panel.getId() === OUTPUT_PANEL_ID) { - CONTEXT_ACTIVE_LOG_OUTPUT.bindTo(this.contextKeyService).set(false); - this._outputPanel.clearInput(); - } - } - - private createChannel(id: string): OutputChannel { - const channelDisposables: IDisposable[] = []; - const channel = this.instantiateChannel(id); - channel.onDidAppendedContent(() => { - if (!channel.scrollLock) { - const panel = this.panelService.getActivePanel(); - if (panel && panel.getId() === OUTPUT_PANEL_ID && this.isChannelShown(channel)) { - let outputPanel = panel; - outputPanel.revealLastLine(); - } - } - }, channelDisposables); - channel.onDispose(() => { - if (this.activeChannel === channel) { - const channels = this.getChannelDescriptors(); - const channel = channels.length ? this.getChannel(channels[0].id) : null; - if (channel && this.isPanelShown()) { - this.showChannel(channel.id, true); - } else { - this.activeChannel = channel; - if (this.activeChannel) { - this._onActiveOutputChannel.fire(this.activeChannel.id); - } - } - } - Registry.as(Extensions.OutputChannels).removeChannel(id); - dispose(channelDisposables); - }, channelDisposables); - - return channel; - } - - private instantiateChannel(id: string): OutputChannel { - const channelData = Registry.as(Extensions.OutputChannels).getChannel(id); - if (!channelData) { - this.logService.error(`Channel '${id}' is not registered yet`); - throw new Error(`Channel '${id}' is not registered yet`); - } - - const uri = URI.from({ scheme: OUTPUT_SCHEME, path: id }); - if (channelData && channelData.file) { - return this.instantiationService.createInstance(FileOutputChannel, channelData, uri); - } - try { - 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); - /* __GDPR__ - "output.channel.creation.error" : {} - */ - this.telemetryService.publicLog('output.channel.creation.error'); - return this.instantiationService.createInstance(BufferredOutputChannel, { id, label: channelData ? channelData.label : '' }); - } - } - - private doShowChannel(channel: IOutputChannel, preserveFocus: boolean): Promise { - if (this._outputPanel) { - CONTEXT_ACTIVE_LOG_OUTPUT.bindTo(this.contextKeyService).set(channel instanceof FileOutputChannel && channel.outputChannelDescriptor.log); - return this._outputPanel.setInput(this.createInput(channel), EditorOptions.create({ preserveFocus }), CancellationToken.None) - .then(() => { - if (!preserveFocus) { - this._outputPanel.focus(); - } - }); - } - return Promise.resolve(undefined); - } - - private isChannelShown(channel: IOutputChannel): boolean { - return this.isPanelShown() && this.activeChannel === channel; - } - - private isPanelShown(): boolean { - const panel = this.panelService.getActivePanel(); - return !!panel && panel.getId() === OUTPUT_PANEL_ID; - } - - private createInput(channel: IOutputChannel): ResourceEditorInput { - const resource = URI.from({ scheme: OUTPUT_SCHEME, path: channel.id }); - return this.instantiationService.createInstance(ResourceEditorInput, nls.localize('output', "{0} - Output", channel.label), nls.localize('channel', "Output channel for '{0}'", channel.label), resource); - } - - private saveState(): void { - if (this.activeChannel) { - this.storageService.store(OUTPUT_ACTIVE_CHANNEL_KEY, this.activeChannel.id, StorageScope.WORKSPACE); - } - } -} - -export class LogContentProvider { - - private channels: Map = new Map(); - - constructor( - @IOutputService private readonly outputService: IOutputService, - @IInstantiationService private readonly instantiationService: IInstantiationService - ) { - } - - provideTextContent(resource: URI): Promise | null { - if (resource.scheme === LOG_SCHEME) { - let channel = this.getChannel(resource); - if (channel) { - return channel.loadModel(); - } - } - return null; - } - - private getChannel(resource: URI): OutputChannel | undefined { - const channelId = resource.path; - let channel = this.channels.get(channelId); - if (!channel) { - const channelDisposables: IDisposable[] = []; - const outputChannelDescriptor = this.outputService.getChannelDescriptors().filter(({ id }) => id === channelId)[0]; - if (outputChannelDescriptor && outputChannelDescriptor.file) { - channel = this.instantiationService.createInstance(FileOutputChannel, outputChannelDescriptor, resource); - channel.onDispose(() => dispose(channelDisposables), channelDisposables); - this.channels.set(channelId, channel); - } - } - return channel; - } -} -// Remove this channel when https://github.com/Microsoft/vscode/issues/47883 is fixed -class BufferredOutputChannel extends Disposable implements OutputChannel { - - readonly id: string; - readonly label: string; - readonly file: URI | null = null; - scrollLock: boolean = false; - - protected _onDidAppendedContent = new Emitter(); - readonly onDidAppendedContent: Event = this._onDidAppendedContent.event; - - private readonly _onDispose = new Emitter(); - readonly onDispose: Event = this._onDispose.event; - - private modelUpdater: RunOnceScheduler; - private model: ITextModel | null; - private readonly bufferredContent: BufferedContent; - private lastReadId: number | undefined = undefined; - - constructor( - protected readonly outputChannelIdentifier: IOutputChannelDescriptor, - @IModelService private readonly modelService: IModelService, - @IModeService private readonly modeService: IModeService - ) { - super(); - - this.id = outputChannelIdentifier.id; - this.label = outputChannelIdentifier.label; - - this.modelUpdater = new RunOnceScheduler(() => this.updateModel(), 300); - this._register(toDisposable(() => this.modelUpdater.cancel())); - - this.bufferredContent = new BufferedContent(); - this._register(toDisposable(() => this.bufferredContent.clear())); - } - - append(output: string) { - this.bufferredContent.append(output); - if (!this.modelUpdater.isScheduled()) { - this.modelUpdater.schedule(); - } - } - - update(): void { } - - clear(): void { - if (this.modelUpdater.isScheduled()) { - this.modelUpdater.cancel(); - } - if (this.model) { - this.model.setValue(''); - } - this.bufferredContent.clear(); - this.lastReadId = undefined; - } - - loadModel(): Promise { - const { value, id } = this.bufferredContent.getDelta(this.lastReadId); - if (this.model) { - this.model.setValue(value); - } else { - this.model = this.createModel(value); - } - this.lastReadId = id; - return Promise.resolve(this.model); - } - - private createModel(content: string): ITextModel { - const model = this.modelService.createModel(content, this.modeService.create(OUTPUT_MIME), URI.from({ scheme: OUTPUT_SCHEME, path: this.id })); - const disposables: IDisposable[] = []; - disposables.push(model.onWillDispose(() => { - this.model = null; - dispose(disposables); - })); - return model; - } - - private updateModel(): void { - if (this.model) { - const { value, id } = this.bufferredContent.getDelta(this.lastReadId); - this.lastReadId = id; - const lastLine = this.model.getLineCount(); - const lastLineMaxColumn = this.model.getLineMaxColumn(lastLine); - this.model.applyEdits([EditOperation.insert(new Position(lastLine, lastLineMaxColumn), value)]); - this._onDidAppendedContent.fire(); - } - } - - dispose(): void { - this._onDispose.fire(); - super.dispose(); - } -} - -class BufferedContent { - - private data: string[] = []; - private dataIds: number[] = []; - private idPool = 0; - private length = 0; - - public append(content: string): void { - this.data.push(content); - this.dataIds.push(++this.idPool); - this.length += content.length; - this.trim(); - } - - public clear(): void { - this.data.length = 0; - this.dataIds.length = 0; - this.length = 0; - } - - private trim(): void { - if (this.length < MAX_OUTPUT_LENGTH * 1.2) { - return; - } - - while (this.length > MAX_OUTPUT_LENGTH) { - this.dataIds.shift(); - const removed = this.data.shift(); - if (removed) { - this.length -= removed.length; - } - } - } - - public getDelta(previousId?: number): { value: string, id: number } { - let idx = -1; - if (previousId !== undefined) { - idx = binarySearch(this.dataIds, previousId, (a, b) => a - b); - } - - const id = this.idPool; - if (idx >= 0) { - const value = strings.removeAnsiEscapeCodes(this.data.slice(idx + 1).join('')); - return { value, id }; - } else { - const value = strings.removeAnsiEscapeCodes(this.data.join('')); - return { value, id }; - } - } -} diff --git a/src/vs/workbench/contrib/performance/electron-browser/startupProfiler.ts b/src/vs/workbench/contrib/performance/electron-browser/startupProfiler.ts index dfd9214684a..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 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/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index 8f1fabdf359..8211f695bbc 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -112,7 +112,7 @@ function _resolveSettingsTree(tocData: ITOCEntry, allSettings: Set): I } if (!children && !settings) { - return null; + throw new Error(`TOC node has no child groups or settings: ${tocData.id}`); } return { @@ -439,7 +439,7 @@ export abstract class AbstractSettingRenderer implements ITreeRenderer this._onDidChangeSetting.fire({ key: element.setting.key, value, type: template.context.valueType }); + const onChange = value => this._onDidChangeSetting.fire({ key: element.setting.key, value, type: template.context!.valueType }); template.deprecationWarningElement.innerText = element.setting.deprecationMessage || ''; this.renderValue(element, template, onChange); @@ -488,7 +488,7 @@ export abstract class AbstractSettingRenderer implements ITreeRenderertemplate.controlElement.firstElementChild) { itemElement.setAttribute('role', 'combobox'); label += modifiedText; } @@ -587,7 +587,7 @@ export class SettingNewExtensionsRenderer implements ITreeRenderer { if (template.context) { @@ -621,9 +621,9 @@ export class SettingComplexRenderer extends AbstractSettingRenderer implements I renderTemplate(container: HTMLElement): ISettingComplexItemTemplate { const common = this.renderCommonTemplate(null, container, 'complex'); - const openSettingsButton = new Button(common.controlElement, { title: true, buttonBackground: null, buttonHoverBackground: null }); + const openSettingsButton = new Button(common.controlElement, { title: true, buttonBackground: undefined, buttonHoverBackground: undefined }); common.toDispose.push(openSettingsButton); - common.toDispose.push(openSettingsButton.onDidClick(() => template.onChange(null))); + common.toDispose.push(openSettingsButton.onDidClick(() => template.onChange!())); openSettingsButton.label = localize('editInSettingsJson', "Edit in settings.json"); openSettingsButton.element.classList.add('edit-in-settings-button'); @@ -770,7 +770,7 @@ export class SettingTextRenderer extends AbstractSettingRenderer implements ITre protected renderValue(dataElement: SettingsTreeSettingElement, template: ISettingTextItemTemplate, onChange: (value: string) => void): void { const label = this.setElementAriaLabels(dataElement, SETTINGS_TEXT_TEMPLATE_ID, template); - template.onChange = null; + template.onChange = undefined; template.inputBox.value = dataElement.value; template.onChange = value => { renderValidations(dataElement, template, false, label); onChange(value); }; @@ -784,7 +784,7 @@ export class SettingEnumRenderer extends AbstractSettingRenderer implements ITre renderTemplate(container: HTMLElement): ISettingEnumItemTemplate { const common = this.renderCommonTemplate(null, container, 'enum'); - const selectBox = new SelectBox([], undefined, this._contextViewService, undefined, { useCustomDrawn: true }); + const selectBox = new SelectBox([], 0, this._contextViewService, undefined, { useCustomDrawn: true }); common.toDispose.push(selectBox); common.toDispose.push(attachSelectBoxStyler(selectBox, this._themeService, { @@ -827,7 +827,7 @@ export class SettingEnumRenderer extends AbstractSettingRenderer implements ITre const enumDescriptions = dataElement.setting.enumDescriptions; const enumDescriptionsAreMarkdown = dataElement.setting.enumDescriptionsAreMarkdown; - const displayOptions = dataElement.setting.enum + const displayOptions = dataElement.setting.enum! .map(String) .map(escapeInvisibleChars) .map((data, index) => { @@ -842,10 +842,10 @@ export class SettingEnumRenderer extends AbstractSettingRenderer implements ITre const label = this.setElementAriaLabels(dataElement, SETTINGS_ENUM_TEMPLATE_ID, template); template.selectBox.setAriaLabel(label); - const idx = dataElement.setting.enum.indexOf(dataElement.value); - template.onChange = null; + const idx = dataElement.setting.enum!.indexOf(dataElement.value); + template.onChange = undefined; template.selectBox.select(idx); - template.onChange = idx => onChange(dataElement.setting.enum[idx]); + template.onChange = idx => onChange(dataElement.setting.enum![idx]); template.enumDescriptionElement.innerHTML = ''; } @@ -889,7 +889,7 @@ export class SettingNumberRenderer extends AbstractSettingRenderer implements IT super.renderSettingElement(element, index, templateData); } - protected renderValue(dataElement: SettingsTreeSettingElement, template: ISettingNumberItemTemplate, onChange: (value: number) => void): void { + protected renderValue(dataElement: SettingsTreeSettingElement, template: ISettingNumberItemTemplate, onChange: (value: number | null) => void): void { const numParseFn = (dataElement.valueType === 'integer' || dataElement.valueType === 'nullable-integer') ? parseInt : parseFloat; @@ -898,9 +898,12 @@ export class SettingNumberRenderer extends AbstractSettingRenderer implements IT const label = this.setElementAriaLabels(dataElement, SETTINGS_NUMBER_TEMPLATE_ID, template); - template.onChange = null; + template.onChange = undefined; template.inputBox.value = dataElement.value; - template.onChange = value => { renderValidations(dataElement, template, false, label); onChange(nullNumParseFn(value)); }; + template.onChange = value => { + renderValidations(dataElement, template, false, label); + onChange(nullNumParseFn(value)); + }; renderValidations(dataElement, template, true, label); } @@ -930,7 +933,7 @@ export class SettingBoolRenderer extends AbstractSettingRenderer implements ITre const deprecationWarningElement = DOM.append(container, $('.setting-item-deprecation-message')); const toDispose: IDisposable[] = []; - const checkbox = new Checkbox({ actionClassName: 'setting-value-checkbox', isChecked: true, title: '', inputActiveOptionBorder: null }); + const checkbox = new Checkbox({ actionClassName: 'setting-value-checkbox', isChecked: true, title: '', inputActiveOptionBorder: undefined }); controlElement.appendChild(checkbox.domNode); toDispose.push(checkbox); toDispose.push(checkbox.onChange(() => { @@ -949,7 +952,7 @@ export class SettingBoolRenderer extends AbstractSettingRenderer implements ITre // Toggle target checkbox if (targetElement.tagName.toLowerCase() !== 'a' && targetId === template.checkbox.domNode.id) { template.checkbox.checked = template.checkbox.checked ? false : true; - template.onChange(checkbox.checked); + template.onChange!(checkbox.checked); } DOM.EventHelper.stop(e); })); @@ -993,7 +996,7 @@ export class SettingBoolRenderer extends AbstractSettingRenderer implements ITre } protected renderValue(dataElement: SettingsTreeSettingElement, template: ISettingBoolItemTemplate, onChange: (value: boolean) => void): void { - template.onChange = null; + template.onChange = undefined; template.checkbox.checked = dataElement.value; template.onChange = onChange; @@ -1067,17 +1070,17 @@ export class SettingTreeRenderers { } showContextMenu(element: SettingsTreeSettingElement, settingDOMElement: HTMLElement): void { - const toolbarElement: HTMLElement = settingDOMElement.querySelector('.toolbar-toggle-more'); + const toolbarElement = settingDOMElement.querySelector('.toolbar-toggle-more'); if (toolbarElement) { this._contextMenuService.showContextMenu({ getActions: () => this.settingActions, - getAnchor: () => toolbarElement, + getAnchor: () => toolbarElement, getActionsContext: () => element }); } } - getSettingDOMElementForDOMElement(domElement: HTMLElement): HTMLElement { + getSettingDOMElementForDOMElement(domElement: HTMLElement): HTMLElement | null { const parent = DOM.findParentWithClass(domElement, AbstractSettingRenderer.CONTENTS_CLASS); if (parent) { return parent; @@ -1090,12 +1093,12 @@ export class SettingTreeRenderers { return treeContainer.querySelectorAll(`[${AbstractSettingRenderer.SETTING_KEY_ATTR}="${key}"]`); } - getKeyForDOMElementInSetting(element: HTMLElement): string { + getKeyForDOMElementInSetting(element: HTMLElement): string | null { const settingElement = this.getSettingDOMElementForDOMElement(element); return settingElement && settingElement.getAttribute(AbstractSettingRenderer.SETTING_KEY_ATTR); } - getIdForDOMElementInSetting(element: HTMLElement): string { + getIdForDOMElementInSetting(element: HTMLElement): string | null { const settingElement = this.getSettingDOMElementForDOMElement(element); return settingElement && settingElement.getAttribute(AbstractSettingRenderer.SETTING_ID_ATTR); } @@ -1108,11 +1111,11 @@ function renderValidations(dataElement: SettingsTreeSettingElement, template: IS DOM.addClass(template.containerElement, 'invalid-input'); template.validationErrorMessageElement.innerText = errMsg; const validationError = localize('validationError', "Validation Error."); - template.inputBox.inputElement.parentElement.setAttribute('aria-label', [originalAriaLabel, validationError, errMsg].join(' ')); + template.inputBox.inputElement.parentElement!.setAttribute('aria-label', [originalAriaLabel, validationError, errMsg].join(' ')); if (!calledOnStartup) { ariaAlert(validationError + ' ' + errMsg); } return; } else { - template.inputBox.inputElement.parentElement.setAttribute('aria-label', originalAriaLabel); + template.inputBox.inputElement.parentElement!.setAttribute('aria-label', originalAriaLabel); } } DOM.removeClass(template.containerElement, 'invalid-input'); diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts index 879d9a9e49f..4e7f56b60d8 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts @@ -24,7 +24,7 @@ export interface ISettingsEditorViewState { export abstract class SettingsTreeElement { id: string; - parent: SettingsTreeGroupElement; + parent?: SettingsTreeGroupElement; /** * Index assigned in display order, used for paging. @@ -130,7 +130,7 @@ export class SettingsTreeSettingElement extends SettingsTreeElement { } private initLabel(): void { - const displayKeyFormat = settingKeyToDisplayFormat(this.setting.key, this.parent.id); + const displayKeyFormat = settingKeyToDisplayFormat(this.setting.key, this.parent!.id); this._displayLabel = displayKeyFormat.label; this._displayCategory = displayKeyFormat.category; } @@ -161,7 +161,7 @@ export class SettingsTreeSettingElement extends SettingsTreeElement { } if (this.setting.tags) { - this.setting.tags.forEach(tag => this.tags.add(tag)); + this.setting.tags.forEach(tag => this.tags!.add(tag)); } } @@ -207,7 +207,7 @@ export class SettingsTreeSettingElement extends SettingsTreeElement { if (this.tags) { let hasFilteredTag = true; tagFilters.forEach(tag => { - hasFilteredTag = hasFilteredTag && this.tags.has(tag); + hasFilteredTag = hasFilteredTag && this.tags!.has(tag); }); return hasFilteredTag; } else { @@ -261,12 +261,12 @@ export class SettingsTreeModel { } } - getElementById(id: string): SettingsTreeElement { - return this._treeElementsById.get(id); + getElementById(id: string): SettingsTreeElement | null { + return this._treeElementsById.get(id) || null; } - getElementsByName(name: string): SettingsTreeSettingElement[] { - return this._treeElementsBySettingName.get(name); + getElementsByName(name: string): SettingsTreeSettingElement[] | null { + return this._treeElementsBySettingName.get(name) || null; } updateElementsByName(name: string): void { @@ -274,7 +274,7 @@ export class SettingsTreeModel { return; } - this._treeElementsBySettingName.get(name).forEach(element => { + this._treeElementsBySettingName.get(name)!.forEach(element => { const inspectResult = inspectSetting(element.setting.key, this._viewState.settingsTarget, this._configurationService); element.update(inspectResult); }); @@ -428,7 +428,7 @@ export const enum SearchResultIdx { export class SearchResultModel extends SettingsTreeModel { private rawSearchResults: ISearchResult[]; - private cachedUniqueSearchResults: ISearchResult[]; + private cachedUniqueSearchResults: ISearchResult[] | undefined; private newExtensionSearchResults: ISearchResult; readonly id = 'searchResultModel'; @@ -473,8 +473,8 @@ export class SearchResultModel extends SettingsTreeModel { return this.rawSearchResults; } - setResult(order: SearchResultIdx, result: ISearchResult): void { - this.cachedUniqueSearchResults = null; + setResult(order: SearchResultIdx, result: ISearchResult | null): void { + this.cachedUniqueSearchResults = undefined; this.rawSearchResults = this.rawSearchResults || []; if (!result) { delete this.rawSearchResults[order]; diff --git a/src/vs/workbench/contrib/preferences/browser/tocTree.ts b/src/vs/workbench/contrib/preferences/browser/tocTree.ts index 273471fdcfa..d3fdbdcc6f5 100644 --- a/src/vs/workbench/contrib/preferences/browser/tocTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/tocTree.ts @@ -22,7 +22,7 @@ const $ = DOM.$; export class TOCTreeModel { - private _currentSearchModel: SearchResultModel; + private _currentSearchModel: SearchResultModel | null; private _settingsTreeRoot: SettingsTreeGroupElement; constructor(private _viewState: ISettingsEditorViewState) { @@ -37,7 +37,11 @@ export class TOCTreeModel { this.update(); } - set currentSearchModel(model: SearchResultModel) { + get currentSearchModel(): SearchResultModel | null { + return this._currentSearchModel; + } + + set currentSearchModel(model: SearchResultModel | null) { this._currentSearchModel = model; this.update(); } @@ -61,7 +65,7 @@ export class TOCTreeModel { const childCount = group.children .filter(child => child instanceof SettingsTreeGroupElement) - .reduce((acc, cur) => acc + (cur).count, 0); + .reduce((acc, cur) => acc + (cur).count!, 0); group.count = childCount + this.getGroupCount(group); } 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 4edd06ce8e7..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; } @@ -277,7 +278,7 @@ class RemoteSearchProvider implements ISearchProvider { headers, timeout: 5000 }, CancellationToken.None).then(context => { - if (context.res.statusCode >= 300) { + if (typeof context.res.statusCode === 'number' && context.res.statusCode >= 300) { throw new Error(`${JSON.stringify(details)} returned status code: ${context.res.statusCode}`); } @@ -426,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 @@ -536,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, @@ -555,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, @@ -574,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..8dccdbea88c 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; private localSearchDelayer: Delayer; private remoteSearchThrottle: ThrottledDelayer; - private searchInProgress: CancellationTokenSource; + private searchInProgress: CancellationTokenSource | null; private settingFastUpdateDelayer: Delayer; private settingSlowUpdateDelayer: Delayer; - private pendingSettingUpdate: { key: string, value: any }; + private pendingSettingUpdate: { key: string, value: any } | null; private readonly viewState: ISettingsEditorViewState; - private _searchResultModel: SearchResultModel; + private _searchResultModel: SearchResultModel | null; private tocRowFocused: IContextKey; private inSettingsEditorContextKey: IContextKey; @@ -124,7 +128,7 @@ export class SettingsEditor2 extends BaseEditor { private editorMemento: IEditorMemento; - private tocFocusedElement: SettingsTreeGroupElement; + private tocFocusedElement: SettingsTreeGroupElement | null; private settingsTreeScrollTop = 0; constructor( @@ -176,18 +180,19 @@ export class SettingsEditor2 extends BaseEditor { return this.searchResultModel || this.settingsTreeModel; } - private get searchResultModel(): SearchResultModel { + private get searchResultModel(): SearchResultModel | null { return this._searchResultModel; } - private set searchResultModel(value: SearchResultModel) { + private set searchResultModel(value: SearchResultModel | null) { this._searchResultModel = value; DOM.toggleClass(this.rootElement, 'search-mode', !!this._searchResultModel); } - private get currentSettingsContextMenuKeyBindingLabel() { - return this.keybindingService.lookupKeybinding(SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU).getAriaLabel(); + private get currentSettingsContextMenuKeyBindingLabel(): string { + const keybinding = this.keybindingService.lookupKeybinding(SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU); + return (keybinding && keybinding.getAriaLabel()) || ''; } createEditor(parent: HTMLElement): void { @@ -229,7 +234,7 @@ export class SettingsEditor2 extends BaseEditor { } private restoreCachedState(): void { - const cachedState = this.editorMemento.loadEditorState(this.group, this.input); + const cachedState = this.group && this.input && this.editorMemento.loadEditorState(this.group, this.input); if (cachedState && typeof cachedState.target === 'object') { cachedState.target = URI.revive(cachedState.target); } @@ -264,14 +269,14 @@ export class SettingsEditor2 extends BaseEditor { clearInput(): void { this.inSettingsEditorContextKey.set(false); - this.editorMemento.clearEditorState(this.input, this.group); + if (this.input) { + this.editorMemento.clearEditorState(this.input, this.group); + } + super.clearInput(); } layout(dimension: DOM.Dimension): void { - // const firstEl = this.settingsTree.getFirstVisibleElement(); - // const firstElTop = this.settingsTree.getRelativeTop(firstEl); - this.layoutTrees(dimension); const innerWidth = dimension.width - 24 * 2; // 24px padding on left and right @@ -314,7 +319,12 @@ export class SettingsEditor2 extends BaseEditor { } showContextMenu(): void { - const settingDOMElement = this.settingRenderers.getSettingDOMElementForDOMElement(this.getActiveElementInSettingsTree()); + const activeElement = this.getActiveElementInSettingsTree(); + if (!activeElement) { + return; + } + + const settingDOMElement = this.settingRenderers.getSettingDOMElementForDOMElement(activeElement); if (!settingDOMElement) { return; } @@ -382,9 +392,10 @@ export class SettingsEditor2 extends BaseEditor { this._register(attachStylerCallback(this.themeService, { badgeBackground, contrastBorder, badgeForeground }, colors => { const background = colors.badgeBackground ? colors.badgeBackground.toString() : null; const border = colors.contrastBorder ? colors.contrastBorder.toString() : null; + const foreground = colors.badgeForeground ? colors.badgeForeground.toString() : null; this.countElement.style.backgroundColor = background; - this.countElement.style.color = colors.badgeForeground.toString(); + this.countElement.style.color = foreground; this.countElement.style.borderWidth = border ? '1px' : null; this.countElement.style.borderStyle = border ? 'solid' : null; @@ -411,6 +422,10 @@ export class SettingsEditor2 extends BaseEditor { const elements = this.currentSettingsModel.getElementsByName(evt.targetKey); if (elements && elements[0]) { let sourceTop = this.settingsTree.getRelativeTop(evt.source); + if (typeof sourceTop !== 'number') { + return; + } + if (sourceTop < 0) { // e.g. clicked a searched element, now the search has been cleared sourceTop = 0.5; @@ -434,12 +449,12 @@ export class SettingsEditor2 extends BaseEditor { } } - switchToSettingsFile(): Promise { + switchToSettingsFile(): Promise { const query = parseQuery(this.searchWidget.getValue()); return this.openSettingsFile(query.query); } - private openSettingsFile(query?: string): Promise { + private openSettingsFile(query?: string): Promise { const currentSettingsTarget = this.settingsTargetsWidget.settingsTarget; const options: ISettingsEditorOptions = { query }; @@ -493,7 +508,7 @@ export class SettingsEditor2 extends BaseEditor { e => { 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 +530,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 +542,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); @@ -573,7 +564,7 @@ export class SettingsEditor2 extends BaseEditor { this.viewState)); this._register(this.tocTree.onDidChangeFocus(e => { - const element: SettingsTreeGroupElement = e.elements[0]; + const element: SettingsTreeGroupElement | null = e.elements[0]; if (this.tocFocusedElement === element) { return; } @@ -582,14 +573,11 @@ export class SettingsEditor2 extends BaseEditor { this.tocTree.setSelection(element ? [element] : []); 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.viewState.filterToCategory = element || undefined; + 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 +675,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; @@ -698,6 +686,10 @@ export class SettingsEditor2 extends BaseEditor { this.tocTree.reveal(element); const elementTop = this.tocTree.getRelativeTop(element); + if (typeof elementTop !== 'number') { + return; + } + this.tocTree.collapseAll(); ancestors.forEach(e => this.tocTree.expand(e)); @@ -712,7 +704,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); } } @@ -764,17 +756,17 @@ export class SettingsEditor2 extends BaseEditor { }); } - private reportModifiedSetting(props: { key: string, query: string, searchResults: ISearchResult[], rawResults: ISearchResult[], showConfiguredOnly: boolean, isReset: boolean, settingsTarget: SettingsTarget }): void { + private reportModifiedSetting(props: { key: string, query: string, searchResults: ISearchResult[] | null, rawResults: ISearchResult[] | null, showConfiguredOnly: boolean, isReset: boolean, settingsTarget: SettingsTarget }): void { this.pendingSettingUpdate = null; - const remoteResult = props.searchResults && props.searchResults[SearchResultIdx.Remote]; - const localResult = props.searchResults && props.searchResults[SearchResultIdx.Local]; - - let groupId = undefined; - let nlpIndex = undefined; - let displayIndex = undefined; + let groupId: string | undefined = undefined; + let nlpIndex: number | undefined = undefined; + let displayIndex: number | undefined = undefined; if (props.searchResults) { - const localIndex = arrays.firstIndex(localResult.filterMatches, m => m.setting.key === props.key); + const remoteResult = props.searchResults[SearchResultIdx.Remote]; + const localResult = props.searchResults[SearchResultIdx.Local]; + + const localIndex = arrays.firstIndex(localResult!.filterMatches, m => m.setting.key === props.key); groupId = localIndex >= 0 ? 'local' : 'remote'; @@ -886,9 +878,9 @@ export class SettingsEditor2 extends BaseEditor { } const commonlyUsed = resolveSettingsTree(commonlyUsedData, dividedGroups.core); - resolvedSettingsRoot.children.unshift(commonlyUsed.tree); + resolvedSettingsRoot.children!.unshift(commonlyUsed.tree); - resolvedSettingsRoot.children.push(resolveExtensionsSettings(dividedGroups.extension || [])); + resolvedSettingsRoot.children!.push(resolveExtensionsSettings(dividedGroups.extension || [])); if (this.searchResultModel) { this.searchResultModel.updateChildren(); @@ -898,7 +890,7 @@ export class SettingsEditor2 extends BaseEditor { this.settingsTreeModel.update(resolvedSettingsRoot); // Make sure that all extensions' settings are included in search results - const cachedState = this.editorMemento.loadEditorState(this.group, this.input); + const cachedState = this.group && this.input && this.editorMemento.loadEditorState(this.group, this.input); if (cachedState && cachedState.searchQuery) { this.triggerSearch(cachedState.searchQuery); } else { @@ -921,7 +913,7 @@ export class SettingsEditor2 extends BaseEditor { private updateElementsByKey(keys: string[]): Promise { if (keys.length) { if (this.searchResultModel) { - keys.forEach(key => this.searchResultModel.updateElementsByName(key)); + keys.forEach(key => this.searchResultModel!.updateElementsByName(key)); } if (this.settingsTreeModel) { @@ -949,7 +941,8 @@ export class SettingsEditor2 extends BaseEditor { } // If a setting control is currently focused, schedule a refresh for later - const focusedSetting = this.settingRenderers.getSettingDOMElementForDOMElement(this.getActiveElementInSettingsTree()); + const activeElement = this.getActiveElementInSettingsTree(); + const focusedSetting = activeElement && this.settingRenderers.getSettingDOMElementForDOMElement(activeElement); if (focusedSetting && !force) { // If a single setting is being refreshed, it's ok to refresh now if that is not the focused setting if (key) { @@ -1004,7 +997,7 @@ export class SettingsEditor2 extends BaseEditor { const isModified = dataElements && dataElements[0] && dataElements[0].isConfigured; // all elements are either configured or not const elements = this.settingRenderers.getDOMElementsForSettingKey(this.settingsTree.getHTMLElement(), key); if (elements && elements[0]) { - DOM.toggleClass(elements[0], 'is-configured', isModified); + DOM.toggleClass(elements[0], 'is-configured', !!isModified); } } @@ -1013,12 +1006,12 @@ export class SettingsEditor2 extends BaseEditor { this.delayedFilterLogging.cancel(); this.triggerSearch(query.replace(/›/g, ' ')).then(() => { if (query && this.searchResultModel) { - this.delayedFilterLogging.trigger(() => this.reportFilteringUsed(query, this.searchResultModel.getUniqueResults())); + this.delayedFilterLogging.trigger(() => this.reportFilteringUsed(query, this.searchResultModel!.getUniqueResults())); } }); } - private parseSettingFromJSON(query: string): string { + private parseSettingFromJSON(query: string): string | null { const match = query.match(/"([a-zA-Z.]+)": /); return match && match[1]; } @@ -1028,7 +1021,7 @@ export class SettingsEditor2 extends BaseEditor { if (query) { const parsedQuery = parseQuery(query); query = parsedQuery.query; - parsedQuery.tags.forEach(tag => this.viewState.tagFilters.add(tag)); + parsedQuery.tags.forEach(tag => this.viewState.tagFilters!.add(tag)); } if (query && query !== '@') { @@ -1049,7 +1042,7 @@ export class SettingsEditor2 extends BaseEditor { this.searchInProgress = null; } - this.viewState.filterToCategory = null; + this.viewState.filterToCategory = undefined; this.tocTreeModel.currentSearchModel = this.searchResultModel; this.onSearchModeToggled(); @@ -1069,7 +1062,7 @@ export class SettingsEditor2 extends BaseEditor { this.refreshTOCTree(); } - return Promise.resolve(null); + return Promise.resolve(); } /** @@ -1147,18 +1140,18 @@ export class SettingsEditor2 extends BaseEditor { if (result && !result.exactMatch) { this.remoteSearchThrottle.trigger(() => { return searchInProgress && !searchInProgress.token.isCancellationRequested ? - this.remoteSearchPreferences(query, this.searchInProgress.token) : - Promise.resolve(null); + this.remoteSearchPreferences(query, this.searchInProgress!.token) : + Promise.resolve(); }); } }); } else { - return Promise.resolve(null); + return Promise.resolve(); } }); } - private localFilterPreferences(query: string, token?: CancellationToken): Promise { + private localFilterPreferences(query: string, token?: CancellationToken): Promise { const localSearchProvider = this.preferencesSearchService.getLocalSearchProvider(query); return this.filterOrSearchPreferences(query, SearchResultIdx.Local, localSearchProvider, token); } @@ -1173,7 +1166,7 @@ export class SettingsEditor2 extends BaseEditor { ]).then(() => { }); } - private filterOrSearchPreferences(query: string, type: SearchResultIdx, searchProvider: ISearchProvider, token?: CancellationToken): Promise { + private filterOrSearchPreferences(query: string, type: SearchResultIdx, searchProvider?: ISearchProvider, token?: CancellationToken): Promise { return this._filterOrSearchPreferencesModel(query, this.defaultSettingsEditorModel, searchProvider, token).then(result => { if (token && token.isCancellationRequested) { // Handle cancellation like this because cancellation is lost inside the search provider due to async/await @@ -1191,7 +1184,7 @@ export class SettingsEditor2 extends BaseEditor { } this.tocTree.setSelection([]); - this.viewState.filterToCategory = null; + this.viewState.filterToCategory = undefined; this.tocTree.expandAll(); return this.renderTree(undefined, true).then(() => result); @@ -1219,7 +1212,7 @@ export class SettingsEditor2 extends BaseEditor { } } - private _filterOrSearchPreferencesModel(filter: string, model: ISettingsEditorModel, provider: ISearchProvider, token?: CancellationToken): Promise { + private _filterOrSearchPreferencesModel(filter: string, model: ISettingsEditorModel, provider?: ISearchProvider, token?: CancellationToken): Promise { const searchP = provider ? provider.searchModel(model, token) : Promise.resolve(null); return searchP .then(null, err => { @@ -1238,7 +1231,7 @@ export class SettingsEditor2 extends BaseEditor { this.telemetryService.publicLog('settingsEditor.searchError', { message, filter }); this.logService.info('Setting search error: ' + message); } - return null; + return Promise.resolve(null); } }); } @@ -1258,7 +1251,9 @@ export class SettingsEditor2 extends BaseEditor { if (this.isVisible()) { const searchQuery = this.searchWidget.getValue().trim(); const target = this.settingsTargetsWidget.settingsTarget as SettingsTarget; - this.editorMemento.saveEditorState(this.group, this.input, { searchQuery, target }); + if (this.group && this.input) { + this.editorMemento.saveEditorState(this.group, this.input, { searchQuery, target }); + } } super.saveState(); 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 b8f8fbbf0a5..79dcaaae6d3 100644 --- a/src/vs/workbench/contrib/splash/electron-browser/partsSplash.contribution.ts +++ b/src/vs/workbench/contrib/splash/electron-browser/partsSplash.contribution.ts @@ -22,6 +22,7 @@ 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 { @@ -29,6 +30,7 @@ class PartsSplash { private readonly _disposables: IDisposable[] = []; + private _didChangeTitleBarStyle: boolean; private _lastBaseTheme: string; private _lastBackground?: string; @@ -39,13 +41,18 @@ class PartsSplash { @IEnvironmentService private readonly _envService: IEnvironmentService, @IBroadcastService private readonly _broadcastService: IBroadcastService, @ILifecycleService lifecycleService: ILifecycleService, - @IEditorGroupsService editorGroupsService: IEditorGroupsService + @IEditorGroupsService editorGroupsService: IEditorGroupsService, + @IConfigurationService configService: IConfigurationService, ) { lifecycleService.when(LifecyclePhase.Restored).then(_ => this._removePartsSplash()); Event.debounce(Event.any( onDidChangeFullscreen, editorGroupsService.onDidLayout ), () => { }, 800)(this._savePartsSplash, this, this._disposables); + + configService.onDidChangeConfiguration(e => { + this._didChangeTitleBarStyle = e.affectsConfiguration('window.titleBarStyle'); + }, this, this._disposables); } dispose(): void { @@ -100,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/tasks/electron-browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/electron-browser/terminalTaskSystem.ts index bfaaf9c091a..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'; @@ -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; @@ -745,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'); @@ -1213,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[] = []; /* 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 f3764101c3a..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,7 +170,7 @@ export class TerminalInstance implements ITerminalInstance { private _cols: number; private _rows: number; private _dimensionsOverride: ITerminalDimensions; - private _windowsShellHelper: WindowsShellHelper | undefined; + private _windowsShellHelper: IWindowsShellHelper | undefined; private _xtermReadyPromise: Promise; private _titleReadyPromise: Promise; private _titleReadyComplete: (title: string) => any; @@ -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({ @@ -805,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) { @@ -931,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)); @@ -948,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); } }); }); 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 94% rename from src/vs/workbench/contrib/terminal/electron-browser/terminalPanel.ts rename to src/vs/workbench/contrib/terminal/browser/terminalPanel.ts index b4e7019e896..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); } } })); @@ -284,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; @@ -306,7 +302,9 @@ export class TerminalPanel extends Panel { const terminal = this._terminalService.getActiveInstance(); if (terminal) { - terminal.sendText(terminalEnvironment.preparePathForTerminal(path), false); + 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 a3fa9af9e3d..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 = {}; @@ -141,11 +145,11 @@ export class TerminalProcessManager implements ITerminalProcessManager { 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 5673030976d..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 @@ -254,6 +253,17 @@ export interface ITerminalService { 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 ab539243fc7..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 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 fcbd99a09b3..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 @@ -81,141 +73,8 @@ 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; - } - - 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) - }] - ); + protected _getDefaultShell(p: platform.Platform): string { + return getDefaultShell(p); } public selectDefaultWindowsShell(): Promise { @@ -243,7 +102,7 @@ export class TerminalService extends AbstractTerminalService implements ITermina let useWSLexe = false; - if (TerminalInstance.getWindowsBuildNumber() >= 16299) { + if (getWindowsBuildNumber() >= 16299) { useWSLexe = true; } @@ -273,45 +132,22 @@ export class TerminalService extends AbstractTerminalService implements ITermina }); } - private _validateShellPaths(label: string, potentialPaths: string[]): Promise<[string, string] | null> { - if (potentialPaths.length === 0) { - return Promise.resolve(null); + 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'); } - const current = potentialPaths.shift(); - return pfs.fileExists(current!).then(exists => { - if (!exists) { - return this._validateShellPaths(label, potentialPaths); - } - return [label, current] as [string, string]; + 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 fbebae679ee..3d40e7ed804 100644 --- a/src/vs/workbench/contrib/terminal/node/terminalProcess.ts +++ b/src/vs/workbench/contrib/terminal/node/terminalProcess.ts @@ -9,9 +9,9 @@ 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 { @@ -51,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, @@ -106,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(() => { 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/webview/electron-browser/webviewEditorService.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewEditorService.ts index 234d1a1bcad..4fdb9469e84 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewEditorService.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewEditorService.ts @@ -12,6 +12,7 @@ import * as vscode from 'vscode'; 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'); @@ -169,7 +170,7 @@ export class WebviewEditorService implements IWebviewEditorService { canRevive( webview: WebviewEditorInput ): boolean { - for (const reviver of this._revivers) { + for (const reviver of values(this._revivers)) { if (reviver.canRevive(webview)) { return true; } @@ -180,7 +181,7 @@ export class WebviewEditorService implements IWebviewEditorService { private async tryRevive( webview: WebviewEditorInput ): Promise { - for (const reviver of this._revivers) { + for (const reviver of values(this._revivers)) { if (reviver.canRevive(webview)) { await reviver.reviveWebview(webview); return true; 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/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 d293d8e11b0..a28f951ca61 100644 --- a/src/vs/workbench/electron-browser/workbench.ts +++ b/src/vs/workbench/electron-browser/workbench.ts @@ -16,7 +16,7 @@ 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 } from 'vs/workbench/common/editor'; @@ -29,9 +29,6 @@ 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, ILayoutOptions } from 'vs/workbench/services/part/common/partService'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; @@ -46,9 +43,7 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IFileService } from 'vs/platform/files/common/files'; 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 { IHistoryService } from 'vs/workbench/services/history/common/history'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { LifecyclePhase, StartupKind, ILifecycleService, WillShutdownEvent } from 'vs/platform/lifecycle/common/lifecycle'; @@ -82,8 +77,7 @@ 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 { 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'; @@ -98,15 +92,13 @@ import { IModelService } from 'vs/editor/common/services/modelService'; import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; import { IUntitledEditorService, UntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; import { ILocalizationsService } from 'vs/platform/localizations/common/localizations'; -import { HistoryService } from 'vs/workbench/services/history/browser/history'; 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 { ILayoutService, IDimension } from 'vs/platform/layout/browser/layoutService'; // import@node -import { BackupFileService, InMemoryBackupFileService } from 'vs/workbench/services/backup/node/backupFileService'; -import { WorkspaceService } from 'vs/workbench/services/configuration/node/configurationService'; 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'; @@ -189,14 +181,14 @@ export class Workbench extends Disposable implements IPartService { private editorService: EditorService; private editorGroupService: IEditorGroupsService; - private contextViewService: ContextViewService; - private keybindingService: IKeybindingService; - private backupFileService: IBackupFileService; - private notificationService: NotificationService; - private themeService: WorkbenchThemeService; - private telemetryService: ITelemetryService; - private windowService: IWindowService; - private lifecycleService: LifecycleService; + + 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; @@ -205,26 +197,28 @@ export class Workbench extends Disposable implements IPartService { private editorPart: EditorPart; private statusbarPart: StatusbarPart; - private quickOpen: QuickOpenController; - private quickInput: QuickInputService; - - private notificationsCenter: NotificationsCenter; - private notificationsToasts: NotificationsToasts; - 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.instantiationService = instantiationService; + this.contextService = contextService; + this.storageService = storageService; + this.configurationService = configurationService; + this.environmentService = environmentService; + this.logService = logService; + this.windowsService = windowsService; + this.registerErrorHandler(); } @@ -269,11 +263,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 { @@ -311,20 +300,25 @@ export class Workbench extends Disposable implements IPartService { readFontInfo(BareFontInfo.createFromRawSettings(this.configurationService.getValue('editor'), getZoomLevel())); // Create Workbench Container - this.createWorkbench(); + this.createWorkbenchContainer(); // Services this.initServices(this.serviceCollection); + // Registries + this.startRegistries(); + // Context Keys this._register(this.instantiationService.createInstance(WorkbenchContextKeysHandler)); // Register Listeners - this.registerListeners(); - this.registerLayoutListeners(); + this.instantiationService.invokeFunction(accessor => { + this.registerListeners(accessor); + this.registerLayoutListeners(accessor); + }); // Layout State - this.initLayoutState(); + this.instantiationService.invokeFunction(accessor => this.initLayoutState(accessor)); // Render Workbench this.renderWorkbench(); @@ -335,15 +329,11 @@ 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().then(() => this.whenStarted(), error => this.whenStarted(error)); + // Restore + return this.restoreWorkbench(); } - private createWorkbench(): void { + private createWorkbenchContainer(): void { this.workbench = document.createElement('div'); const platformClass = isWindows ? 'windows' : isLinux ? 'linux' : 'mac'; @@ -355,22 +345,21 @@ export class Workbench extends Disposable implements IPartService { private initServices(serviceCollection: ServiceCollection): void { // Parts - serviceCollection.set(IPartService, this); + serviceCollection.set(IPartService, this); // TODO@Ben use SyncDescriptor + serviceCollection.set(ILayoutService, this); // TODO@Ben use SyncDescriptor // Labels serviceCollection.set(ILabelService, new SyncDescriptor(LabelService, undefined, 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); - serviceCollection.set(IWindowService, this.windowService); + serviceCollection.set(IWindowService, new SyncDescriptor(WindowService, [this.configuration])); // Product const productService = new ProductService(); - serviceCollection.set(IProductService, productService); + serviceCollection.set(IProductService, productService); // TODO@Ben use SyncDescriptor // Shared Process const sharedProcess = this.windowsService.whenSharedProcessReady() @@ -382,6 +371,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 = { @@ -390,23 +380,15 @@ 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)); + serviceCollection.set(ITelemetryService, telemetryService); // TODO@Ben use SyncDescriptor // 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(() => { - this._onShutdown.fire(); - this.dispose(); - })); + serviceCollection.set(ILifecycleService, new SyncDescriptor(LifecycleService)); // Request Service serviceCollection.set(IRequestService, new SyncDescriptor(RequestService, undefined, true)); @@ -415,12 +397,10 @@ export class Workbench extends Disposable implements IPartService { 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); + serviceCollection.set(IRemoteAgentService, new SyncDescriptor(RemoteAgentService, [this.configuration])); // Extensions Management const extensionManagementChannel = getDelayedChannel(sharedProcess.then(c => c.getChannel('extensions'))); @@ -435,8 +415,7 @@ export class Workbench extends Disposable implements IPartService { 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)); @@ -462,18 +441,16 @@ export class Workbench extends Disposable implements IPartService { // Status bar this.statusbarPart = this.instantiationService.createInstance(StatusbarPart, Identifiers.STATUSBAR_PART); - serviceCollection.set(IStatusbarService, this.statusbarPart); + serviceCollection.set(IStatusbarService, this.statusbarPart); // TODO@Ben use SyncDescriptor // Context Keys serviceCollection.set(IContextKeyService, new SyncDescriptor(ContextKeyService)); // Keybindings - this.keybindingService = this.instantiationService.createInstance(WorkbenchKeybindingService, window); - serviceCollection.set(IKeybindingService, this.keybindingService); + serviceCollection.set(IKeybindingService, new SyncDescriptor(WorkbenchKeybindingService, [window])); // Context view service - this.contextViewService = this.instantiationService.createInstance(ContextViewService, this.workbench); - serviceCollection.set(IContextViewService, this.contextViewService); + serviceCollection.set(IContextViewService, new SyncDescriptor(ContextViewService, [this.workbench], true)); // Use themable context menus when custom titlebar is enabled to match custom menubar if (!isMacintosh && getTitleBarStyle(this.configurationService, this.environmentService) === 'custom') { @@ -482,15 +459,13 @@ export class Workbench extends Disposable implements IPartService { serviceCollection.set(IContextMenuService, new SyncDescriptor(NativeContextMenuService)); } - // Sidebar part + // Viewlet service (sidebar part) this.sidebarPart = this.instantiationService.createInstance(SidebarPart, Identifiers.SIDEBAR_PART); - - // Viewlet service - serviceCollection.set(IViewletService, this.sidebarPart); + serviceCollection.set(IViewletService, this.sidebarPart); // TODO@Ben use SyncDescriptor // Panel service (panel part) this.panelPart = this.instantiationService.createInstance(PanelPart, Identifiers.PANEL_PART); - serviceCollection.set(IPanelService, this.panelPart); + serviceCollection.set(IPanelService, this.panelPart); // TODO@Ben use SyncDescriptor // Views service serviceCollection.set(IViewsService, new SyncDescriptor(ViewsService)); @@ -500,43 +475,21 @@ export class Workbench extends Disposable implements IPartService { serviceCollection.set(IActivityService, new SyncDescriptor(ActivityService, [this.activitybarPart, this.panelPart], true)); // File Service - const fileService = this.instantiationService.createInstance(RemoteFileService); - serviceCollection.set(IFileService, fileService); - this.configurationService.acquireFileService(fileService); - this.themeService.acquireFileService(fileService); + serviceCollection.set(IFileService, new SyncDescriptor(RemoteFileService)); // Editor and Group services this.editorPart = this.instantiationService.createInstance(EditorPart, Identifiers.EDITOR_PART, !this.hasInitialFilesToOpen()); this.editorGroupService = this.editorPart; - serviceCollection.set(IEditorGroupsService, this.editorPart); + serviceCollection.set(IEditorGroupsService, this.editorPart); // TODO@Ben use SyncDescriptor this.editorService = this.instantiationService.createInstance(EditorService); - serviceCollection.set(IEditorService, this.editorService); + serviceCollection.set(IEditorService, this.editorService); // TODO@Ben use SyncDescriptor // Accessibility serviceCollection.set(IAccessibilityService, new SyncDescriptor(AccessibilityService, undefined, true)); // Title bar this.titlebarPart = this.instantiationService.createInstance(TitlebarPart, Identifiers.TITLEBAR_PART); - serviceCollection.set(ITitleService, this.titlebarPart); - - // History - serviceCollection.set(IHistoryService, new SyncDescriptor(HistoryService)); - - // 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); - - // Quick open service (quick open controller) - this.quickOpen = this.instantiationService.createInstance(QuickOpenController); - serviceCollection.set(IQuickOpenService, this.quickOpen); - - // Quick input service - this.quickInput = this.instantiationService.createInstance(QuickInputService); - serviceCollection.set(IQuickInputService, this.quickInput); + serviceCollection.set(ITitleService, this.titlebarPart); // TODO@Ben use SyncDescriptor // Contributed services const contributedServices = getServices(); @@ -544,21 +497,43 @@ export class Workbench extends Disposable implements IPartService { serviceCollection.set(contributedService.id, contributedService.descriptor); } - // TODO this should move somewhere else - 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)); - } + // 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)); + } + }); - // 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@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; - // TODO@Sandeep debt around cyclic dependencies - this.configurationService.acquireInstantiationService(this.instantiationService); + if (typeof configurationService.acquireFileService === 'function') { + configurationService.acquireFileService(fileService); + } + + if (typeof configurationService.acquireInstantiationService === 'function') { + configurationService.acquireInstantiationService(instantiationService); + } + + if (typeof themeService.acquireFileService === 'function') { + themeService.acquireFileService(fileService); + } + }); + } + + 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 { @@ -568,13 +543,42 @@ export class Workbench extends Disposable implements IPartService { (this.configuration.filesToDiff && this.configuration.filesToDiff.length > 0)); } - private registerListeners(): void { + private registerListeners(accessor: ServicesAccessor): void { + const lifecycleService = accessor.get(ILifecycleService); + const storageService = accessor.get(IStorageService); + const configurationService = accessor.get(IConfigurationService); + + // Lifecycle + this._register(lifecycleService.onWillShutdown(event => this._onWillShutdown.fire(event))); + this._register(lifecycleService.onShutdown(() => { + this._onShutdown.fire(); + this.dispose(); + })); // Storage - this._register(this.storageService.onWillSaveState(e => this.saveState(e))); + this._register(storageService.onWillSaveState(e => this.saveState(e))); // Configuration changes - this._register(this.configurationService.onDidChangeConfiguration(() => this.setFontAliasing())); + this._register(configurationService.onDidChangeConfiguration(() => this.setFontAliasing())); + } + + 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; + } + + 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 { @@ -606,31 +610,12 @@ export class Workbench extends Disposable implements IPartService { this.createStatusbarPart(); // Notification Handlers - this.createNotificationsHandlers(); + this.instantiationService.invokeFunction(accessor => this.createNotificationsHandlers(accessor)); // Add Workbench to DOM this.container.appendChild(this.workbench); } - 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; - } - - 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 createTitlebarPart(): void { const titlebarContainer = this.createPart(Identifiers.TITLEBAR_PART, 'contentinfo', 'titlebar'); @@ -682,35 +667,32 @@ export class Workbench extends Disposable implements IPartService { return part; } - private createNotificationsHandlers(): void { + private createNotificationsHandlers(accessor: ServicesAccessor): void { + const notificationService = accessor.get(INotificationService) as NotificationService; - // Notifications Center - this.notificationsCenter = this._register(this.instantiationService.createInstance(NotificationsCenter, this.workbench, this.notificationService.model)); + // Instantiate Notification components + const notificationsCenter = this._register(this.instantiationService.createInstance(NotificationsCenter, this.workbench, notificationService.model)); + const notificationsToasts = this._register(this.instantiationService.createInstance(NotificationsToasts, this.workbench, notificationService.model)); + this._register(this.instantiationService.createInstance(NotificationsAlerts, notificationService.model)); + const notificationsStatus = this.instantiationService.createInstance(NotificationsStatus, notificationService.model); - // Notifications Toasts - this.notificationsToasts = this._register(this.instantiationService.createInstance(NotificationsToasts, this.workbench, this.notificationService.model)); + // Visibility + this._register(notificationsCenter.onDidChangeVisibility(() => { + notificationsStatus.update(notificationsCenter.isVisible); + notificationsToasts.update(notificationsCenter.isVisible); + })); - // 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); + // Layout + this._register(this.onLayout(dimension => { + notificationsCenter.layout(dimension); + notificationsToasts.layout(dimension); })); // Register Commands - registerNotificationCommands(this.notificationsCenter, this.notificationsToasts); + registerNotificationCommands(notificationsCenter, notificationsToasts); } - private restoreParts(): Promise { + private restoreWorkbench(): Promise { const restorePromises: Promise[] = []; // Restore editors @@ -736,14 +718,20 @@ export class Workbench extends Disposable implements IPartService { if (this.state.sideBar.viewletToRestore) { mark('willRestoreViewlet'); restorePromises.push(this.sidebarPart.openViewlet(this.state.sideBar.viewletToRestore) - .then(viewlet => viewlet || this.sidebarPart.openViewlet(this.sidebarPart.getDefaultViewletId())) + .then(viewlet => { + if (!viewlet) { + return this.sidebarPart.openViewlet(this.sidebarPart.getDefaultViewletId()); // fallback to default viewlet as needed + } + + return viewlet; + }) .then(() => mark('didRestoreViewlet'))); } // Restore Panel if (this.state.panel.panelToRestore) { mark('willRestorePanel'); - this.panelPart.openPanel(this.state.panel.panelToRestore, false); + this.panelPart.openPanel(this.state.panel.panelToRestore); mark('didRestorePanel'); } @@ -757,20 +745,30 @@ export class Workbench extends Disposable implements IPartService { this.centerEditorLayout(true); } - return Promise.all(restorePromises); + // 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); + + let error: Error; + return Promise.all(restorePromises) + .then(() => clearTimeout(restoreTimeoutHandle)) + .catch(err => error = err) + .finally(() => this.instantiationService.invokeFunction(accessor => this.whenRestored(accessor, error))); + } - private whenStarted(error?: Error): void { + private whenRestored(accessor: ServicesAccessor, error?: Error): void { + const lifecycleService = accessor.get(ILifecycleService); + this.restored = true; // Set lifecycle phase to `Restored` - this.lifecycleService.phase = LifecyclePhase.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(() => { - this.lifecycleService.phase = LifecyclePhase.Eventually; + lifecycleService.phase = LifecyclePhase.Eventually; }, 2500)); }, 2500); @@ -778,45 +776,6 @@ export class Workbench extends Disposable implements IPartService { onUnexpectedError(error); } - const { filesToOpen, filesToCreate, filesToDiff } = this.configuration; - - /* __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: this.keybindingService.customKeybindingsCount(), - theme: this.themeService.getColorTheme().id, - language, - pinnedViewlets: this.activitybarPart.getPinnedViewletIds(), - restoredViewlet: this.state.sideBar.viewletToRestore, - restoredEditors: this.editorService.visibleEditors.length, - startupKind: this.lifecycleService.startupKind - }); - // Telemetry: startup metrics mark('didStartWorkbench'); } @@ -843,6 +802,12 @@ export class Workbench extends Disposable implements IPartService { private readonly _onZenMode: Emitter = this._register(new Emitter()); get onZenModeChange(): Event { return this._onZenMode.event; } + private readonly _onLayout = this._register(new Emitter()); + get onLayout(): Event { return this._onLayout.event; } + + private _dimension: IDimension; + get dimension(): IDimension { return this._dimension; } + private workbenchGrid: Grid | WorkbenchLegacyLayout; private titleBarPartView: View; @@ -852,6 +817,8 @@ export class Workbench extends Disposable implements IPartService { private editorPartView: View; private statusBarPartView: View; + private windowService: IWindowService; + private readonly state = { fullscreen: false, @@ -901,31 +868,37 @@ export class Workbench extends Disposable implements IPartService { } }; - private registerLayoutListeners(): void { + private registerLayoutListeners(accessor: ServicesAccessor): void { + const storageService = accessor.get(IStorageService); + const editorService = accessor.get(IEditorService); + const configurationService = accessor.get(IConfigurationService); + const editorGroupService = accessor.get(IEditorGroupsService); + const titleService = accessor.get(ITitleService); + const environmentService = accessor.get(IEnvironmentService); // Storage - this._register(this.storageService.onWillSaveState(e => this.saveLayoutState(e))); + this._register(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))); + this._register(editorService.onDidVisibleEditorsChange(() => this.setEditorHidden(false))); + this._register(editorGroupService.onDidActivateGroup(() => this.setEditorHidden(false))); // Configuration changes - this._register(this.configurationService.onDidChangeConfiguration(() => this.doUpdateLayoutConfiguration())); + this._register(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))); + this._register(editorGroupService.onDidAddGroup(() => this.centerEditorLayout(this.state.editor.centered))); + this._register(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))); + if ((isWindows || isLinux) && getTitleBarStyle(configurationService, environmentService) === 'custom') { + this._register(titleService.onMenubarVisibilityChange(visible => this.onMenubarToggled(visible))); } } @@ -1033,22 +1006,29 @@ export class Workbench extends Disposable implements IPartService { } } - private initLayoutState(): void { + 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); + + this.windowService = accessor.get(IWindowService); // Fullscreen this.state.fullscreen = isFullscreen(); // Menubar visibility - this.state.menuBar.visibility = this.configurationService.getValue(Settings.MENUBAR_VISIBLE); + this.state.menuBar.visibility = configurationService.getValue(Settings.MENUBAR_VISIBLE); // Activity bar visibility - this.state.activityBar.hidden = !this.configurationService.getValue(Settings.ACTIVITYBAR_VISIBLE); + this.state.activityBar.hidden = !configurationService.getValue(Settings.ACTIVITYBAR_VISIBLE); // Sidebar visibility - this.state.sideBar.hidden = this.storageService.getBoolean(Storage.SIDEBAR_HIDDEN, StorageScope.WORKSPACE, this.contextService.getWorkbenchState() === WorkbenchState.EMPTY); + this.state.sideBar.hidden = storageService.getBoolean(Storage.SIDEBAR_HIDDEN, StorageScope.WORKSPACE, contextService.getWorkbenchState() === WorkbenchState.EMPTY); // Sidebar position - this.state.sideBar.position = (this.configurationService.getValue(Settings.SIDEBAR_POSITION) === 'right') ? Position.RIGHT : Position.LEFT; + this.state.sideBar.position = (configurationService.getValue(Settings.SIDEBAR_POSITION) === 'right') ? Position.RIGHT : Position.LEFT; // Sidebar viewlet if (!this.state.sideBar.hidden) { @@ -1056,8 +1036,8 @@ export class Workbench extends Disposable implements IPartService { // Only restore last viewlet if window was reloaded or we are in development mode let viewletToRestore: string; - if (!this.environmentService.isBuilt || this.lifecycleService.startupKind === StartupKind.ReloadedWindow) { - viewletToRestore = this.storageService.get(SidebarPart.activeViewletSettingsKey, StorageScope.WORKSPACE, viewletRegistry.getDefaultViewletId()); + if (!environmentService.isBuilt || lifecycleService.startupKind === StartupKind.ReloadedWindow) { + viewletToRestore = storageService.get(SidebarPart.activeViewletSettingsKey, StorageScope.WORKSPACE, viewletRegistry.getDefaultViewletId()); } else { viewletToRestore = viewletRegistry.getDefaultViewletId(); } @@ -1070,13 +1050,13 @@ export class Workbench extends Disposable implements IPartService { } // Editor centered layout - this.state.editor.restoreCentered = this.storageService.getBoolean(Storage.CENTERED_LAYOUT_ENABLED, StorageScope.WORKSPACE, false); + this.state.editor.restoreCentered = storageService.getBoolean(Storage.CENTERED_LAYOUT_ENABLED, StorageScope.WORKSPACE, false); // Editors to open - this.state.editor.editorsToOpen = this.resolveEditorsToOpen(); + this.state.editor.editorsToOpen = this.resolveEditorsToOpen(accessor); // Panel visibility - this.state.panel.hidden = this.storageService.getBoolean(Storage.PANEL_HIDDEN, StorageScope.WORKSPACE, true); + this.state.panel.hidden = storageService.getBoolean(Storage.PANEL_HIDDEN, StorageScope.WORKSPACE, true); // Panel position this.updatePanelPosition(); @@ -1085,7 +1065,7 @@ export class Workbench extends Disposable implements IPartService { if (!this.state.panel.hidden) { const panelRegistry = Registry.as(PanelExtensions.Panels); - let panelToRestore = this.storageService.get(PanelPart.activePanelSettingsKey, StorageScope.WORKSPACE, panelRegistry.getDefaultPanelId()); + let panelToRestore = storageService.get(PanelPart.activePanelSettingsKey, StorageScope.WORKSPACE, panelRegistry.getDefaultPanelId()); if (!panelRegistry.hasPanel(panelToRestore)) { panelToRestore = panelRegistry.getDefaultPanelId(); // fallback to default if panel is unknown } @@ -1098,19 +1078,24 @@ export class Workbench extends Disposable implements IPartService { } // Statusbar visibility - this.state.statusBar.hidden = !this.configurationService.getValue(Settings.STATUSBAR_VISIBLE); + this.state.statusBar.hidden = !configurationService.getValue(Settings.STATUSBAR_VISIBLE); // Zen mode enablement - this.state.zenMode.restore = this.storageService.getBoolean(Storage.ZEN_MODE_ENABLED, StorageScope.WORKSPACE, false) && this.configurationService.getValue(Settings.ZEN_MODE_RESTORE); + this.state.zenMode.restore = storageService.getBoolean(Storage.ZEN_MODE_ENABLED, StorageScope.WORKSPACE, false) && configurationService.getValue(Settings.ZEN_MODE_RESTORE); } - private resolveEditorsToOpen(): Promise | IResourceEditor[] { + 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()) { // 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, @@ -1120,21 +1105,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.configurationService.inspect('workbench.startupEditor').value === 'newUntitledFile') { - 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 } @@ -1330,6 +1315,7 @@ export class Workbench extends Disposable implements IPartService { 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. @@ -1398,38 +1384,30 @@ export class Workbench extends Disposable implements IPartService { 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.disposed) { + this._dimension = getClientArea(this.container); + if (this.workbenchGrid instanceof Grid) { - const dimensions = getClientArea(this.container); position(this.workbench, 0, 0, 0, 0, 'relative'); - size(this.workbench, dimensions.width, dimensions.height); + size(this.workbench, this._dimension.width, this._dimension.height); - // Layout the grid - this.workbenchGrid.layout(dimensions.width, dimensions.height); + // Layout the grid widget + this.workbenchGrid.layout(this._dimension.width, this._dimension.height); - // Layout non-view ui components - this.quickInput.layout(dimensions); - this.quickOpen.layout(dimensions); - this.notificationsCenter.layout(dimensions); - this.notificationsToasts.layout(dimensions); - - // Layout Grid + // Layout grid views this.layoutGrid(); } else { this.workbenchGrid.layout(options); } + + // Emit as event + this._onLayout.fire(this._dimension); } } 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/bulkEdit/browser/bulkEditService.ts b/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts index 93494c3bbc7..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 { ITextModelService, IActiveTextEditorModel } 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 e8f1ab3be9b..5c08f612786 100644 --- a/src/vs/workbench/services/configuration/node/jsonEditingService.ts +++ b/src/vs/workbench/services/configuration/node/jsonEditingService.ts @@ -17,7 +17,7 @@ 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, IActiveTextEditorModel } 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'; @@ -84,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 }); @@ -98,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; }); 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/dialogs/electron-browser/remoteFileDialog.ts b/src/vs/workbench/services/dialogs/electron-browser/remoteFileDialog.ts index 50395ba25f6..299ac57f673 100644 --- a/src/vs/workbench/services/dialogs/electron-browser/remoteFileDialog.ts +++ b/src/vs/workbench/services/dialogs/electron-browser/remoteFileDialog.ts @@ -97,8 +97,8 @@ export class RemoteFileDialog { } private getOptions(options: ISaveDialogOptions | IOpenDialogOptions): IOpenDialogOptions | undefined { - const defaultUri = options.defaultUri ? options.defaultUri : URI.from({ scheme: REMOTE_HOST_SCHEME, authority: this.remoteAuthority, path: '/' }); - if (!this.remoteFileService.canHandleResource(defaultUri)) { + const defaultUri = options.defaultUri ? options.defaultUri : URI.from({ scheme: this.scheme, authority: this.remoteAuthority, path: '/' }); + if ((this.scheme !== Schemas.file) && !this.remoteFileService.canHandleResource(defaultUri)) { this.notificationService.info(nls.localize('remoteFileDialog.notConnectedToRemote', 'File system provider for {0} is not available.', defaultUri.toString())); return undefined; } @@ -119,7 +119,7 @@ export class RemoteFileDialog { 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 homedir: URI = options.defaultUri ? options.defaultUri : this.workspaceContextService.getWorkspace().folders[0].uri; let trailing: string | undefined; let stat: IFileStat | undefined; let ext: string = resources.extname(options.defaultUri); @@ -214,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); @@ -256,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; @@ -288,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); @@ -299,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); @@ -321,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]; @@ -442,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; @@ -468,7 +468,9 @@ export class RemoteFileDialog { if (i1.isFolder !== i2.isFolder) { return i1.isFolder ? -1 : 1; } - return i1.label.localeCompare(i2.label); + const trimmed1 = this.endsWithSlash(i1.label) ? i1.label.substr(0, i1.label.length - 1) : i1.label; + const trimmed2 = this.endsWithSlash(i2.label) ? i2.label.substr(0, i2.label.length - 1) : i2.label; + return trimmed1.localeCompare(trimmed2); }); if (backDir) { diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index b9665358c3b..d04efc8119d 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -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 948c1ec185c..b4913a3e9d5 100644 --- a/src/vs/workbench/services/editor/common/editorGroupsService.ts +++ b/src/vs/workbench/services/editor/common/editorGroupsService.ts @@ -165,6 +165,11 @@ export interface IEditorGroupsService { */ readonly onDidMoveGroup: Event; + /** + * An event for when a group gets activated. + */ + readonly onDidActivateGroup: Event; + /** * An event for when the group container is layed out. */ 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/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..c01f0c214ac 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,11 +549,11 @@ export class RemoteFileService extends FileService { } } - private _doMoveWithInScheme(source: URI, target: URI, overwrite?: boolean): Promise { + private _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); + ? Promise.resolve(this.del(target, { recursive: true }).catch(_err => { /*ignore*/ })) + : Promise.resolve(); return prepare.then(() => this._withProvider(source)).then(RemoteFileService._throwIfFileSystemIsReadonly).then(provider => { return RemoteFileService._mkdirp(provider, resources.dirname(target)).then(() => { @@ -593,7 +593,7 @@ export class RemoteFileService extends FileService { 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)); @@ -608,12 +608,12 @@ export class RemoteFileService extends FileService { } const prepare = overwrite - ? Promise.resolve(this.del(target, { recursive: true }).then(undefined, err => { /*ignore*/ })) - : Promise.resolve(null); + ? Promise.resolve(this.del(target, { recursive: true }).catch(_err => { /*ignore*/ })) + : Promise.resolve(); + // todo@ben, can only copy text files + // https://github.com/Microsoft/vscode/issues/41543 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( @@ -643,15 +643,11 @@ export class RemoteFileService extends FileService { 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 +659,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..11c83492a74 100644 --- a/src/vs/workbench/services/history/browser/history.ts +++ b/src/vs/workbench/services/history/browser/history.ts @@ -30,6 +30,7 @@ import { EditorServiceImpl } from 'vs/workbench/browser/parts/editor/editor'; import { IPartService } from 'vs/workbench/services/part/common/partService'; import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { coalesce } from 'vs/base/common/arrays'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; /** * Stores the selection & view state of an editor and allows to compare it to other selection states. @@ -420,7 +421,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 }; @@ -971,3 +972,5 @@ export class HistoryService extends Disposable implements IHistoryService { return undefined; } } + +registerSingleton(IHistoryService, HistoryService); \ No newline at end of file diff --git a/src/vs/workbench/services/output/common/outputChannelModel.ts b/src/vs/workbench/services/output/common/outputChannelModel.ts new file mode 100644 index 00000000000..aabcd3ffc54 --- /dev/null +++ b/src/vs/workbench/services/output/common/outputChannelModel.ts @@ -0,0 +1,413 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import * as strings from 'vs/base/common/strings'; +import { ITextModel } from 'vs/editor/common/model'; +import { Emitter, Event } from 'vs/base/common/event'; +import { URI } from 'vs/base/common/uri'; +import { RunOnceScheduler, ThrottledDelayer } from 'vs/base/common/async'; +import { IFileService } from 'vs/platform/files/common/files'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { Disposable, toDisposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { isNumber } from 'vs/base/common/types'; +import { EditOperation } from 'vs/editor/common/core/editOperation'; +import { Position } from 'vs/editor/common/core/position'; +import { binarySearch } from 'vs/base/common/arrays'; + +export interface IOutputChannelModel extends IDisposable { + readonly onDidAppendedContent: Event; + readonly onDispose: Event; + append(output: string): void; + update(): void; + loadModel(): Promise; + clear(till?: number): void; +} + +export const IOutputChannelModelService = createDecorator('outputChannelModelService'); + +export interface IOutputChannelModelService { + _serviceBrand: any; + + createOutputChannelModel(id: string, modelUri: URI, mimeType: string, file?: URI): IOutputChannelModel; + +} + +export abstract class AsbtractOutputChannelModelService { + + constructor( + @IInstantiationService protected readonly instantiationService: IInstantiationService + ) { } + + createOutputChannelModel(id: string, modelUri: URI, mimeType: string, file?: URI): IOutputChannelModel { + return file ? this.instantiationService.createInstance(FileOutputChannelModel, modelUri, mimeType, file) : this.instantiationService.createInstance(BufferredOutputChannel, modelUri, mimeType); + } + +} + +export abstract class AbstractFileOutputChannelModel extends Disposable implements IOutputChannelModel { + + protected _onDidAppendedContent = new Emitter(); + readonly onDidAppendedContent: Event = this._onDidAppendedContent.event; + + protected _onDispose = new Emitter(); + readonly onDispose: Event = this._onDispose.event; + + protected modelUpdater: RunOnceScheduler; + protected model: ITextModel | null; + + protected startOffset: number = 0; + protected endOffset: number = 0; + + constructor( + private readonly modelUri: URI, + private readonly mimeType: string, + protected readonly file: URI, + protected fileService: IFileService, + protected modelService: IModelService, + protected modeService: IModeService, + ) { + super(); + this.modelUpdater = new RunOnceScheduler(() => this.updateModel(), 300); + this._register(toDisposable(() => this.modelUpdater.cancel())); + } + + clear(till?: number): void { + if (this.modelUpdater.isScheduled()) { + this.modelUpdater.cancel(); + this.onUpdateModelCancelled(); + } + if (this.model) { + this.model.setValue(''); + } + this.endOffset = isNumber(till) ? till : this.endOffset; + this.startOffset = this.endOffset; + } + + update(): void { } + + protected createModel(content: string): ITextModel { + if (this.model) { + this.model.setValue(content); + } else { + this.model = this.modelService.createModel(content, this.modeService.create(this.mimeType), this.modelUri); + this.onModelCreated(this.model); + const disposables: IDisposable[] = []; + disposables.push(this.model.onWillDispose(() => { + this.onModelWillDispose(this.model); + this.model = null; + dispose(disposables); + })); + } + return this.model; + } + + appendToModel(content: string): void { + if (this.model && content) { + const lastLine = this.model.getLineCount(); + const lastLineMaxColumn = this.model.getLineMaxColumn(lastLine); + this.model.applyEdits([EditOperation.insert(new Position(lastLine, lastLineMaxColumn), content)]); + this._onDidAppendedContent.fire(); + } + } + + abstract loadModel(): Promise; + abstract append(message: string); + + protected onModelCreated(model: ITextModel) { } + protected onModelWillDispose(model: ITextModel | null) { } + protected onUpdateModelCancelled() { } + protected updateModel() { } + + dispose(): void { + this._onDispose.fire(); + super.dispose(); + } +} + +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. + */ +export class FileOutputChannelModel extends AbstractFileOutputChannelModel implements IOutputChannelModel { + + private readonly fileHandler: OutputFileListener; + + private updateInProgress: boolean = false; + private etag: string | undefined = ''; + private loadModelPromise: Promise | null = null; + + constructor( + modelUri: URI, + mimeType: string, + file: URI, + @IFileService fileService: IFileService, + @IModelService modelService: IModelService, + @IModeService modeService: IModeService + ) { + super(modelUri, mimeType, file, fileService, modelService, modeService); + + 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())); + } + + loadModel(): Promise { + this.loadModelPromise = this.fileService.resolveContent(this.file, { position: this.startOffset, encoding: 'utf8' }) + .then(content => { + this.endOffset = this.startOffset + Buffer.from(content.value).byteLength; + this.etag = content.etag; + return this.createModel(content.value); + }); + return this.loadModelPromise; + } + + clear(till?: number): void { + const loadModelPromise: Promise = this.loadModelPromise ? this.loadModelPromise : Promise.resolve(); + loadModelPromise.then(() => { + super.clear(till); + this.update(); + }); + } + + append(message: string): void { + throw new Error('Not supported'); + } + + protected updateModel(): void { + if (this.model) { + this.fileService.resolveContent(this.file, { position: this.endOffset, encoding: 'utf8' }) + .then(content => { + this.etag = content.etag; + if (content.value) { + this.endOffset = this.endOffset + Buffer.from(content.value).byteLength; + this.appendToModel(content.value); + } + this.updateInProgress = false; + }, () => this.updateInProgress = false); + } else { + this.updateInProgress = false; + } + } + + protected onModelCreated(model: ITextModel): void { + this.fileHandler.watch(this.etag); + } + + protected onModelWillDispose(model: ITextModel | null): void { + this.fileHandler.unwatch(); + } + + protected onUpdateModelCancelled(): void { + this.updateInProgress = false; + } + + update(size?: number): void { + 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(); + } + } + } +} + +class BufferredOutputChannel extends Disposable implements IOutputChannelModel { + + readonly file: URI | null = null; + scrollLock: boolean = false; + + protected _onDidAppendedContent = new Emitter(); + readonly onDidAppendedContent: Event = this._onDidAppendedContent.event; + + private readonly _onDispose = new Emitter(); + readonly onDispose: Event = this._onDispose.event; + + private modelUpdater: RunOnceScheduler; + private model: ITextModel | null; + private readonly bufferredContent: BufferedContent; + private lastReadId: number | undefined = undefined; + + constructor( + private readonly modelUri: URI, private readonly mimeType: string, + @IModelService private readonly modelService: IModelService, + @IModeService private readonly modeService: IModeService + ) { + super(); + + this.modelUpdater = new RunOnceScheduler(() => this.updateModel(), 300); + this._register(toDisposable(() => this.modelUpdater.cancel())); + + this.bufferredContent = new BufferedContent(); + this._register(toDisposable(() => this.bufferredContent.clear())); + } + + append(output: string) { + this.bufferredContent.append(output); + if (!this.modelUpdater.isScheduled()) { + this.modelUpdater.schedule(); + } + } + + update(): void { } + + clear(): void { + if (this.modelUpdater.isScheduled()) { + this.modelUpdater.cancel(); + } + if (this.model) { + this.model.setValue(''); + } + this.bufferredContent.clear(); + this.lastReadId = undefined; + } + + loadModel(): Promise { + const { value, id } = this.bufferredContent.getDelta(this.lastReadId); + if (this.model) { + this.model.setValue(value); + } else { + this.model = this.createModel(value); + } + this.lastReadId = id; + return Promise.resolve(this.model); + } + + private createModel(content: string): ITextModel { + const model = this.modelService.createModel(content, this.modeService.create(this.mimeType), this.modelUri); + const disposables: IDisposable[] = []; + disposables.push(model.onWillDispose(() => { + this.model = null; + dispose(disposables); + })); + return model; + } + + private updateModel(): void { + if (this.model) { + const { value, id } = this.bufferredContent.getDelta(this.lastReadId); + this.lastReadId = id; + const lastLine = this.model.getLineCount(); + const lastLineMaxColumn = this.model.getLineMaxColumn(lastLine); + this.model.applyEdits([EditOperation.insert(new Position(lastLine, lastLineMaxColumn), value)]); + this._onDidAppendedContent.fire(); + } + } + + dispose(): void { + this._onDispose.fire(); + super.dispose(); + } +} + +class BufferedContent { + + private static MAX_OUTPUT_LENGTH = 10000 /* Max. number of output lines to show in output */ * 100 /* Guestimated chars per line */; + + private data: string[] = []; + private dataIds: number[] = []; + private idPool = 0; + private length = 0; + + public append(content: string): void { + this.data.push(content); + this.dataIds.push(++this.idPool); + this.length += content.length; + this.trim(); + } + + public clear(): void { + this.data.length = 0; + this.dataIds.length = 0; + this.length = 0; + } + + private trim(): void { + if (this.length < BufferedContent.MAX_OUTPUT_LENGTH * 1.2) { + return; + } + + while (this.length > BufferedContent.MAX_OUTPUT_LENGTH) { + this.dataIds.shift(); + const removed = this.data.shift(); + if (removed) { + this.length -= removed.length; + } + } + } + + public getDelta(previousId?: number): { value: string, id: number } { + let idx = -1; + if (previousId !== undefined) { + idx = binarySearch(this.dataIds, previousId, (a, b) => a - b); + } + + const id = this.idPool; + if (idx >= 0) { + const value = strings.removeAnsiEscapeCodes(this.data.slice(idx + 1).join('')); + return { value, id }; + } else { + const value = strings.removeAnsiEscapeCodes(this.data.join('')); + return { value, id }; + } + } +} \ No newline at end of file diff --git a/src/vs/workbench/services/output/common/outputChannelModelService.ts b/src/vs/workbench/services/output/common/outputChannelModelService.ts new file mode 100644 index 00000000000..6aceb81e99e --- /dev/null +++ b/src/vs/workbench/services/output/common/outputChannelModelService.ts @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IOutputChannelModelService, AsbtractOutputChannelModelService } from 'vs/workbench/services/output/common/outputChannelModel'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; + +export class OutputChannelModelService extends AsbtractOutputChannelModelService implements IOutputChannelModelService { + _serviceBrand: any; +} + +registerSingleton(IOutputChannelModelService, OutputChannelModelService); + diff --git a/src/vs/workbench/contrib/output/node/outputAppender.ts b/src/vs/workbench/services/output/node/outputAppender.ts similarity index 100% rename from src/vs/workbench/contrib/output/node/outputAppender.ts rename to src/vs/workbench/services/output/node/outputAppender.ts diff --git a/src/vs/workbench/services/output/node/outputChannelModelService.ts b/src/vs/workbench/services/output/node/outputChannelModelService.ts new file mode 100644 index 00000000000..7709518de6f --- /dev/null +++ b/src/vs/workbench/services/output/node/outputChannelModelService.ts @@ -0,0 +1,192 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import * as extfs from 'vs/base/node/extfs'; +import { dirname, join } from 'vs/base/common/path'; +import { ITextModel } from 'vs/editor/common/model'; +import { URI } from 'vs/base/common/uri'; +import { ThrottledDelayer } from 'vs/base/common/async'; +import { IFileService } from 'vs/platform/files/common/files'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { toDisposable, IDisposable } from 'vs/base/common/lifecycle'; +import { ILogService } from 'vs/platform/log/common/log'; +import { IOutputChannelModel, AbstractFileOutputChannelModel, IOutputChannelModelService, AsbtractOutputChannelModelService } from 'vs/workbench/services/output/common/outputChannelModel'; +import { OutputAppender } from 'vs/workbench/services/output/node/outputAppender'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IWindowService } from 'vs/platform/windows/common/windows'; +import { toLocalISOString } from 'vs/base/common/date'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; + +let watchingOutputDir = false; +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) { + const watcherDisposable = extfs.watch(outputDir, (eventType, fileName) => { + for (const callback of callbacks) { + callback(eventType, fileName); + } + }, (error: string) => { + logService.error(error); + }); + watchingOutputDir = true; + return toDisposable(() => { + callbacks = []; + watcherDisposable.dispose(); + }); + } + return toDisposable(() => { }); +} + +class OutputChannelBackedByFile extends AbstractFileOutputChannelModel implements IOutputChannelModel { + + private appender: OutputAppender; + private appendedMessage: string; + private loadingFromFileInProgress: boolean; + private resettingDelayer: ThrottledDelayer; + private readonly rotatingFilePath: string; + + constructor( + id: string, + modelUri: URI, + mimeType: string, + @IWindowService windowService: IWindowService, + @IEnvironmentService environmentService: IEnvironmentService, + @IFileService fileService: IFileService, + @IModelService modelService: IModelService, + @IModeService modeService: IModeService, + @ILogService logService: ILogService + ) { + const outputDir = join(environmentService.logsPath, `output_${windowService.getCurrentWindowId()}_${toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')}`); + super(modelUri, mimeType, URI.file(join(outputDir, `${id}.log`)), fileService, modelService, modeService); + this.appendedMessage = ''; + this.loadingFromFileInProgress = false; + + // Use one rotating file to check for main file reset + this.appender = new OutputAppender(id, this.file.fsPath); + this.rotatingFilePath = `${id}.1.log`; + this._register(watchOutputDirectory(dirname(this.file.fsPath), logService, (eventType, file) => this.onFileChangedInOutputDirector(eventType, file))); + + this.resettingDelayer = new ThrottledDelayer(50); + } + + append(message: string): void { + // update end offset always as message is read + this.endOffset = this.endOffset + Buffer.from(message).byteLength; + if (this.loadingFromFileInProgress) { + this.appendedMessage += message; + } else { + this.write(message); + if (this.model) { + this.appendedMessage += message; + if (!this.modelUpdater.isScheduled()) { + this.modelUpdater.schedule(); + } + } + } + } + + clear(till?: number): void { + super.clear(till); + this.appendedMessage = ''; + } + + loadModel(): Promise { + this.loadingFromFileInProgress = true; + if (this.modelUpdater.isScheduled()) { + this.modelUpdater.cancel(); + } + this.appendedMessage = ''; + return this.loadFile() + .then(content => { + if (this.endOffset !== this.startOffset + Buffer.from(content).byteLength) { + // Queue content is not written into the file + // Flush it and load file again + this.flush(); + return this.loadFile(); + } + return content; + }) + .then(content => { + if (this.appendedMessage) { + this.write(this.appendedMessage); + this.appendedMessage = ''; + } + this.loadingFromFileInProgress = false; + return this.createModel(content); + }); + } + + private resetModel(): Promise { + this.startOffset = 0; + this.endOffset = 0; + if (this.model) { + return this.loadModel().then(() => undefined); + } + return Promise.resolve(undefined); + } + + private loadFile(): Promise { + return this.fileService.resolveContent(this.file, { position: this.startOffset, encoding: 'utf8' }) + .then(content => this.appendedMessage ? content.value + this.appendedMessage : content.value); + } + + protected updateModel(): void { + if (this.model && this.appendedMessage) { + this.appendToModel(this.appendedMessage); + this.appendedMessage = ''; + } + } + + 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()); + } + } + + private write(content: string): void { + this.appender.append(content); + } + + private flush(): void { + this.appender.flush(); + } +} + +export class OutputChannelModelService extends AsbtractOutputChannelModelService implements IOutputChannelModelService { + + _serviceBrand: any; + + constructor( + @IInstantiationService instantiationService: IInstantiationService, + @ILogService private readonly logService: ILogService, + @ITelemetryService private readonly telemetryService: ITelemetryService + ) { + super(instantiationService); + } + + createOutputChannelModel(id: string, modelUri: URI, mimeType: string, file?: URI): IOutputChannelModel { + if (!file) { + try { + return this.instantiationService.createInstance(OutputChannelBackedByFile, id, modelUri, mimeType); + } 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); + /* __GDPR__ + "output.channel.creation.error" : {} + */ + this.telemetryService.publicLog('output.channel.creation.error'); + } + } + return super.createOutputChannelModel(id, modelUri, mimeType, file); + } + +} + +registerSingleton(IOutputChannelModelService, OutputChannelModelService); diff --git a/src/vs/workbench/services/preferences/browser/preferencesService.ts b/src/vs/workbench/services/preferences/browser/preferencesService.ts index e5cd0c64ab7..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,7 +29,6 @@ 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'; @@ -59,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, @@ -89,10 +88,10 @@ export class PreferencesService extends Disposable implements IPreferencesServic 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); } @@ -100,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); @@ -156,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); } @@ -169,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; @@ -190,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); } @@ -198,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; @@ -216,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 ? @@ -226,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); } } @@ -276,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") }); } @@ -287,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(); @@ -301,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); @@ -335,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) { @@ -362,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, @@ -374,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); @@ -382,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!; }); }); }); @@ -464,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 { @@ -494,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); @@ -505,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; } @@ -523,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(() => { }); @@ -557,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 { diff --git a/src/vs/workbench/services/preferences/common/preferences.ts b/src/vs/workbench/services/preferences/common/preferences.ts index 4bf2e5500f5..06b0a00d28e 100644 --- a/src/vs/workbench/services/preferences/common/preferences.ts +++ b/src/vs/workbench/services/preferences/common/preferences.ts @@ -53,7 +53,7 @@ export interface ISetting { value: any; valueRange: IRange; description: string[]; - descriptionIsMarkdown: boolean; + descriptionIsMarkdown?: boolean; descriptionRanges: IRange[]; overrides?: ISetting[]; overrideOf?: ISetting; @@ -65,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 { @@ -97,7 +97,7 @@ export interface IFilterResult { export interface ISettingMatch { setting: ISetting; - matches: IRange[]; + matches: IRange[] | null; score: number; } @@ -136,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; @@ -162,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; @@ -195,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 f61446ca4b9..d267ae05770 100644 --- a/src/vs/workbench/services/progress/browser/progressService2.ts +++ b/src/vs/workbench/services/progress/browser/progressService2.ts @@ -39,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) { @@ -55,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}`)); } } @@ -269,4 +267,4 @@ export class ProgressService2 implements IProgressService2 { } } -registerSingleton(IProgressService2, ProgressService2, true); \ No newline at end of file +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/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index 64f2a50b4dc..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'; @@ -661,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 diff --git a/src/vs/workbench/services/textfile/common/textfiles.ts b/src/vs/workbench/services/textfile/common/textfiles.ts index a51df6695ac..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; } /** @@ -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/textmodelResolver/common/textModelResolverService.ts b/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts index 1c6388a5929..bc4a2084711 100644 --- a/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts +++ b/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts @@ -12,7 +12,7 @@ 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'; @@ -104,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) { @@ -130,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 @@ -149,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()); diff --git a/src/vs/workbench/services/title/common/titleService.ts b/src/vs/workbench/services/title/common/titleService.ts index e651fae3add..1745a6a2449 100644 --- a/src/vs/workbench/services/title/common/titleService.ts +++ b/src/vs/workbench/services/title/common/titleService.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { Event } from 'vs/base/common/event'; export const ITitleService = createDecorator('titleService'); @@ -15,6 +16,11 @@ export interface ITitleProperties { export interface ITitleService { _serviceBrand: any; + /** + * An event when the menubar visibility changes. + */ + readonly onMenubarVisibilityChange: Event; + /** * Update some environmental title properties. */ 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/common/workspaceEditing.ts b/src/vs/workbench/services/workspace/common/workspaceEditing.ts index e65b8e1c29e..c1656dab17f 100644 --- a/src/vs/workbench/services/workspace/common/workspaceEditing.ts +++ b/src/vs/workbench/services/workspace/common/workspaceEditing.ts @@ -51,4 +51,9 @@ export interface IWorkspaceEditingService { * copies current workspace settings to the target workspace. */ copyWorkspaceSettings(toWorkspace: IWorkspaceIdentifier): Promise; + + /** + * picks a new workspace path + */ + pickNewWorkspacePath(): Promise; } \ No newline at end of file diff --git a/src/vs/workbench/services/workspace/node/workspaceEditingService.ts b/src/vs/workbench/services/workspace/node/workspaceEditingService.ts index a1884b928a5..c79cd34347b 100644 --- a/src/vs/workbench/services/workspace/node/workspaceEditingService.ts +++ b/src/vs/workbench/services/workspace/node/workspaceEditingService.ts @@ -9,8 +9,7 @@ import * as nls from 'vs/nls'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/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, isWorkspaceIdentifier, toWorkspaceIdentifier, IWorkspacesService, rewriteWorkspaceFileForNewLocation } from 'vs/platform/workspaces/common/workspaces'; -import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; +import { IWorkspaceIdentifier, IWorkspaceFolderCreationData, IWorkspacesService, rewriteWorkspaceFileForNewLocation, WORKSPACE_FILTER } from 'vs/platform/workspaces/common/workspaces'; 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'; @@ -21,11 +20,15 @@ import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { BackupFileService } from 'vs/workbench/services/backup/node/backupFileService'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { distinct } from 'vs/base/common/arrays'; -import { isLinux } from 'vs/base/common/platform'; -import { isEqual, basename } from 'vs/base/common/resources'; +import { isLinux, isWindows, isMacintosh } from 'vs/base/common/platform'; +import { isEqual, basename, isEqualOrParent } from 'vs/base/common/resources'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { IFileService } from 'vs/platform/files/common/files'; 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 { @@ -36,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, @@ -45,8 +48,94 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { @IFileService private readonly fileSystemService: IFileService, @IWindowsService private readonly windowsService: IWindowsService, @IWorkspacesService private readonly workspaceService: IWorkspacesService, - @IEnvironmentService private readonly environmentService: IEnvironmentService + @IEnvironmentService private readonly environmentService: IEnvironmentService, + @IFileDialogService private readonly fileDialogService: IFileDialogService, + @IDialogService private readonly dialogService: IDialogService, + @ILifecycleService readonly lifecycleService: ILifecycleService ) { + + lifecycleService.onBeforeShutdown(async e => { + const saveOperation = this.saveUntitedBeforeShutdown(e.reason); + if (saveOperation) { + e.veto(saveOperation); + } + }); + + } + + private saveUntitedBeforeShutdown(reason: ShutdownReason): Promise | undefined { + if (reason !== ShutdownReason.LOAD && reason !== ShutdownReason.CLOSE) { + return undefined; // only interested when window is closing or loading + } + const workspaceIdentifier = this.getCurrentWorkspaceIdentifier(); + if (!workspaceIdentifier || !isEqualOrParent(workspaceIdentifier.configPath, this.environmentService.untitledWorkspacesHome)) { + return undefined; // only care about untitled workspaces to ask for saving + } + + return this.windowsService.getWindowCount().then(windowCount => { + if (reason === ShutdownReason.CLOSE && !isMacintosh && windowCount === 1) { + return false; // Windows/Linux: quits when last window is closed, so do not ask then + } + enum ConfirmResult { + SAVE, + DONT_SAVE, + CANCEL + } + + const save = { label: mnemonicButtonLabel(nls.localize('save', "Save")), result: ConfirmResult.SAVE }; + const dontSave = { label: mnemonicButtonLabel(nls.localize('doNotSave', "Don't Save")), result: ConfirmResult.DONT_SAVE }; + const cancel = { label: nls.localize('cancel', "Cancel"), result: ConfirmResult.CANCEL }; + + const buttons: { label: string; result: ConfirmResult; }[] = []; + if (isWindows) { + buttons.push(save, dontSave, cancel); + } else if (isLinux) { + buttons.push(dontSave, cancel, save); + } else { + buttons.push(save, cancel, dontSave); + } + + const message = nls.localize('saveWorkspaceMessage', "Do you want to save your workspace configuration as a file?"); + const detail = nls.localize('saveWorkspaceDetail', "Save your workspace if you plan to open it again."); + const cancelId = buttons.indexOf(cancel); + + return this.dialogService.show(Severity.Warning, message, buttons.map(button => button.label), { detail, cancelId }).then(res => { + switch (buttons[res].result) { + + // Cancel: veto unload + case ConfirmResult.CANCEL: + return true; + + // Don't Save: delete workspace + case ConfirmResult.DONT_SAVE: + this.workspaceService.deleteUntitledWorkspace(workspaceIdentifier); + return false; + + // Save: save workspace, but do not veto unload + case ConfirmResult.SAVE: { + return this.pickNewWorkspacePath().then(newWorkspacePath => { + if (newWorkspacePath) { + return this.saveWorkspaceAs(workspaceIdentifier, newWorkspacePath).then(_ => { + this.windowsService.addRecentlyOpened([newWorkspacePath], [], []); + this.workspaceService.deleteUntitledWorkspace(workspaceIdentifier); + return false; + }, () => false); + } + return true; // keep veto if no target was provided + }); + } + } + }); + }); + } + + pickNewWorkspacePath(): Promise { + return this.fileDialogService.showSaveDialog({ + saveLabel: mnemonicButtonLabel(nls.localize('save', "Save")), + title: nls.localize('saveWorkspace', "Save Workspace"), + filters: WORKSPACE_FILTER, + defaultUri: this.fileDialogService.defaultWorkspacePath() + }); } updateFolders(index: number, deleteCount?: number, foldersToAdd?: IWorkspaceFolderCreationData[], donotNotifyError?: boolean): Promise { @@ -165,11 +254,11 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { if (!this.isValidTargetWorkspacePath(path)) { return Promise.reject(null); } - const currentWorkspaceIdentifier = toWorkspaceIdentifier(this.contextService.getWorkspace()); - if (!isWorkspaceIdentifier(currentWorkspaceIdentifier)) { + const workspaceIdentifier = this.getCurrentWorkspaceIdentifier(); + if (!workspaceIdentifier) { return Promise.reject(null); } - await this.saveWorkspaceAs(currentWorkspaceIdentifier, path); + await this.saveWorkspaceAs(workspaceIdentifier, path); return this.enterWorkspace(path); } @@ -329,6 +418,14 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { return this.jsonEditingService.write(toWorkspace.configPath, { key: 'settings', value: targetWorkspaceConfiguration }, true); } + + private getCurrentWorkspaceIdentifier(): IWorkspaceIdentifier | undefined { + const workspace = this.contextService.getWorkspace(); + if (workspace && workspace.configuration) { + return { id: workspace.id, configPath: workspace.configuration }; + } + 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/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/mainThreadEditors.test.ts b/src/vs/workbench/test/electron-browser/api/mainThreadEditors.test.ts index fb0d2c8e3aa..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)); 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 096e70ce7d2..1da752ea1f9 100644 --- a/src/vs/workbench/test/electron-browser/colorRegistry.releaseTest.ts +++ b/src/vs/workbench/test/electron-browser/colorRegistry.releaseTest.ts @@ -6,7 +6,7 @@ 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'; diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index 6f31697329d..1eb218fa51d 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -548,6 +548,7 @@ export class TestEditorGroupsService implements EditorGroupsServiceImpl { constructor(public groups: TestEditorGroup[] = []) { } onDidActiveGroupChange: Event = Event.None; + onDidActivateGroup: Event = Event.None; onDidAddGroup: Event = Event.None; onDidRemoveGroup: Event = Event.None; onDidMoveGroup: Event = Event.None; @@ -1240,7 +1241,7 @@ export class TestWindowsService implements IWindowsService { return Promise.resolve(); } - addRecentlyOpened(_files: URI[]): Promise { + addRecentlyOpened(_workspaces: URI[], _folders: URI[], _files: URI[]): Promise { return Promise.resolve(); } @@ -1332,7 +1333,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 7a1ce54df5f..af9408a8051 100644 --- a/src/vs/workbench/workbench.main.ts +++ b/src/vs/workbench/workbench.main.ts @@ -69,10 +69,15 @@ 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/output/node/outputChannelModelService'; 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'; +import 'vs/workbench/services/history/browser/history'; +import 'vs/workbench/browser/parts/quickinput/quickInput'; +import 'vs/workbench/browser/parts/quickopen/quickOpenController'; registerSingleton(IMenuService, MenuService, true); registerSingleton(IListService, ListService, true); @@ -88,6 +93,9 @@ registerSingleton(IClipboardService, ClipboardService, true); //#region --- workbench contributions +// Telemetry +import 'vs/workbench/contrib/telemetry/browser/telemetry.contribution'; + // Localizations import 'vs/workbench/contrib/localizations/browser/localizations.contribution'; @@ -121,8 +129,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'; @@ -148,13 +156,14 @@ import 'vs/workbench/contrib/extensions/browser/extensionsQuickOpen'; import 'vs/workbench/contrib/extensions/electron-browser/extensionsViewlet'; // Output Panel -import 'vs/workbench/contrib/output/electron-browser/output.contribution'; +import 'vs/workbench/contrib/output/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'; 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"