diff --git a/.vscode/launch.json b/.vscode/launch.json index 0cde845dc24..ab1b861b763 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -194,8 +194,8 @@ { "type": "node", "request": "launch", - "name": "Unit Tests", - "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", + "name": "Run Unit Tests", + "program": "${workspaceFolder}/test/electron/index.js", "runtimeExecutable": "${workspaceFolder}/.build/electron/Code - OSS.app/Contents/MacOS/Electron", "windows": { "runtimeExecutable": "${workspaceFolder}/.build/electron/Code - OSS.exe" @@ -205,14 +205,9 @@ }, "outputCapture": "std", "args": [ - "--delay", - "--timeout", - "2000" + "--remote-debugging-port=9222" ], "cwd": "${workspaceFolder}", - "env": { - "ELECTRON_RUN_AS_NODE": "true" - }, "outFiles": [ "${workspaceFolder}/out/**/*.js" ] @@ -269,6 +264,13 @@ "Launch VS Code", "Attach to Extension Host" ] - } + }, + { + "name": "Debug Unit Tests", + "configurations": [ + "Attach to VS Code", + "Run Unit Tests" + ] + }, ] } diff --git a/build/azure-pipelines/darwin/continuous-build-darwin.yml b/build/azure-pipelines/darwin/continuous-build-darwin.yml index ffa80168b11..d65ab668f30 100644 --- a/build/azure-pipelines/darwin/continuous-build-darwin.yml +++ b/build/azure-pipelines/darwin/continuous-build-darwin.yml @@ -2,24 +2,25 @@ steps: - task: NodeTool@0 inputs: versionSpec: "10.15.1" +- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 + inputs: + versionSpec: "1.10.1" - task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 inputs: keyfile: '**/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' targetfolder: '**/node_modules, !**/node_modules/**/node_modules' vstsFeed: '$(ArtifactFeed)' -- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 - inputs: - versionSpec: "1.10.1" + condition: eq(variables['System.PullRequest.PullRequestId'], '') - script: | yarn displayName: Install Dependencies - condition: ne(variables['CacheRestored'], 'true') + condition: or(ne(variables['System.PullRequest.PullRequestId'], ''), ne(variables['CacheRestored'], 'true')) - task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 inputs: keyfile: '**/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' targetfolder: '**/node_modules, !**/node_modules/**/node_modules' vstsFeed: '$(ArtifactFeed)' - condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + condition: and(succeeded(), eq(variables['System.PullRequest.PullRequestId'], ''), ne(variables['CacheRestored'], 'true')) - script: | yarn gulp electron-x64 displayName: Download Electron diff --git a/build/azure-pipelines/linux/continuous-build-linux.yml b/build/azure-pipelines/linux/continuous-build-linux.yml index 484334c7b76..e52db94a4ba 100644 --- a/build/azure-pipelines/linux/continuous-build-linux.yml +++ b/build/azure-pipelines/linux/continuous-build-linux.yml @@ -10,24 +10,25 @@ steps: - task: NodeTool@0 inputs: versionSpec: "10.15.1" +- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 + inputs: + versionSpec: "1.10.1" - task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 inputs: keyfile: '**/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' targetfolder: '**/node_modules, !**/node_modules/**/node_modules' vstsFeed: '$(ArtifactFeed)' -- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 - inputs: - versionSpec: "1.10.1" + condition: eq(variables['System.PullRequest.PullRequestId'], '') - script: | yarn displayName: Install Dependencies - condition: ne(variables['CacheRestored'], 'true') + condition: or(ne(variables['System.PullRequest.PullRequestId'], ''), ne(variables['CacheRestored'], 'true')) - task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 inputs: keyfile: '**/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' targetfolder: '**/node_modules, !**/node_modules/**/node_modules' vstsFeed: '$(ArtifactFeed)' - condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + condition: and(succeeded(), eq(variables['System.PullRequest.PullRequestId'], ''), ne(variables['CacheRestored'], 'true')) - script: | yarn gulp electron-x64 displayName: Download Electron diff --git a/build/azure-pipelines/win32/continuous-build-win32.yml b/build/azure-pipelines/win32/continuous-build-win32.yml index 764d7916fec..cb6ef0c4a5a 100644 --- a/build/azure-pipelines/win32/continuous-build-win32.yml +++ b/build/azure-pipelines/win32/continuous-build-win32.yml @@ -14,16 +14,17 @@ steps: keyfile: '**/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' targetfolder: '**/node_modules, !**/node_modules/**/node_modules' vstsFeed: '$(ArtifactFeed)' + condition: eq(variables['System.PullRequest.PullRequestId'], '') - powershell: | yarn displayName: Install Dependencies - condition: ne(variables['CacheRestored'], 'true') + condition: or(ne(variables['System.PullRequest.PullRequestId'], ''), ne(variables['CacheRestored'], 'true')) - task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 inputs: keyfile: '**/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' targetfolder: '**/node_modules, !**/node_modules/**/node_modules' vstsFeed: '$(ArtifactFeed)' - condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + condition: and(succeeded(), eq(variables['System.PullRequest.PullRequestId'], ''), ne(variables['CacheRestored'], 'true')) - powershell: | yarn gulp electron displayName: Download Electron diff --git a/build/gulpfile.vscode.linux.js b/build/gulpfile.vscode.linux.js index fc40616ef09..bff52dedfb5 100644 --- a/build/gulpfile.vscode.linux.js +++ b/build/gulpfile.vscode.linux.js @@ -42,7 +42,7 @@ function prepareDebPackage(arch) { .pipe(replace('@@NAME_LONG@@', product.nameLong)) .pipe(replace('@@NAME_SHORT@@', product.nameShort)) .pipe(replace('@@NAME@@', product.applicationName)) - .pipe(replace('@@ICON@@', product.applicationName)) + .pipe(replace('@@ICON@@', product.linuxIconName)) .pipe(replace('@@URLPROTOCOL@@', product.urlProtocol)); const appdata = gulp.src('resources/linux/code.appdata.xml', { base: '.' }) @@ -52,7 +52,7 @@ function prepareDebPackage(arch) { .pipe(rename('usr/share/appdata/' + product.applicationName + '.appdata.xml')); const icon = gulp.src('resources/linux/code.png', { base: '.' }) - .pipe(rename('usr/share/pixmaps/' + product.applicationName + '.png')); + .pipe(rename('usr/share/pixmaps/' + product.linuxIconName + '.png')); // const bash_completion = gulp.src('resources/completions/bash/code') // .pipe(rename('usr/share/bash-completion/completions/code')); @@ -132,7 +132,7 @@ function prepareRpmPackage(arch) { .pipe(replace('@@NAME_LONG@@', product.nameLong)) .pipe(replace('@@NAME_SHORT@@', product.nameShort)) .pipe(replace('@@NAME@@', product.applicationName)) - .pipe(replace('@@ICON@@', product.applicationName)) + .pipe(replace('@@ICON@@', product.linuxIconName)) .pipe(replace('@@URLPROTOCOL@@', product.urlProtocol)); const appdata = gulp.src('resources/linux/code.appdata.xml', { base: '.' }) @@ -142,7 +142,7 @@ function prepareRpmPackage(arch) { .pipe(rename('usr/share/appdata/' + product.applicationName + '.appdata.xml')); const icon = gulp.src('resources/linux/code.png', { base: '.' }) - .pipe(rename('BUILD/usr/share/pixmaps/' + product.applicationName + '.png')); + .pipe(rename('BUILD/usr/share/pixmaps/' + product.linuxIconName + '.png')); // const bash_completion = gulp.src('resources/completions/bash/code') // .pipe(rename('BUILD/usr/share/bash-completion/completions/code')); @@ -156,6 +156,7 @@ function prepareRpmPackage(arch) { const spec = gulp.src('resources/linux/rpm/code.spec.template', { base: '.' }) .pipe(replace('@@NAME@@', product.applicationName)) .pipe(replace('@@NAME_LONG@@', product.nameLong)) + .pipe(replace('@@ICON@@', product.linuxIconName)) .pipe(replace('@@VERSION@@', packageJson.version)) .pipe(replace('@@RELEASE@@', linuxPackageRevision)) .pipe(replace('@@ARCHITECTURE@@', rpmArch)) @@ -202,11 +203,11 @@ function prepareSnapPackage(arch) { .pipe(replace('@@NAME_LONG@@', product.nameLong)) .pipe(replace('@@NAME_SHORT@@', product.nameShort)) .pipe(replace('@@NAME@@', product.applicationName)) - .pipe(replace('@@ICON@@', `/usr/share/pixmaps/${product.applicationName}.png`)) + .pipe(replace('@@ICON@@', `/usr/share/pixmaps/${product.linuxIconName}.png`)) .pipe(rename(`usr/share/applications/${product.applicationName}.desktop`)); const icon = gulp.src('resources/linux/code.png', { base: '.' }) - .pipe(rename(`usr/share/pixmaps/${product.applicationName}.png`)); + .pipe(rename(`usr/share/pixmaps/${product.linuxIconName}.png`)); const code = gulp.src(binaryDir + '/**/*', { base: binaryDir }) .pipe(rename(function (p) { p.dirname = `usr/share/${product.applicationName}/${p.dirname}`; })); diff --git a/extensions/coffeescript/package.json b/extensions/coffeescript/package.json index 2758bcdc50b..8d9293a8852 100644 --- a/extensions/coffeescript/package.json +++ b/extensions/coffeescript/package.json @@ -11,7 +11,7 @@ "contributes": { "languages": [{ "id": "coffeescript", - "extensions": [ ".coffee", ".cson" ], + "extensions": [ ".coffee", ".cson", ".iced" ], "aliases": [ "CoffeeScript", "coffeescript", "coffee" ], "configuration": "./language-configuration.json" }], diff --git a/extensions/css-language-features/server/package.json b/extensions/css-language-features/server/package.json index 71e14f56ea2..b778ab5ccff 100644 --- a/extensions/css-language-features/server/package.json +++ b/extensions/css-language-features/server/package.json @@ -9,7 +9,7 @@ }, "main": "./out/cssServerMain", "dependencies": { - "vscode-css-languageservice": "^4.0.0-next.3", + "vscode-css-languageservice": "^4.0.0-next.5", "vscode-languageserver": "^5.3.0-next.2" }, "devDependencies": { diff --git a/extensions/css-language-features/server/yarn.lock b/extensions/css-language-features/server/yarn.lock index bfbcab4c931..121917c3ed0 100644 --- a/extensions/css-language-features/server/yarn.lock +++ b/extensions/css-language-features/server/yarn.lock @@ -229,10 +229,10 @@ supports-color@5.4.0: dependencies: has-flag "^3.0.0" -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== +vscode-css-languageservice@^4.0.0-next.5: + version "4.0.0-next.5" + resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-4.0.0-next.5.tgz#a9246d3c85c33e2878b305a93de6a2c7fef53d3b" + integrity sha512-ZO8mfoZjPgY+T77+frQHtlXPxt00ogPJfdOY2T8I3vT+1m32GIUKjcmgwg4sMtx3/+PYwu2SowgygrStny9JvQ== dependencies: vscode-languageserver-types "^3.14.0" vscode-nls "^4.0.0" diff --git a/extensions/debug-server-ready/src/extension.ts b/extensions/debug-server-ready/src/extension.ts index 4aeffd8306b..67c4d02fde5 100644 --- a/extensions/debug-server-ready/src/extension.ts +++ b/extensions/debug-server-ready/src/extension.ts @@ -119,7 +119,7 @@ class ServerReadyDetector extends vscode.Disposable { request: 'launch', url: uri, webRoot: args.webRoot || WEB_ROOT - }); + }, session); break; default: // not supported diff --git a/extensions/javascript/syntaxes/JavaScript.tmLanguage.json b/extensions/javascript/syntaxes/JavaScript.tmLanguage.json index ccc67f2c1cf..d856ea886f8 100644 --- a/extensions/javascript/syntaxes/JavaScript.tmLanguage.json +++ b/extensions/javascript/syntaxes/JavaScript.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/06d49b5ea993412a21aad630a17c6e7e7081c30f", + "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/17761d144b2bd62ffc9ad61eddcd10ec307a7bbc", "name": "JavaScript (with React support)", "scopeName": "source.js", "patterns": [ @@ -1618,9 +1618,12 @@ }, "class-expression": { "name": "meta.class.js", - "begin": "(? { + return text.replace(/\r\n|\r|\n/g, (match, offset) => { extra = match === '\r\n' ? -1 : 0; offset += total; diff --git a/src/vs/base/browser/ui/menu/menubar.ts b/src/vs/base/browser/ui/menu/menubar.ts index cfb2fa8bf42..5e61860c553 100644 --- a/src/vs/base/browser/ui/menu/menubar.ts +++ b/src/vs/base/browser/ui/menu/menubar.ts @@ -16,6 +16,7 @@ import { RunOnceScheduler } from 'vs/base/common/async'; import { Event, Emitter } from 'vs/base/common/event'; import { KeyCode, ResolvedKeybinding } from 'vs/base/common/keyCodes'; import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { withNullAsUndefined } from 'vs/base/common/types'; const $ = DOM.$; @@ -86,7 +87,7 @@ export class MenuBar extends Disposable { constructor(private container: HTMLElement, private options: IMenuBarOptions = {}) { super(); - this.container.attributes['role'] = 'menubar'; + this.container.setAttribute('role', 'menubar'); this.menuCache = []; this.mnemonics = new Map(); @@ -857,7 +858,7 @@ export class MenuBar extends Disposable { getKeyBinding: this.options.getKeybinding, actionRunner: this.actionRunner, enableMnemonics: this.options.alwaysOnMnemonics || (this.mnemonicsInUse && this.options.enableMnemonics), - ariaLabel: customMenu.buttonElement.attributes['aria-label'].value + ariaLabel: withNullAsUndefined(customMenu.buttonElement.getAttribute('aria-label')) }; let menuWidget = this._register(new Menu(menuHolder, customMenu.actions, menuOptions)); diff --git a/src/vs/base/common/resources.ts b/src/vs/base/common/resources.ts index 60a58363f18..8efaddb615c 100644 --- a/src/vs/base/common/resources.ts +++ b/src/vs/base/common/resources.ts @@ -218,14 +218,15 @@ export function relativePath(from: URI, to: URI): string | undefined { * Resolves a absolute or relative path against a base URI. */ export function resolvePath(base: URI, path: string): URI { - let resolvedPath: string; if (base.scheme === Schemas.file) { - resolvedPath = URI.file(paths.resolve(originalFSPath(base), path)).path; - } else { - resolvedPath = paths.posix.resolve(base.path, path); + const newURI = URI.file(paths.resolve(originalFSPath(base), path)); + return base.with({ + authority: newURI.authority, + path: newURI.path + }); } return base.with({ - path: resolvedPath + path: paths.posix.resolve(base.path, path) }); } diff --git a/src/vs/base/node/stats.ts b/src/vs/base/node/stats.ts deleted file mode 100644 index 1c3598a5d30..00000000000 --- a/src/vs/base/node/stats.ts +++ /dev/null @@ -1,200 +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 { readdir, stat, exists, readFile } from 'fs'; -import { join } from 'vs/base/common/path'; -import { parse, ParseError } from 'vs/base/common/json'; - -export interface WorkspaceStatItem { - name: string; - count: number; -} - -export interface WorkspaceStats { - fileTypes: WorkspaceStatItem[]; - configFiles: WorkspaceStatItem[]; - fileCount: number; - maxFilesReached: boolean; - launchConfigFiles: WorkspaceStatItem[]; -} - -function asSortedItems(map: Map): 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 { - const launchConfigs = new Map(); - - const launchConfig = join(folder, '.vscode', 'launch.json'); - return new Promise((resolve, reject) => { - exists(launchConfig, (doesExist) => { - if (doesExist) { - readFile(launchConfig, (err, contents) => { - if (err) { - return resolve([]); - } - - const errors: ParseError[] = []; - const json = parse(contents.toString(), errors); - if (errors.length) { - console.log(`Unable to parse ${launchConfig}`); - return resolve([]); - } - - if (json['configurations']) { - for (const each of json['configurations']) { - const type = each['type']; - if (type) { - if (launchConfigs.has(type)) { - launchConfigs.set(type, launchConfigs.get(type)! + 1); - } else { - launchConfigs.set(type, 1); - } - } - } - } - - return resolve(asSortedItems(launchConfigs)); - }); - } else { - return resolve([]); - } - }); - }); -} - -export async function collectWorkspaceStats(folder: string, filter: string[]): Promise { - const configFilePatterns = [ - { 'tag': 'grunt.js', 'pattern': /^gruntfile\.js$/i }, - { 'tag': 'gulp.js', 'pattern': /^gulpfile\.js$/i }, - { 'tag': 'tsconfig.json', 'pattern': /^tsconfig\.json$/i }, - { 'tag': 'package.json', 'pattern': /^package\.json$/i }, - { 'tag': 'jsconfig.json', 'pattern': /^jsconfig\.json$/i }, - { 'tag': 'tslint.json', 'pattern': /^tslint\.json$/i }, - { 'tag': 'eslint.json', 'pattern': /^eslint\.json$/i }, - { 'tag': 'tasks.json', 'pattern': /^tasks\.json$/i }, - { 'tag': 'launch.json', 'pattern': /^launch\.json$/i }, - { 'tag': 'settings.json', 'pattern': /^settings\.json$/i }, - { 'tag': 'webpack.config.js', 'pattern': /^webpack\.config\.js$/i }, - { 'tag': 'project.json', 'pattern': /^project\.json$/i }, - { 'tag': 'makefile', 'pattern': /^makefile$/i }, - { 'tag': 'sln', 'pattern': /^.+\.sln$/i }, - { 'tag': 'csproj', 'pattern': /^.+\.csproj$/i }, - { 'tag': 'cmake', 'pattern': /^.+\.cmake$/i } - ]; - - const fileTypes = new Map(); - const configFiles = new Map(); - - const MAX_FILES = 20000; - - function walk(dir: string, filter: string[], token, done: (allFiles: string[]) => void): void { - let results: string[] = []; - readdir(dir, async (err, files) => { - // Ignore folders that can't be read - if (err) { - return done(results); - } - - let pending = files.length; - if (pending === 0) { - return done(results); - } - - for (const file of files) { - if (token.maxReached) { - return done(results); - } - - stat(join(dir, file), (err, stats) => { - // Ignore files that can't be read - if (err) { - if (--pending === 0) { - return done(results); - } - } else { - if (stats.isDirectory()) { - if (filter.indexOf(file) === -1) { - walk(join(dir, file), filter, token, (res: string[]) => { - results = results.concat(res); - - if (--pending === 0) { - return done(results); - } - }); - } else { - if (--pending === 0) { - done(results); - } - } - } else { - if (token.count >= MAX_FILES) { - token.maxReached = true; - } - - token.count++; - results.push(file); - - if (--pending === 0) { - done(results); - } - } - } - }); - } - }); - } - - const addFileType = (fileType: string) => { - if (fileTypes.has(fileType)) { - fileTypes.set(fileType, fileTypes.get(fileType)! + 1); - } - else { - fileTypes.set(fileType, 1); - } - }; - - const addConfigFiles = (fileName: string) => { - for (const each of configFilePatterns) { - if (each.pattern.test(fileName)) { - if (configFiles.has(each.tag)) { - configFiles.set(each.tag, configFiles.get(each.tag)! + 1); - } else { - configFiles.set(each.tag, 1); - } - } - } - }; - - const acceptFile = (name: string) => { - if (name.lastIndexOf('.') >= 0) { - const suffix: string | undefined = name.split('.').pop(); - if (suffix) { - addFileType(suffix); - } - } - addConfigFiles(name); - }; - - const token: { count: number, maxReached: boolean } = { count: 0, maxReached: false }; - - return new Promise((resolve, reject) => { - walk(folder, filter, token, async (files) => { - files.forEach(acceptFile); - - const launchConfigs = await collectLaunchConfigs(folder); - - resolve({ - configFiles: asSortedItems(configFiles), - fileTypes: asSortedItems(fileTypes), - fileCount: token.count, - maxFilesReached: token.maxReached, - launchConfigFiles: launchConfigs - }); - }); - }); -} \ No newline at end of file diff --git a/src/vs/base/parts/ipc/common/ipc.ts b/src/vs/base/parts/ipc/common/ipc.ts new file mode 100644 index 00000000000..af712fd86b3 --- /dev/null +++ b/src/vs/base/parts/ipc/common/ipc.ts @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CancellationToken } from 'vs/base/common/cancellation'; +import { Event } from 'vs/base/common/event'; + +/** + * An `IChannel` is an abstraction over a collection of commands. + * You can `call` several commands on a channel, each taking at + * most one single argument. A `call` always returns a promise + * with at most one single return value. + */ +export interface IChannel { + call(command: string, arg?: any, cancellationToken?: CancellationToken): Promise; + listen(event: string, arg?: any): Event; +} + +/** + * An `IServerChannel` is the couter part to `IChannel`, + * on the server-side. You should implement this interface + * if you'd like to handle remote promises or events. + */ +export interface IServerChannel { + call(ctx: TContext, command: string, arg?: any, cancellationToken?: CancellationToken): Promise; + listen(ctx: TContext, event: string, arg?: any): Event; +} \ No newline at end of file diff --git a/src/vs/base/parts/ipc/node/ipc.cp.ts b/src/vs/base/parts/ipc/node/ipc.cp.ts index 3125c3d7156..8009e83c99c 100644 --- a/src/vs/base/parts/ipc/node/ipc.cp.ts +++ b/src/vs/base/parts/ipc/node/ipc.cp.ts @@ -9,10 +9,11 @@ import { Delayer, createCancelablePromise } from 'vs/base/common/async'; import { deepClone, assign } from 'vs/base/common/objects'; import { Emitter, Event } from 'vs/base/common/event'; import { createQueuedSender } from 'vs/base/node/processes'; -import { ChannelServer as IPCServer, ChannelClient as IPCClient, IChannelClient, IChannel } from 'vs/base/parts/ipc/node/ipc'; +import { ChannelServer as IPCServer, ChannelClient as IPCClient, IChannelClient } from 'vs/base/parts/ipc/node/ipc'; import { isRemoteConsoleLog, log } from 'vs/base/node/console'; import { CancellationToken } from 'vs/base/common/cancellation'; import * as errors from 'vs/base/common/errors'; +import { IChannel } from 'vs/base/parts/ipc/common/ipc'; /** * This implementation doesn't perform well since it uses base64 encoding for buffers. diff --git a/src/vs/base/parts/ipc/node/ipc.ts b/src/vs/base/parts/ipc/node/ipc.ts index 07a952cb54d..69b69273979 100644 --- a/src/vs/base/parts/ipc/node/ipc.ts +++ b/src/vs/base/parts/ipc/node/ipc.ts @@ -8,6 +8,7 @@ import { Event, Emitter, Relay } from 'vs/base/common/event'; import { CancelablePromise, createCancelablePromise, timeout } from 'vs/base/common/async'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import * as errors from 'vs/base/common/errors'; +import { IServerChannel, IChannel } from 'vs/base/parts/ipc/common/ipc'; export const enum RequestType { Promise = 100, @@ -51,27 +52,6 @@ enum State { Idle } -/** - * An `IChannel` is an abstraction over a collection of commands. - * You can `call` several commands on a channel, each taking at - * most one single argument. A `call` always returns a promise - * with at most one single return value. - */ -export interface IChannel { - call(command: string, arg?: any, cancellationToken?: CancellationToken): Promise; - listen(event: string, arg?: any): Event; -} - -/** - * An `IServerChannel` is the couter part to `IChannel`, - * on the server-side. You should implement this interface - * if you'd like to handle remote promises or events. - */ -export interface IServerChannel { - call(ctx: TContext, command: string, arg?: any, cancellationToken?: CancellationToken): Promise; - listen(ctx: TContext, event: string, arg?: any): Event; -} - /** * An `IChannelServer` hosts a collection of channels. You are * able to register channels onto it, provided a channel name. diff --git a/src/vs/base/parts/ipc/test/node/ipc.test.ts b/src/vs/base/parts/ipc/test/node/ipc.test.ts index f8cd8a0ee83..c2738ea1ac5 100644 --- a/src/vs/base/parts/ipc/test/node/ipc.test.ts +++ b/src/vs/base/parts/ipc/test/node/ipc.test.ts @@ -4,7 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { IMessagePassingProtocol, IPCServer, ClientConnectionEvent, IPCClient, IChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; +import { IMessagePassingProtocol, IPCServer, ClientConnectionEvent, IPCClient } from 'vs/base/parts/ipc/node/ipc'; import { Emitter, Event } from 'vs/base/common/event'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { canceled } from 'vs/base/common/errors'; diff --git a/src/vs/base/parts/ipc/test/node/testService.ts b/src/vs/base/parts/ipc/test/node/testService.ts index 6c35db5e4ea..2e18d8ae872 100644 --- a/src/vs/base/parts/ipc/test/node/testService.ts +++ b/src/vs/base/parts/ipc/test/node/testService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; import { Event, Emitter } from 'vs/base/common/event'; import { timeout } from 'vs/base/common/async'; diff --git a/src/vs/base/test/common/resources.test.ts b/src/vs/base/test/common/resources.test.ts index fc2428541d6..22d576dd3f1 100644 --- a/src/vs/base/test/common/resources.test.ts +++ b/src/vs/base/test/common/resources.test.ts @@ -296,6 +296,9 @@ suite('Resources', () => { assertResolve(URI.file('\\\\server\\share\\some\\'), 'b1\\file.js', URI.file('\\\\server\\share\\some\\b1\\file.js')); assertResolve(URI.file('\\\\server\\share\\some\\'), '\\file.js', URI.file('\\\\server\\share\\file.js')); + + assertResolve(URI.file('c:\\'), '\\\\server\\share\\some\\', URI.file('\\\\server\\share\\some')); + assertResolve(URI.file('\\\\server\\share\\some\\'), 'c:\\', URI.file('c:\\')); } else { assertResolve(URI.file('/foo/bar'), 'file.js', URI.file('/foo/bar/file.js')); assertResolve(URI.file('/foo/bar'), './file.js', URI.file('/foo/bar/file.js')); diff --git a/src/vs/code/buildfile.js b/src/vs/code/buildfile.js index 1a39a0a0c2c..221bea21443 100644 --- a/src/vs/code/buildfile.js +++ b/src/vs/code/buildfile.js @@ -5,8 +5,9 @@ 'use strict'; function createModuleDescription(name, exclude) { - var result = {}; - var excludes = ['vs/css', 'vs/nls']; + const result = {}; + + let excludes = ['vs/css', 'vs/nls']; result.name = name; if (Array.isArray(exclude) && exclude.length > 0) { excludes = excludes.concat(exclude); diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index ae90617ca38..ffaff18e94c 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -41,13 +41,14 @@ import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IDisposable, dispose, combinedDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { DownloadService } from 'vs/platform/download/node/downloadService'; import { IDownloadService } from 'vs/platform/download/common/download'; -import { StaticRouter, IServerChannel, IChannel } from 'vs/base/parts/ipc/node/ipc'; +import { StaticRouter } from 'vs/base/parts/ipc/node/ipc'; import { NodeCachedDataCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/nodeCachedDataCleaner'; import { LanguagePackCachedDataCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/languagePackCachedDataCleaner'; import { StorageDataCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/storageDataCleaner'; import { LogsDataCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/logsDataCleaner'; import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; +import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; export interface ISharedProcessConfiguration { readonly machineId: string; diff --git a/src/vs/code/electron-browser/workbench/workbench.nodeless.js b/src/vs/code/electron-browser/workbench/workbench.nodeless.js index 9f52c0ab096..4d7098fa3e4 100644 --- a/src/vs/code/electron-browser/workbench/workbench.nodeless.js +++ b/src/vs/code/electron-browser/workbench/workbench.nodeless.js @@ -8,6 +8,32 @@ (function () { + function uriFromPath(_path) { + let pathName = _path.replace(/\\/g, '/'); + if (pathName.length > 0 && pathName.charAt(0) !== '/') { + pathName = '/' + pathName; + } + + let uri; + if (navigator.userAgent.indexOf('Windows') >= 0 && pathName.startsWith('//')) { // specially handle Windows UNC paths + uri = encodeURI('file:' + pathName); + } else { + uri = encodeURI('file://' + pathName); + } + + return uri.replace(/#/g, '%23'); + } + + function parseURLQueryArgs() { + const search = window.location.search || ''; + + return search.split(/[?&]/) + .filter(function (param) { return !!param; }) + .map(function (param) { return param.split('='); }) + .filter(function (param) { return param.length === 2; }) + .reduce(function (r, param) { r[param[0]] = decodeURIComponent(param[1]); return r; }, {}); + } + function loadScript(path, callback) { let script = document.createElement('script'); script.onload = callback; @@ -17,11 +43,14 @@ document.head.appendChild(script); } - loadScript('../../../../../src/vs/loader.js', function () { + loadScript('../../../../../out/vs/loader.js', function () { + + const args = parseURLQueryArgs(); + const configuration = JSON.parse(args['config'] || '{}') || {}; // @ts-ignore require.config({ - baseUrl: 'file:../../../../../out' + baseUrl: uriFromPath(configuration.appRoot) + '/out', }); // @ts-ignore diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index d0b77c48850..c196e40a5a5 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -55,7 +55,7 @@ import { LogLevelSetterChannel } from 'vs/platform/log/node/logIpc'; import * as errors from 'vs/base/common/errors'; import { ElectronURLListener } from 'vs/platform/url/electron-main/electronUrlListener'; import { serve as serveDriver } from 'vs/platform/driver/electron-main/driver'; -import { connectRemoteAgentManagement, RemoteAgentConnectionContext } from 'vs/platform/remote/node/remoteAgentConnection'; +import { connectRemoteAgentManagement } from 'vs/platform/remote/node/remoteAgentConnection'; import { IMenubarService } from 'vs/platform/menubar/common/menubar'; import { MenubarService } from 'vs/platform/menubar/electron-main/menubarService'; import { MenubarChannel } from 'vs/platform/menubar/node/menubarIpc'; @@ -78,6 +78,7 @@ import { IBackupMainService } from 'vs/platform/backup/common/backup'; import { HistoryMainService } from 'vs/platform/history/electron-main/historyMainService'; import { URLService } from 'vs/platform/url/common/urlService'; import { WorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService'; +import { RemoteAgentConnectionContext } from 'vs/platform/remote/common/remoteAgentEnvironment'; export class CodeApplication extends Disposable { diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index ff0bee63634..1d5a69263a7 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -314,7 +314,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { this._win.webContents.session.webRequest.onBeforeSendHeaders({ urls }, (details: any, cb: any) => { this.marketplaceHeadersPromise.then(headers => { const requestHeaders = objects.assign(details.requestHeaders, headers); - if (this.configurationService.getValue('extensions.enableExperimentalAzureSearch')) { + if (!this.configurationService.getValue('extensions.disableExperimentalAzureSearch')) { requestHeaders['Cookie'] = `${requestHeaders['Cookie'] ? requestHeaders['Cookie'] + ';' : ''}EnableExternalSearchForVSCode=true`; } cb({ cancel: false, requestHeaders }); diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index f2e902d4429..a761a8c6e2e 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -691,7 +691,7 @@ export class WindowsManager implements IWindowsMainService { } // Handle empty to restore - const allEmptyToRestore = arrays.distinct(emptyToRestore, info => info.backupFolder || ''); // prevent duplicates + const allEmptyToRestore = arrays.distinct(emptyToRestore, info => info.backupFolder); // prevent duplicates if (allEmptyToRestore.length > 0) { allEmptyToRestore.forEach(emptyWindowBackupInfo => { const remoteAuthority = emptyWindowBackupInfo.remoteAuthority; @@ -1247,7 +1247,7 @@ export class WindowsManager implements IWindowsMainService { // For all other cases we first call into registerEmptyWindowBackupSync() to set it before // loading the window. if (options.emptyWindowBackupInfo) { - configuration.backupPath = join(this.environmentService.backupHome, options.emptyWindowBackupInfo.backupFolder!); + configuration.backupPath = join(this.environmentService.backupHome, options.emptyWindowBackupInfo.backupFolder); } let window: ICodeWindow | undefined; @@ -1352,7 +1352,7 @@ export class WindowsManager implements IWindowsMainService { configuration.backupPath = this.backupMainService.registerFolderBackupSync(configuration.folderUri); } else { const backupFolder = options.emptyWindowBackupInfo && options.emptyWindowBackupInfo.backupFolder; - configuration.backupPath = this.backupMainService.registerEmptyWindowBackupSync({ backupFolder, remoteAuthority: configuration.remoteAuthority }); + configuration.backupPath = this.backupMainService.registerEmptyWindowBackupSync(backupFolder, configuration.remoteAuthority); } } diff --git a/src/vs/editor/contrib/codeAction/codeAction.ts b/src/vs/editor/contrib/codeAction/codeAction.ts index e1fa212b7c2..edcc6e07284 100644 --- a/src/vs/editor/contrib/codeAction/codeAction.ts +++ b/src/vs/editor/contrib/codeAction/codeAction.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { flatten, isNonEmptyArray, mergeSort } from 'vs/base/common/arrays'; -import { CancellationToken } from 'vs/base/common/cancellation'; +import { equals, flatten, isNonEmptyArray, mergeSort } from 'vs/base/common/arrays'; +import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { illegalArgument, isPromiseCanceledError, onUnexpectedExternalError } from 'vs/base/common/errors'; import { URI } from 'vs/base/common/uri'; import { registerLanguageCommand } from 'vs/editor/browser/editorExtensions'; @@ -13,7 +13,7 @@ import { Selection } from 'vs/editor/common/core/selection'; import { ITextModel } from 'vs/editor/common/model'; import { CodeAction, CodeActionContext, CodeActionProviderRegistry, CodeActionTrigger as CodeActionTriggerKind } from 'vs/editor/common/modes'; import { IModelService } from 'vs/editor/common/services/modelService'; -import { CodeActionKind, CodeActionTrigger, filtersAction, mayIncludeActionsOfKind, CodeActionFilter } from './codeActionTrigger'; +import { CodeActionFilter, CodeActionKind, CodeActionTrigger, filtersAction, mayIncludeActionsOfKind } from './codeActionTrigger'; export class CodeActionSet { @@ -55,8 +55,13 @@ export function getCodeActions( trigger: trigger.type === 'manual' ? CodeActionTriggerKind.Manual : CodeActionTriggerKind.Automatic }; - const promises = getCodeActionProviders(model, filter).map(provider => { - return Promise.resolve(provider.provideCodeActions(model, rangeOrSelection, codeActionContext, token)).then(providedCodeActions => { + const chainedCancellation = new CancellationTokenSource(); + token.onCancellationRequested(() => chainedCancellation.cancel()); + + const providers = getCodeActionProviders(model, filter); + + const promises = providers.map(provider => { + return Promise.resolve(provider.provideCodeActions(model, rangeOrSelection, codeActionContext, chainedCancellation.token)).then(providedCodeActions => { if (!Array.isArray(providedCodeActions)) { return []; } @@ -71,9 +76,19 @@ export function getCodeActions( }); }); + const listener = CodeActionProviderRegistry.onDidChange(() => { + const newProviders = CodeActionProviderRegistry.all(model); + if (!equals(newProviders, providers)) { + chainedCancellation.cancel(); + } + }); + return Promise.all(promises) .then(flatten) - .then(actions => new CodeActionSet(actions)); + .then(actions => new CodeActionSet(actions)) + .finally(() => { + listener.dispose(); + }); } function getCodeActionProviders( diff --git a/src/vs/editor/contrib/codeAction/codeActionCommands.ts b/src/vs/editor/contrib/codeAction/codeActionCommands.ts index e73c8cfcb7f..28e7a707b03 100644 --- a/src/vs/editor/contrib/codeAction/codeActionCommands.ts +++ b/src/vs/editor/contrib/codeAction/codeActionCommands.ts @@ -5,7 +5,7 @@ import { CancelablePromise } from 'vs/base/common/async'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { Disposable } from 'vs/base/common/lifecycle'; import { escapeRegExpCharacters } from 'vs/base/common/strings'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorAction, EditorCommand, ServicesAccessor } from 'vs/editor/browser/editorExtensions'; @@ -35,7 +35,7 @@ function contextKeyForSupportedActions(kind: CodeActionKind) { new RegExp('(\\s|^)' + escapeRegExpCharacters(kind.value) + '\\b')); } -export class QuickFixController implements IEditorContribution { +export class QuickFixController extends Disposable implements IEditorContribution { private static readonly ID = 'editor.contrib.quickFixController'; @@ -47,11 +47,11 @@ export class QuickFixController implements IEditorContribution { private readonly _model: CodeActionModel; private readonly _codeActionContextMenu: CodeActionContextMenu; private readonly _lightBulbWidget: LightBulbWidget; - private readonly _disposables: IDisposable[] = []; private _activeRequest: CancelablePromise | undefined; - constructor(editor: ICodeEditor, + constructor( + editor: ICodeEditor, @IMarkerService markerService: IMarkerService, @IContextKeyService contextKeyService: IContextKeyService, @IProgressService progressService: IProgressService, @@ -60,24 +60,24 @@ export class QuickFixController implements IEditorContribution { @IKeybindingService private readonly _keybindingService: IKeybindingService, @IBulkEditService private readonly _bulkEditService: IBulkEditService, ) { + super(); + this._editor = editor; this._model = new CodeActionModel(this._editor, markerService, contextKeyService, progressService); this._codeActionContextMenu = new CodeActionContextMenu(editor, contextMenuService, action => this._onApplyCodeAction(action)); - this._lightBulbWidget = new LightBulbWidget(editor); + this._lightBulbWidget = this._register(new LightBulbWidget(editor)); this._updateLightBulbTitle(); - this._disposables.push( - this._codeActionContextMenu.onDidExecuteCodeAction(_ => this._model.trigger({ type: 'auto', filter: {} })), - this._lightBulbWidget.onClick(this._handleLightBulbSelect, this), - this._model.onDidChangeState(e => this._onDidChangeCodeActionsState(e)), - this._keybindingService.onDidUpdateKeybindings(this._updateLightBulbTitle, this) - ); + this._register(this._codeActionContextMenu.onDidExecuteCodeAction(_ => this._model.trigger({ type: 'auto', filter: {} }))); + this._register(this._lightBulbWidget.onClick(this._handleLightBulbSelect, this)); + this._register(this._model.onDidChangeState(e => this._onDidChangeCodeActionsState(e))); + this._register(this._keybindingService.onDidUpdateKeybindings(this._updateLightBulbTitle, this)); } public dispose(): void { + super.dispose(); this._model.dispose(); - dispose(this._disposables); } private _onDidChangeCodeActionsState(newState: CodeActionsState.State): void { diff --git a/src/vs/platform/backup/common/backup.ts b/src/vs/platform/backup/common/backup.ts index c6255df7c84..9a6cc9bdf0e 100644 --- a/src/vs/platform/backup/common/backup.ts +++ b/src/vs/platform/backup/common/backup.ts @@ -23,7 +23,7 @@ export interface IBackupWorkspacesFormat { export const IBackupMainService = createDecorator('backupMainService'); export interface IEmptyWindowBackupInfo { - backupFolder?: string; + backupFolder: string; remoteAuthority?: string; } @@ -43,7 +43,7 @@ export interface IBackupMainService { registerWorkspaceBackupSync(workspace: IWorkspaceBackupInfo, migrateFrom?: string): string; registerFolderBackupSync(folderUri: URI): string; - registerEmptyWindowBackupSync(backupInfo: IEmptyWindowBackupInfo): string; + registerEmptyWindowBackupSync(backupFolder?: string, remoteAuthority?: string): string; unregisterWorkspaceBackupSync(workspace: IWorkspaceIdentifier): void; unregisterFolderBackupSync(folderUri: URI): void; diff --git a/src/vs/platform/backup/electron-main/backupMainService.ts b/src/vs/platform/backup/electron-main/backupMainService.ts index 838c37727a7..68ed7f004d8 100644 --- a/src/vs/platform/backup/electron-main/backupMainService.ts +++ b/src/vs/platform/backup/electron-main/backupMainService.ts @@ -213,9 +213,7 @@ export class BackupMainService implements IBackupMainService { } } - registerEmptyWindowBackupSync(backupInfo: IEmptyWindowBackupInfo): string { - let backupFolder = backupInfo.backupFolder; - let remoteAuthority = backupInfo.remoteAuthority; + registerEmptyWindowBackupSync(backupFolder?: string, remoteAuthority?: string): string { // Generate a new folder if this is a new empty workspace if (!backupFolder) { diff --git a/src/vs/platform/backup/test/electron-main/backupMainService.test.ts b/src/vs/platform/backup/test/electron-main/backupMainService.test.ts index 4c94518a2fd..2448d5ed6ff 100644 --- a/src/vs/platform/backup/test/electron-main/backupMainService.test.ts +++ b/src/vs/platform/backup/test/electron-main/backupMainService.test.ts @@ -643,8 +643,8 @@ suite('BackupMainService', () => { }); test('should remove empty workspaces from workspaces.json', () => { - service.registerEmptyWindowBackupSync({ backupFolder: 'foo' }); - service.registerEmptyWindowBackupSync({ backupFolder: 'bar' }); + service.registerEmptyWindowBackupSync('foo'); + service.registerEmptyWindowBackupSync('bar'); service.unregisterEmptyWindowBackupSync('foo'); return pfs.readFile(backupWorkspacesPath, 'utf-8').then(buffer => { const json = JSON.parse(buffer); diff --git a/src/vs/platform/diagnostics/electron-main/diagnosticsService.ts b/src/vs/platform/diagnostics/electron-main/diagnosticsService.ts index d17f0869484..80dc3944ef8 100644 --- a/src/vs/platform/diagnostics/electron-main/diagnosticsService.ts +++ b/src/vs/platform/diagnostics/electron-main/diagnosticsService.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { WorkspaceStats, collectWorkspaceStats } from 'vs/base/node/stats'; import { IMainProcessInfo } from 'vs/platform/launch/electron-main/launchService'; import { ProcessItem, listProcesses } from 'vs/base/node/ps'; import product from 'vs/platform/product/node/product'; @@ -13,9 +12,10 @@ import { virtualMachineHint } from 'vs/base/node/id'; import { repeat, pad } from 'vs/base/common/strings'; import { isWindows } from 'vs/base/common/platform'; import { app } from 'electron'; -import { basename } from 'vs/base/common/path'; +import { basename, join } from 'vs/base/common/path'; import { URI } from 'vs/base/common/uri'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { readdir, stat } from 'fs'; export const ID = 'diagnosticsService'; export const IDiagnosticsService = createDecorator(ID); @@ -247,14 +247,14 @@ export class DiagnosticsService implements IDiagnosticsService { output.push(line); } - if (workspaceStats.launchConfigFiles.length > 0) { - let line = '| Launch Configs:'; - workspaceStats.launchConfigFiles.forEach(each => { - const item = each.count > 1 ? ` ${each.name}(${each.count})` : ` ${each.name}`; - line += item; - }); - output.push(line); - } + // if (workspaceStats.launchConfigFiles.length > 0) { + // let line = '| Launch Configs:'; + // workspaceStats.launchConfigFiles.forEach(each => { + // const item = each.count > 1 ? ` ${each.name}(${each.count})` : ` ${each.name}`; + // line += item; + // }); + // output.push(line); + // } return output.join('\n'); } @@ -305,3 +305,197 @@ export class DiagnosticsService implements IDiagnosticsService { } } } + +interface WorkspaceStatItem { + name: string; + count: number; +} + +interface WorkspaceStats { + fileTypes: WorkspaceStatItem[]; + configFiles: WorkspaceStatItem[]; + fileCount: number; + maxFilesReached: boolean; + // launchConfigFiles: WorkspaceStatItem[]; +} + +function asSortedItems(map: Map): WorkspaceStatItem[] { + const a: WorkspaceStatItem[] = []; + map.forEach((value, index) => a.push({ name: index, count: value })); + return a.sort((a, b) => b.count - a.count); +} + +// function collectLaunchConfigs(folder: string): Promise { +// const launchConfigs = new Map(); + +// const launchConfig = join(folder, '.vscode', 'launch.json'); +// return new Promise((resolve, reject) => { +// exists(launchConfig, (doesExist) => { +// if (doesExist) { +// readFile(launchConfig, (err, contents) => { +// if (err) { +// return resolve([]); +// } + +// const errors: ParseError[] = []; +// const json = parse(contents.toString(), errors); +// if (errors.length) { +// console.log(`Unable to parse ${launchConfig}`); +// return resolve([]); +// } + +// if (json['configurations']) { +// for (const each of json['configurations']) { +// const type = each['type']; +// if (type) { +// if (launchConfigs.has(type)) { +// launchConfigs.set(type, launchConfigs.get(type)! + 1); +// } else { +// launchConfigs.set(type, 1); +// } +// } +// } +// } + +// return resolve(asSortedItems(launchConfigs)); +// }); +// } else { +// return resolve([]); +// } +// }); +// }); +// } + +function collectWorkspaceStats(folder: string, filter: string[]): Promise { + const configFilePatterns = [ + { 'tag': 'grunt.js', 'pattern': /^gruntfile\.js$/i }, + { 'tag': 'gulp.js', 'pattern': /^gulpfile\.js$/i }, + { 'tag': 'tsconfig.json', 'pattern': /^tsconfig\.json$/i }, + { 'tag': 'package.json', 'pattern': /^package\.json$/i }, + { 'tag': 'jsconfig.json', 'pattern': /^jsconfig\.json$/i }, + { 'tag': 'tslint.json', 'pattern': /^tslint\.json$/i }, + { 'tag': 'eslint.json', 'pattern': /^eslint\.json$/i }, + { 'tag': 'tasks.json', 'pattern': /^tasks\.json$/i }, + { 'tag': 'launch.json', 'pattern': /^launch\.json$/i }, + { 'tag': 'settings.json', 'pattern': /^settings\.json$/i }, + { 'tag': 'webpack.config.js', 'pattern': /^webpack\.config\.js$/i }, + { 'tag': 'project.json', 'pattern': /^project\.json$/i }, + { 'tag': 'makefile', 'pattern': /^makefile$/i }, + { 'tag': 'sln', 'pattern': /^.+\.sln$/i }, + { 'tag': 'csproj', 'pattern': /^.+\.csproj$/i }, + { 'tag': 'cmake', 'pattern': /^.+\.cmake$/i } + ]; + + const fileTypes = new Map(); + const configFiles = new Map(); + + const MAX_FILES = 20000; + + function walk(dir: string, filter: string[], token, done: (allFiles: string[]) => void): void { + let results: string[] = []; + readdir(dir, async (err, files) => { + // Ignore folders that can't be read + if (err) { + return done(results); + } + + let pending = files.length; + if (pending === 0) { + return done(results); + } + + for (const file of files) { + if (token.maxReached) { + return done(results); + } + + stat(join(dir, file), (err, stats) => { + // Ignore files that can't be read + if (err) { + if (--pending === 0) { + return done(results); + } + } else { + if (stats.isDirectory()) { + if (filter.indexOf(file) === -1) { + walk(join(dir, file), filter, token, (res: string[]) => { + results = results.concat(res); + + if (--pending === 0) { + return done(results); + } + }); + } else { + if (--pending === 0) { + done(results); + } + } + } else { + if (token.count >= MAX_FILES) { + token.maxReached = true; + } + + token.count++; + results.push(file); + + if (--pending === 0) { + done(results); + } + } + } + }); + } + }); + } + + const addFileType = (fileType: string) => { + if (fileTypes.has(fileType)) { + fileTypes.set(fileType, fileTypes.get(fileType)! + 1); + } + else { + fileTypes.set(fileType, 1); + } + }; + + const addConfigFiles = (fileName: string) => { + for (const each of configFilePatterns) { + if (each.pattern.test(fileName)) { + if (configFiles.has(each.tag)) { + configFiles.set(each.tag, configFiles.get(each.tag)! + 1); + } else { + configFiles.set(each.tag, 1); + } + } + } + }; + + const acceptFile = (name: string) => { + if (name.lastIndexOf('.') >= 0) { + const suffix: string | undefined = name.split('.').pop(); + if (suffix) { + addFileType(suffix); + } + } + addConfigFiles(name); + }; + + const token: { count: number, maxReached: boolean } = { count: 0, maxReached: false }; + + return new Promise((resolve, reject) => { + walk(folder, filter, token, async (files) => { + files.forEach(acceptFile); + + // TODO@rachel commented out due to severe performance issues + // see https://github.com/Microsoft/vscode/issues/70563 + // const launchConfigs = await collectLaunchConfigs(folder); + + resolve({ + configFiles: asSortedItems(configFiles), + fileTypes: asSortedItems(fileTypes), + fileCount: token.count, + maxFilesReached: token.maxReached, + // launchConfigFiles: launchConfigs + }); + }); + }); +} \ No newline at end of file diff --git a/src/vs/platform/dialogs/node/dialogIpc.ts b/src/vs/platform/dialogs/node/dialogIpc.ts index 43d06766023..2408c26db36 100644 --- a/src/vs/platform/dialogs/node/dialogIpc.ts +++ b/src/vs/platform/dialogs/node/dialogIpc.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; import { IDialogService, IConfirmation, IConfirmationResult } from 'vs/platform/dialogs/common/dialogs'; import Severity from 'vs/base/common/severity'; import { Event } from 'vs/base/common/event'; diff --git a/src/vs/platform/download/node/downloadIpc.ts b/src/vs/platform/download/node/downloadIpc.ts index 26b5d3812a6..b00396c97ef 100644 --- a/src/vs/platform/download/node/downloadIpc.ts +++ b/src/vs/platform/download/node/downloadIpc.ts @@ -6,7 +6,7 @@ import { URI } from 'vs/base/common/uri'; import * as path from 'vs/base/common/path'; import * as fs from 'fs'; -import { IChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; import { Event, Emitter } from 'vs/base/common/event'; import { IDownloadService } from 'vs/platform/download/common/download'; import { mkdirp } from 'vs/base/node/pfs'; diff --git a/src/vs/platform/driver/node/driver.ts b/src/vs/platform/driver/node/driver.ts index e861984ca42..fc0bb819720 100644 --- a/src/vs/platform/driver/node/driver.ts +++ b/src/vs/platform/driver/node/driver.ts @@ -5,7 +5,7 @@ import { connect as connectNet, Client } from 'vs/base/parts/ipc/node/ipc.net'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; import { Event } from 'vs/base/common/event'; export const ID = 'driverService'; diff --git a/src/vs/platform/extensionManagement/node/extensionManagementIpc.ts b/src/vs/platform/extensionManagement/node/extensionManagementIpc.ts index e7fafc49e92..8d4232a2bbc 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementIpc.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementIpc.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; import { IExtensionManagementService, ILocalExtension, InstallExtensionEvent, DidInstallExtensionEvent, IGalleryExtension, DidUninstallExtensionEvent, IExtensionIdentifier, IGalleryMetadata, IReportedExtension } from '../common/extensionManagement'; import { Event } from 'vs/base/common/event'; import { URI, UriComponents } from 'vs/base/common/uri'; diff --git a/src/vs/platform/extensions/common/extensions.ts b/src/vs/platform/extensions/common/extensions.ts index ee0a9aed604..3d2eb91ca81 100644 --- a/src/vs/platform/extensions/common/extensions.ts +++ b/src/vs/platform/extensions/common/extensions.ts @@ -228,6 +228,15 @@ export class ExtensionIdentifier { } } +export interface IExtensionDescription extends IExtensionManifest { + readonly identifier: ExtensionIdentifier; + readonly uuid?: string; + readonly isBuiltin: boolean; + readonly isUnderDevelopment: boolean; + readonly extensionLocation: URI; + enableProposedApi?: boolean; +} + export function isLanguagePackExtension(manifest: IExtensionManifest): boolean { return manifest.contributes && manifest.contributes.localizations ? manifest.contributes.localizations.length > 0 : false; } \ No newline at end of file diff --git a/src/vs/platform/files/node/files.ts b/src/vs/platform/files/node/fileConstants.ts similarity index 100% rename from src/vs/platform/files/node/files.ts rename to src/vs/platform/files/node/fileConstants.ts diff --git a/src/vs/platform/history/electron-main/historyMainService.ts b/src/vs/platform/history/electron-main/historyMainService.ts index a42a8ca4ee2..47fab1e6c8e 100644 --- a/src/vs/platform/history/electron-main/historyMainService.ts +++ b/src/vs/platform/history/electron-main/historyMainService.ts @@ -48,38 +48,44 @@ export class HistoryMainService implements IHistoryMainService { } addRecentlyOpened(newlyAdded: IRecent[]): void { - const mru = this.getRecentlyOpened(); + const workspaces: Array = []; + const files: IRecentFile[] = []; for (let curr of newlyAdded) { if (isRecentWorkspace(curr)) { - if (!this.workspacesMainService.isUntitledWorkspace(curr.workspace) && indexOfWorkspace(mru.workspaces, curr.workspace) === -1) { - mru.workspaces.unshift(curr); + if (!this.workspacesMainService.isUntitledWorkspace(curr.workspace) && indexOfWorkspace(workspaces, curr.workspace) === -1) { + workspaces.push(curr); } } else if (isRecentFolder(curr)) { - if (indexOfFolder(mru.workspaces, curr.folderUri) === -1) { - mru.workspaces.unshift(curr); + if (indexOfFolder(workspaces, curr.folderUri) === -1) { + workspaces.push(curr); } } else { - if (indexOfFile(mru.files, curr.fileUri) === -1) { - mru.files.unshift(curr); + if (indexOfFile(files, curr.fileUri) === -1) { + files.push(curr); // Add to recent documents (Windows only, macOS later) if (isWindows && curr.fileUri.scheme === Schemas.file) { app.addRecentDocument(curr.fileUri.fsPath); } } } + } - // Make sure its bounded - mru.workspaces = mru.workspaces.slice(0, HistoryMainService.MAX_TOTAL_RECENT_ENTRIES); - mru.files = mru.files.slice(0, HistoryMainService.MAX_TOTAL_RECENT_ENTRIES); + this.addEntriesFromStorage(workspaces, files); - this.saveRecentlyOpened(mru); - this._onRecentlyOpenedChange.fire(); + if (workspaces.length > HistoryMainService.MAX_TOTAL_RECENT_ENTRIES) { + workspaces.length = HistoryMainService.MAX_TOTAL_RECENT_ENTRIES; + } + if (files.length > HistoryMainService.MAX_TOTAL_RECENT_ENTRIES) { + files.length = HistoryMainService.MAX_TOTAL_RECENT_ENTRIES; + } - // Schedule update to recent documents on macOS dock - if (isMacintosh) { - this.macOSRecentDocumentsUpdater.trigger(() => this.updateMacOSRecentDocuments()); - } + this.saveRecentlyOpened({ workspaces, files }); + this._onRecentlyOpenedChange.fire(); + + // Schedule update to recent documents on macOS dock + if (isMacintosh) { + this.macOSRecentDocumentsUpdater.trigger(() => this.updateMacOSRecentDocuments()); } } @@ -177,7 +183,12 @@ export class HistoryMainService implements IHistoryMainService { } } } + this.addEntriesFromStorage(workspaces, files); + return { workspaces, files }; + } + + private addEntriesFromStorage(workspaces: Array, files: IRecentFile[]) { // Get from storage let recents = this.getRecentlyOpenedFromStorage(); for (let recent of recents.workspaces) { @@ -196,7 +207,6 @@ export class HistoryMainService implements IHistoryMainService { files.push(recent); } } - return { workspaces, files }; } private getRecentlyOpenedFromStorage(): IRecentlyOpened { diff --git a/src/vs/platform/ipc/electron-browser/mainProcessService.ts b/src/vs/platform/ipc/electron-browser/mainProcessService.ts index a60095a445e..31ac99261c6 100644 --- a/src/vs/platform/ipc/electron-browser/mainProcessService.ts +++ b/src/vs/platform/ipc/electron-browser/mainProcessService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; -import { IChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; import { Client } from 'vs/base/parts/ipc/electron-browser/ipc.electron-browser'; import { Disposable } from 'vs/base/common/lifecycle'; diff --git a/src/vs/platform/ipc/electron-browser/sharedProcessService.ts b/src/vs/platform/ipc/electron-browser/sharedProcessService.ts index 90100879bee..0b4848204e9 100644 --- a/src/vs/platform/ipc/electron-browser/sharedProcessService.ts +++ b/src/vs/platform/ipc/electron-browser/sharedProcessService.ts @@ -7,7 +7,8 @@ import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/co import { Client, connect } from 'vs/base/parts/ipc/node/ipc.net'; import { IWindowsService, IWindowService } from 'vs/platform/windows/common/windows'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { IChannel, getDelayedChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc'; +import { getDelayedChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; export const ISharedProcessService = createDecorator('sharedProcessService'); diff --git a/src/vs/platform/issue/electron-browser/issueService.ts b/src/vs/platform/issue/electron-browser/issueService.ts index 705c5ce41be..67ba09b24ad 100644 --- a/src/vs/platform/issue/electron-browser/issueService.ts +++ b/src/vs/platform/issue/electron-browser/issueService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { IIssueService, IssueReporterData, ProcessExplorerData } from 'vs/platform/issue/common/issue'; import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; diff --git a/src/vs/platform/issue/node/issueIpc.ts b/src/vs/platform/issue/node/issueIpc.ts index 2183eac8e61..e507a77f9b2 100644 --- a/src/vs/platform/issue/node/issueIpc.ts +++ b/src/vs/platform/issue/node/issueIpc.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IServerChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IServerChannel } from 'vs/base/parts/ipc/common/ipc'; import { Event } from 'vs/base/common/event'; import { IIssueService } from 'vs/platform/issue/common/issue'; diff --git a/src/vs/platform/launch/electron-main/launchService.ts b/src/vs/platform/launch/electron-main/launchService.ts index 1815d52389c..9550cf220ea 100644 --- a/src/vs/platform/launch/electron-main/launchService.ts +++ b/src/vs/platform/launch/electron-main/launchService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; import { ILogService } from 'vs/platform/log/common/log'; import { IURLService } from 'vs/platform/url/common/url'; import { IProcessEnvironment, isMacintosh } from 'vs/base/common/platform'; diff --git a/src/vs/platform/localizations/electron-browser/localizationsService.ts b/src/vs/platform/localizations/electron-browser/localizationsService.ts index 13d70a8872f..353161166e8 100644 --- a/src/vs/platform/localizations/electron-browser/localizationsService.ts +++ b/src/vs/platform/localizations/electron-browser/localizationsService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { Event } from 'vs/base/common/event'; import { ILocalizationsService, LanguageType } from 'vs/platform/localizations/common/localizations'; import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; diff --git a/src/vs/platform/localizations/node/localizationsIpc.ts b/src/vs/platform/localizations/node/localizationsIpc.ts index 02c36add60e..862d3796c9e 100644 --- a/src/vs/platform/localizations/node/localizationsIpc.ts +++ b/src/vs/platform/localizations/node/localizationsIpc.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IServerChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IServerChannel } from 'vs/base/parts/ipc/common/ipc'; import { Event } from 'vs/base/common/event'; import { ILocalizationsService } from 'vs/platform/localizations/common/localizations'; diff --git a/src/vs/platform/log/node/logIpc.ts b/src/vs/platform/log/node/logIpc.ts index 1ed2949e865..a983c0f63f4 100644 --- a/src/vs/platform/log/node/logIpc.ts +++ b/src/vs/platform/log/node/logIpc.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; import { LogLevel, ILogService, DelegatedLogService } from 'vs/platform/log/common/log'; import { Event } from 'vs/base/common/event'; diff --git a/src/vs/platform/menubar/electron-browser/menubarService.ts b/src/vs/platform/menubar/electron-browser/menubarService.ts index 55ad9a46142..7a288b3d348 100644 --- a/src/vs/platform/menubar/electron-browser/menubarService.ts +++ b/src/vs/platform/menubar/electron-browser/menubarService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { IMenubarService, IMenubarData } from 'vs/platform/menubar/common/menubar'; import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; diff --git a/src/vs/platform/menubar/node/menubarIpc.ts b/src/vs/platform/menubar/node/menubarIpc.ts index 3b92dada057..2533b4a1d09 100644 --- a/src/vs/platform/menubar/node/menubarIpc.ts +++ b/src/vs/platform/menubar/node/menubarIpc.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IServerChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IServerChannel } from 'vs/base/parts/ipc/common/ipc'; import { IMenubarService } from 'vs/platform/menubar/common/menubar'; import { Event } from 'vs/base/common/event'; diff --git a/src/vs/platform/remote/common/remoteAgentEnvironment.ts b/src/vs/platform/remote/common/remoteAgentEnvironment.ts new file mode 100644 index 00000000000..84ed13ed78e --- /dev/null +++ b/src/vs/platform/remote/common/remoteAgentEnvironment.ts @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { URI } from 'vs/base/common/uri'; +import { OperatingSystem } from 'vs/base/common/platform'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; + +export interface IRemoteAgentEnvironment { + pid: number; + appRoot: URI; + appSettingsHome: URI; + logsPath: URI; + extensionsPath: URI; + extensionHostLogsPath: URI; + globalStorageHome: URI; + userHome: URI; + extensions: IExtensionDescription[]; + os: OperatingSystem; + syncExtensions: boolean; +} + +export interface RemoteAgentConnectionContext { + remoteAuthority: string; + clientId: string; +} \ No newline at end of file diff --git a/src/vs/platform/remote/node/remoteAgentConnection.ts b/src/vs/platform/remote/node/remoteAgentConnection.ts index bfbe7b3f58d..22cfa997da7 100644 --- a/src/vs/platform/remote/node/remoteAgentConnection.ts +++ b/src/vs/platform/remote/node/remoteAgentConnection.ts @@ -4,11 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Client, BufferedProtocol } from 'vs/base/parts/ipc/node/ipc.net'; - -export interface RemoteAgentConnectionContext { - remoteAuthority: string; - clientId: string; -} +import { RemoteAgentConnectionContext } from 'vs/platform/remote/common/remoteAgentEnvironment'; export function connectRemoteAgentManagement(remoteAuthority: string, host: string, port: number, clientId: string, isBuilt: boolean): Promise> { throw new Error(`Not implemented`); diff --git a/src/vs/platform/remote/node/remoteAgentFileSystemChannel.ts b/src/vs/platform/remote/node/remoteAgentFileSystemChannel.ts index 68726eabc2c..4bec2ec3c79 100644 --- a/src/vs/platform/remote/node/remoteAgentFileSystemChannel.ts +++ b/src/vs/platform/remote/node/remoteAgentFileSystemChannel.ts @@ -7,7 +7,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { URI, UriComponents } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; -import { IChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { FileChangeType, FileDeleteOptions, FileOverwriteOptions, FileSystemProviderCapabilities, FileType, FileWriteOptions, IFileChange, IFileSystemProvider, IStat, IWatchOptions } from 'vs/platform/files/common/files'; export const REMOTE_FILE_SYSTEM_CHANNEL_NAME = 'remotefilesystem'; diff --git a/src/vs/platform/request/node/request.ts b/src/vs/platform/request/node/request.ts index c8145bca837..0dc5cdc6b4a 100644 --- a/src/vs/platform/request/node/request.ts +++ b/src/vs/platform/request/node/request.ts @@ -10,7 +10,7 @@ import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/co import { Registry } from 'vs/platform/registry/common/platform'; import { CancellationToken } from 'vs/base/common/cancellation'; -export const IRequestService = createDecorator('requestService2'); +export const IRequestService = createDecorator('requestService'); export interface IRequestService { _serviceBrand: any; diff --git a/src/vs/platform/storage/node/storageIpc.ts b/src/vs/platform/storage/node/storageIpc.ts index 01e9448c985..20adcf34de1 100644 --- a/src/vs/platform/storage/node/storageIpc.ts +++ b/src/vs/platform/storage/node/storageIpc.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; import { Event, Emitter } from 'vs/base/common/event'; import { StorageMainService, IStorageChangeEvent } from 'vs/platform/storage/node/storageMainService'; import { IUpdateRequest, IStorageDatabase, IStorageItemsChangeEvent } from 'vs/base/node/storage'; diff --git a/src/vs/platform/telemetry/node/telemetryIpc.ts b/src/vs/platform/telemetry/node/telemetryIpc.ts index c0a1e657ea7..fc34d26c297 100644 --- a/src/vs/platform/telemetry/node/telemetryIpc.ts +++ b/src/vs/platform/telemetry/node/telemetryIpc.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; import { ITelemetryAppender } from 'vs/platform/telemetry/common/telemetryUtils'; import { Event } from 'vs/base/common/event'; diff --git a/src/vs/platform/update/electron-browser/updateService.ts b/src/vs/platform/update/electron-browser/updateService.ts index 57ac62f05ca..023ea3ea7da 100644 --- a/src/vs/platform/update/electron-browser/updateService.ts +++ b/src/vs/platform/update/electron-browser/updateService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { Event, Emitter } from 'vs/base/common/event'; import { IUpdateService, State } from 'vs/platform/update/common/update'; import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; diff --git a/src/vs/platform/update/node/updateIpc.ts b/src/vs/platform/update/node/updateIpc.ts index df6760eda45..0ef0dae6952 100644 --- a/src/vs/platform/update/node/updateIpc.ts +++ b/src/vs/platform/update/node/updateIpc.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IServerChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IServerChannel } from 'vs/base/parts/ipc/common/ipc'; import { Event } from 'vs/base/common/event'; import { IUpdateService } from 'vs/platform/update/common/update'; diff --git a/src/vs/platform/url/node/urlIpc.ts b/src/vs/platform/url/node/urlIpc.ts index 9116fb66444..e83b176b8c1 100644 --- a/src/vs/platform/url/node/urlIpc.ts +++ b/src/vs/platform/url/node/urlIpc.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; import { URI } from 'vs/base/common/uri'; import { IDisposable } from 'vs/base/common/lifecycle'; import { Event } from 'vs/base/common/event'; diff --git a/src/vs/platform/windows/electron-browser/windowsService.ts b/src/vs/platform/windows/electron-browser/windowsService.ts index 531d7ce55a0..e455a54ce88 100644 --- a/src/vs/platform/windows/electron-browser/windowsService.ts +++ b/src/vs/platform/windows/electron-browser/windowsService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Event } from 'vs/base/common/event'; -import { IChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { IWindowsService, INativeOpenDialogOptions, IEnterWorkspaceResult, CrashReporterStartOptions, IMessageBoxResult, MessageBoxOptions, SaveDialogOptions, OpenDialogOptions, IDevToolsOptions, INewWindowOptions, IURIToOpen } from 'vs/platform/windows/common/windows'; import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, reviveWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { IRecentlyOpened, IRecent, isRecentWorkspace } from 'vs/platform/history/common/history'; diff --git a/src/vs/platform/windows/node/windowsIpc.ts b/src/vs/platform/windows/node/windowsIpc.ts index 287e5c6aa78..a9a928745fa 100644 --- a/src/vs/platform/windows/node/windowsIpc.ts +++ b/src/vs/platform/windows/node/windowsIpc.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Event } from 'vs/base/common/event'; -import { IServerChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IServerChannel } from 'vs/base/parts/ipc/common/ipc'; import { IWindowsService, IURIToOpen } from 'vs/platform/windows/common/windows'; import { reviveWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { URI } from 'vs/base/common/uri'; diff --git a/src/vs/platform/workspaces/common/workspaces.ts b/src/vs/platform/workspaces/common/workspaces.ts index 57961a4c5c5..0e6a7deba6f 100644 --- a/src/vs/platform/workspaces/common/workspaces.ts +++ b/src/vs/platform/workspaces/common/workspaces.ts @@ -173,7 +173,7 @@ const SLASH = '/'; */ export function getStoredWorkspaceFolder(folderURI: URI, folderName: string | undefined, targetConfigFolderURI: URI, useSlashForPath = !isWindows): IStoredWorkspaceFolder { - if (folderURI.scheme !== targetConfigFolderURI.scheme || !isEqualAuthority(folderURI.authority, targetConfigFolderURI.authority)) { + if (folderURI.scheme !== targetConfigFolderURI.scheme) { return { name: folderName, uri: folderURI.toString(true) }; } @@ -200,6 +200,9 @@ export function getStoredWorkspaceFolder(folderURI: URI, folderName: string | un } } } else { + if (!isEqualAuthority(folderURI.authority, targetConfigFolderURI.authority)) { + return { name: folderName, uri: folderURI.toString(true) }; + } folderPath = folderURI.path; } } diff --git a/src/vs/platform/workspaces/electron-browser/workspacesService.ts b/src/vs/platform/workspaces/electron-browser/workspacesService.ts index d9cbca83c41..dc32978d8c2 100644 --- a/src/vs/platform/workspaces/electron-browser/workspacesService.ts +++ b/src/vs/platform/workspaces/electron-browser/workspacesService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { IWorkspacesService, IWorkspaceIdentifier, IWorkspaceFolderCreationData, reviveWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; diff --git a/src/vs/platform/workspaces/node/workspacesIpc.ts b/src/vs/platform/workspaces/node/workspacesIpc.ts index efc6351bf93..6a9f40a13cb 100644 --- a/src/vs/platform/workspaces/node/workspacesIpc.ts +++ b/src/vs/platform/workspaces/node/workspacesIpc.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IServerChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IServerChannel } from 'vs/base/parts/ipc/common/ipc'; import { IWorkspaceIdentifier, IWorkspaceFolderCreationData, IWorkspacesMainService } from 'vs/platform/workspaces/common/workspaces'; import { URI } from 'vs/base/common/uri'; import { Event } from 'vs/base/common/event'; 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 a74f525c7f3..5e9d7d5de43 100644 --- a/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts +++ b/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts @@ -316,6 +316,31 @@ suite('WorkspacesMainService', () => { }); }); + test('rewriteWorkspaceFileForNewLocation (unc paths)', () => { + if (!isWindows) { + return Promise.resolve(); + } + + const workspaceLocation = path.join(os.tmpdir(), 'wsloc'); + const folder1Location = 'x:\\foo'; + const folder2Location = '\\\\server\\share2\\some\\path'; + const folder3Location = path.join(os.tmpdir(), 'wsloc', 'inner', 'more'); + + return createWorkspace([folder1Location, folder2Location, folder3Location]).then(workspace => { + const workspaceConfigPath = URI.file(path.join(workspaceLocation, `myworkspace.${Date.now()}.${WORKSPACE_EXTENSION}`)); + let origContent = fs.readFileSync(workspace.configPath.fsPath).toString(); + + const newContent = rewriteWorkspaceFileForNewLocation(origContent, workspace.configPath, workspaceConfigPath); + + const ws = JSON.parse(newContent) as IStoredWorkspace; + assertPathEquals((ws.folders[0]).path, folder1Location); + assertPathEquals((ws.folders[1]).path, folder2Location); + assertPathEquals((ws.folders[2]).path, 'inner\\more'); + + service.deleteUntitledWorkspaceSync(workspace); + }); + }); + test('deleteUntitledWorkspaceSync (untitled)', () => { return createWorkspace([process.cwd(), os.tmpdir()]).then(workspace => { assert.ok(fs.existsSync(workspace.configPath.fsPath)); diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 2a845ca6e68..b637c8147dc 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -635,6 +635,22 @@ declare module 'vscode' { //#region André: debug + export namespace debug { + + /** + * Start debugging by using either a named launch or named compound configuration, + * or by directly passing a [DebugConfiguration](#DebugConfiguration). + * The named configurations are looked up in '.vscode/launch.json' found in the given folder. + * Before debugging starts, all unsaved files are saved and the launch configurations are brought up-to-date. + * Folder specific variables used in the configuration (e.g. '${workspaceFolder}') are resolved against the given folder. + * @param folder The [workspace folder](#WorkspaceFolder) for looking up named configurations and resolving variables or `undefined` for a non-folder setup. + * @param nameOrConfiguration Either the name of a debug or compound configuration or a [DebugConfiguration](#DebugConfiguration) object. + * @param parent If specified the newly created debug session is registered as a "child" session of a "parent" debug session. + * @return A thenable that resolves when debugging could be successfully started. + */ + export function startDebugging(folder: WorkspaceFolder | undefined, nameOrConfiguration: string | DebugConfiguration, parentSession?: DebugSession): Thenable; + } + // deprecated export interface DebugConfigurationProvider { @@ -842,6 +858,11 @@ declare module 'vscode' { * Defaults to Collapsed. */ collapsibleState?: CommentThreadCollapsibleState; + + /** + * Dispose this comment thread. + * Once disposed, the comment thread will be removed from visible text editors and Comments Panel. + */ dispose?(): void; } @@ -860,7 +881,8 @@ declare module 'vscode' { body: MarkdownString; /** - * Label describing the [Comment](#Comment) + * Optional label describing the [Comment](#Comment) + * Label will be rendered next to userName if exists. */ label?: string; @@ -874,7 +896,6 @@ declare module 'vscode' { */ userIconPath?: Uri; - /** * @deprecated Use userIconPath instead. The avatar src of the user who created the comment */ @@ -911,13 +932,30 @@ declare module 'vscode' { */ selectCommand?: Command; + /** + * The command to be executed when users try to save the edits to the comment + */ editCommand?: Command; + + /** + * The command to be executed when users try to delete the comment + */ deleteCommand?: Command; + /** + * Deprecated + */ isDraft?: boolean; + + /** + * Proposed Comment Reaction + */ commentReactions?: CommentReaction[]; } + /** + * Deprecated + */ export interface CommentThreadChangedEvent { /** * Added comment threads. @@ -940,6 +978,9 @@ declare module 'vscode' { readonly inDraftMode: boolean; } + /** + * Comment Reactions + */ interface CommentReaction { readonly label?: string; readonly iconPath?: string | Uri; @@ -1010,23 +1051,28 @@ declare module 'vscode' { onDidChangeCommentThreads: Event; } + /** + * The comment input box in Comment Widget. + */ export interface CommentInputBox { - /** - * Setter and getter for the contents of the input box. + * Setter and getter for the contents of the comment input box. */ value: string; } - export interface CommentingRangeProvider { - provideCommentingRanges(document: TextDocument, token: CancellationToken): ProviderResult; - } - export interface CommentReactionProvider { availableReactions: CommentReaction[]; toggleReaction?(document: TextDocument, comment: Comment, reaction: CommentReaction): Promise; } + export interface CommentingRangeProvider { + /** + * Provide a list of ranges which allow new comment threads creation or null for a given document + */ + provideCommentingRanges(document: TextDocument, token: CancellationToken): ProviderResult; + } + export interface EmptyCommentThreadFactory { /** * The method `createEmptyCommentThread` is called when users attempt to create new comment thread from the gutter or command palette. @@ -1050,7 +1096,11 @@ declare module 'vscode' { * The active (focused) [comment input box](#CommentInputBox). */ readonly inputBox?: CommentInputBox; - createCommentThread(id: string, resource: Uri, range: Range): CommentThread; + + /** + * Create a [CommentThread](#CommentThread) + */ + createCommentThread(id: string, resource: Uri, range: Range, comments: Comment[]): CommentThread; /** * Optional commenting range provider. @@ -1061,7 +1111,7 @@ declare module 'vscode' { /** * Optional new comment thread factory. */ - emptyCommentThreadFactory: EmptyCommentThreadFactory; + emptyCommentThreadFactory?: EmptyCommentThreadFactory; /** * Optional reaction provider diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts index 4dc7dea8cfc..9adf4c932e7 100644 --- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts @@ -19,10 +19,10 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { VIEWLET_ID as EXPLORER } from 'vs/workbench/contrib/files/common/files'; import { VIEWLET_ID as SCM } from 'vs/workbench/contrib/scm/common/scm'; import { VIEWLET_ID as DEBUG } from 'vs/workbench/contrib/debug/common/debug'; -import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { URI } from 'vs/base/common/uri'; import { ViewletRegistry, Extensions as ViewletExtensions, ViewletDescriptor, ShowViewletAction } from 'vs/workbench/browser/viewlet'; -import { IExtensionDescription, IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { ViewContainerViewlet } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; diff --git a/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts b/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts index 79ea63cecdd..ab50e64e536 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts @@ -158,18 +158,18 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb }; if (hasProvide) { provider.provideDebugConfigurations = (folder) => { - return Promise.resolve(this._proxy.$provideDebugConfigurations(handle, folder)); + return this._proxy.$provideDebugConfigurations(handle, folder); }; } if (hasResolve) { provider.resolveDebugConfiguration = (folder, config) => { - return Promise.resolve(this._proxy.$resolveDebugConfiguration(handle, folder, config)); + return this._proxy.$resolveDebugConfiguration(handle, folder, config); }; } if (hasProvideDebugAdapter) { console.info('DebugConfigurationProvider.debugAdapterExecutable is deprecated and will be removed soon; please use DebugAdapterDescriptorFactory.createDebugAdapterDescriptor instead.'); provider.debugAdapterExecutable = (folder) => { - return Promise.resolve(this._proxy.$legacyDebugAdapterExecutable(handle, folder)); + return this._proxy.$legacyDebugAdapterExecutable(handle, folder); }; } this._debugConfigurationProviders.set(handle, provider); @@ -226,10 +226,17 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb } } - public $startDebugging(_folderUri: uri | undefined, nameOrConfiguration: string | IConfig): Promise { + private getSession(sessionId: DebugSessionUUID | undefined): IDebugSession | undefined { + if (sessionId) { + return this.debugService.getModel().getSessions(true).filter(s => s.getId() === sessionId).pop(); + } + return undefined; + } + + public $startDebugging(_folderUri: uri | undefined, nameOrConfiguration: string | IConfig, parentSessionID: DebugSessionUUID | undefined): Promise { const folderUri = _folderUri ? uri.revive(_folderUri) : undefined; const launch = this.debugService.getConfigurationManager().getLaunch(folderUri); - return this.debugService.startDebugging(launch, nameOrConfiguration).then(success => { + return this.debugService.startDebugging(launch, nameOrConfiguration, false, this.getSession(parentSessionID)).then(success => { return success; }, err => { return Promise.reject(new Error(err && err.message ? err.message : 'cannot start debugging')); diff --git a/src/vs/workbench/api/electron-browser/mainThreadExtensionService.ts b/src/vs/workbench/api/electron-browser/mainThreadExtensionService.ts index 21034b7ddc6..29004b89168 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadExtensionService.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadExtensionService.ts @@ -7,30 +7,43 @@ import { SerializedError } from 'vs/base/common/errors'; import Severity from 'vs/base/common/severity'; import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; import { IExtHostContext, MainContext, MainThreadExtensionServiceShape } from 'vs/workbench/api/node/extHost.protocol'; -import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { IExtensionService, ExtensionActivationError } from 'vs/workbench/services/extensions/common/extensions'; import { ExtensionService } from 'vs/workbench/services/extensions/electron-browser/extensionService'; -import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { localize } from 'vs/nls'; +import { Action } from 'vs/base/common/actions'; +import { EnablementState } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { IWindowService } from 'vs/platform/windows/common/windows'; +import { IExtensionsWorkbenchService, IExtension } from 'vs/workbench/contrib/extensions/common/extensions'; @extHostNamedCustomer(MainContext.MainThreadExtensionService) export class MainThreadExtensionService implements MainThreadExtensionServiceShape { private readonly _extensionService: ExtensionService; + private readonly _notificationService: INotificationService; + private readonly _extensionsWorkbenchService: IExtensionsWorkbenchService; + private readonly _windowService: IWindowService; constructor( extHostContext: IExtHostContext, - @IExtensionService extensionService: IExtensionService + @IExtensionService extensionService: IExtensionService, + @INotificationService notificationService: INotificationService, + @IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService, + @IWindowService windowService: IWindowService ) { if (extensionService instanceof ExtensionService) { this._extensionService = extensionService; } + this._notificationService = notificationService; + this._extensionsWorkbenchService = extensionsWorkbenchService; + this._windowService = windowService; } public dispose(): void { } - $localShowMessage(severity: Severity, msg: string): void { - this._extensionService._logOrShowMessage(severity, msg); - } $activateExtension(extensionId: ExtensionIdentifier, activationEvent: string): Promise { return this._extensionService._activateById(extensionId, activationEvent); } @@ -49,6 +62,66 @@ export class MainThreadExtensionService implements MainThreadExtensionServiceSha console.error(`[${extensionId}]${error.message}`); console.error(error.stack); } - $onExtensionActivationFailed(extensionId: ExtensionIdentifier): void { + async $onExtensionActivationError(extensionId: ExtensionIdentifier, activationError: ExtensionActivationError): Promise { + if (typeof activationError === 'string') { + this._extensionService._logOrShowMessage(Severity.Error, activationError); + } else { + this._handleMissingDependency(extensionId, activationError.dependency); + } } + + private async _handleMissingDependency(extensionId: ExtensionIdentifier, missingDependency: string): Promise { + const extension = await this._extensionService.getExtension(extensionId.value); + if (extension) { + const local = await this._extensionsWorkbenchService.queryLocal(); + const installedDependency = local.filter(i => areSameExtensions(i.identifier, { id: missingDependency }))[0]; + if (installedDependency) { + await this._handleMissingInstalledDependency(extension, installedDependency); + } else { + await this._handleMissingNotInstalledDependency(extension, missingDependency); + } + } + } + + private async _handleMissingInstalledDependency(extension: IExtensionDescription, missingInstalledDependency: IExtension): Promise { + const extName = extension.displayName || extension.name; + if (missingInstalledDependency.enablementState === EnablementState.Enabled || missingInstalledDependency.enablementState === EnablementState.WorkspaceEnabled) { + this._notificationService.notify({ + severity: Severity.Error, + message: localize('reload window', "Cannot activate extension '{0}' because it depends on extension '{1}', which is not loaded. Would you like to reload the window to load the extension?", extName, missingInstalledDependency.displayName), + actions: { + primary: [new Action('reload', localize('reload', "Reload Window"), '', true, () => this._windowService.reloadWindow())] + } + }); + } else { + this._notificationService.notify({ + severity: Severity.Error, + message: localize('disabledDep', "Cannot activate extension '{0}' because it depends on extension '{1}', which is disabled. Would you like to enable the extension and reload the window?", extName, missingInstalledDependency.displayName), + actions: { + primary: [new Action('enable', localize('enable dep', "Enable and Reload"), '', true, + () => this._extensionsWorkbenchService.setEnablement([missingInstalledDependency], missingInstalledDependency.enablementState === EnablementState.Disabled ? EnablementState.Enabled : EnablementState.WorkspaceEnabled) + .then(() => this._windowService.reloadWindow(), e => this._notificationService.error(e)))] + } + }); + } + } + + private async _handleMissingNotInstalledDependency(extension: IExtensionDescription, missingDependency: string): Promise { + const extName = extension.displayName || extension.name; + const dependencyExtension = (await this._extensionsWorkbenchService.queryGallery({ names: [missingDependency] })).firstPage[0]; + if (dependencyExtension) { + this._notificationService.notify({ + severity: Severity.Error, + message: localize('uninstalledDep', "Cannot activate extension '{0}' because it depends on extension '{1}', which is not installed. Would you like to install the extension and reload the window?", extName, dependencyExtension.displayName), + actions: { + primary: [new Action('install', localize('install missing dep', "Install and Reload"), '', true, + () => this._extensionsWorkbenchService.install(dependencyExtension) + .then(() => this._windowService.reloadWindow(), e => this._notificationService.error(e)))] + } + }); + } else { + this._notificationService.error(localize('unknownDep', "Cannot activate extension '{0}' because it depends on an unknown extension '{1}'.", extName, missingDependency)); + } + } + } diff --git a/src/vs/workbench/api/electron-browser/mainThreadMessageService.ts b/src/vs/workbench/api/electron-browser/mainThreadMessageService.ts index be0157d8b5f..b50944135c4 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadMessageService.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadMessageService.ts @@ -8,13 +8,12 @@ import Severity from 'vs/base/common/severity'; import { Action, IAction } from 'vs/base/common/actions'; import { MainThreadMessageServiceShape, MainContext, IExtHostContext, MainThreadMessageOptions } from '../node/extHost.protocol'; import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; -import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { Event } from 'vs/base/common/event'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { dispose } from 'vs/base/common/lifecycle'; -import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; @extHostNamedCustomer(MainContext.MainThreadMessageService) export class MainThreadMessageService implements MainThreadMessageServiceShape { diff --git a/src/vs/workbench/api/electron-browser/mainThreadTask.ts b/src/vs/workbench/api/electron-browser/mainThreadTask.ts index 89d5c5778b1..eb4892a794b 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadTask.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadTask.ts @@ -42,12 +42,6 @@ namespace TaskExecutionDTO { task: TaskDTO.from(value.task) }; } - export function to(value: TaskExecutionDTO, workspace: IWorkspaceContextService, executeOnly: boolean): TaskExecution { - return { - id: value.id, - task: TaskDTO.to(value.task, workspace, executeOnly) - }; - } } namespace TaskProcessStartedDTO { @@ -143,7 +137,7 @@ namespace ProcessExecutionDTO { return candidate && !!candidate.process; } export function from(value: CommandConfiguration): ProcessExecutionDTO { - const process: string = Types.isString(value.name) ? value.name : value.name.value; + 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, @@ -347,8 +341,8 @@ namespace TaskDTO { return result; } - export function to(task: TaskDTO, workspace: IWorkspaceContextService, executeOnly: boolean): ContributedTask | undefined { - if (typeof task.name !== 'string') { + export function to(task: TaskDTO | undefined, workspace: IWorkspaceContextService, executeOnly: boolean): ContributedTask | undefined { + if (!task || (typeof task.name !== 'string')) { return undefined; } @@ -370,7 +364,7 @@ namespace TaskDTO { const source = TaskSourceDTO.to(task.source, workspace); const label = nls.localize('task.label', '{0}: {1}', source.label, task.name); - const definition = TaskDefinitionDTO.to(task.definition, executeOnly); + const definition = TaskDefinitionDTO.to(task.definition, executeOnly)!; const id = `${task.source.extensionId}.${definition._key}`; const result: ContributedTask = new ContributedTask( id, // uuidMap.getUUID(identifier) @@ -420,7 +414,7 @@ export class MainThreadTask implements MainThreadTaskShape { this._taskService.onDidStateChange((event: TaskEvent) => { const task = event.__task!; if (event.kind === TaskEventKind.Start) { - this._proxy.$onDidStartTask(TaskExecutionDTO.from(task.getTaskExecution()), event.terminalId); + this._proxy.$onDidStartTask(TaskExecutionDTO.from(task.getTaskExecution()), event.terminalId!); } else if (event.kind === TaskEventKind.ProcessStarted) { this._proxy.$onDidStartTaskProcess(TaskProcessStartedDTO.from(task.getTaskExecution(), event.processId!)); } else if (event.kind === TaskEventKind.ProcessEnded) { @@ -439,9 +433,13 @@ export class MainThreadTask implements MainThreadTaskShape { } $createTaskId(taskDTO: TaskDTO): Promise { - return new Promise((resolve) => { + return new Promise((resolve, reject) => { let task = TaskDTO.to(taskDTO, this._workspaceContextServer, true); - resolve(task._id); + if (task) { + resolve(task._id); + } else { + reject(new Error('Task could not be created from DTO')); + } }); } @@ -471,8 +469,11 @@ export class MainThreadTask implements MainThreadTaskShape { } public $unregisterTaskProvider(handle: number): Promise { - this._providers.get(handle).disposable.dispose(); - this._providers.delete(handle); + const provider = this._providers.get(handle); + if (provider) { + provider.disposable.dispose(); + this._providers.delete(handle); + } return Promise.resolve(undefined); } @@ -493,20 +494,24 @@ export class MainThreadTask implements MainThreadTaskShape { return new Promise((resolve, reject) => { if (TaskHandleDTO.is(value)) { 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 + if (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 + }); + const result: TaskExecutionDTO = { + id: value.id, + task: TaskDTO.from(task) + }; + resolve(result); + }, (_error) => { + reject(new Error('Task not found')); }); - const result: TaskExecutionDTO = { - id: value.id, - task: TaskDTO.from(task) - }; - resolve(result); - }, (_error) => { - reject(new Error('Task not found')); - }); + } else { + reject(new Error('No workspace folder')); + } } else { - const 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 }); diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index ed31eb3fed8..7472c494fe9 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -59,11 +59,11 @@ import { ExtHostUrls } from 'vs/workbench/api/node/extHostUrls'; import { ExtHostWebviews } from 'vs/workbench/api/node/extHostWebview'; import { ExtHostWindow } from 'vs/workbench/api/node/extHostWindow'; import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace'; -import { IExtensionDescription, throwProposedApiError, checkProposedApiEnabled, nullExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; +import { throwProposedApiError, checkProposedApiEnabled, nullExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import { ProxyIdentifier } from 'vs/workbench/services/extensions/node/proxyIdentifier'; import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry'; import * as vscode from 'vscode'; -import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { originalFSPath } from 'vs/base/common/resources'; import { CLIServer } from 'vs/workbench/api/node/extHostCLIServer'; @@ -699,8 +699,8 @@ export function createApiFactory( registerDebugAdapterTrackerFactory(debugType: string, factory: vscode.DebugAdapterTrackerFactory) { return extHostDebugService.registerDebugAdapterTrackerFactory(debugType, factory); }, - startDebugging(folder: vscode.WorkspaceFolder | undefined, nameOrConfig: string | vscode.DebugConfiguration) { - return extHostDebugService.startDebugging(folder, nameOrConfig); + startDebugging(folder: vscode.WorkspaceFolder | undefined, nameOrConfig: string | vscode.DebugConfiguration, parentSession?: vscode.DebugSession) { + return extHostDebugService.startDebugging(folder, nameOrConfig, parentSession); }, addBreakpoints(breakpoints: vscode.Breakpoint[]) { return extHostDebugService.addBreakpoints(breakpoints); diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 119d5239ec4..9abd44b1371 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -36,14 +36,14 @@ import { ITreeItem, IRevealOptions } from 'vs/workbench/common/views'; import { IAdapterDescriptor, IConfig, ITerminalSettings } from 'vs/workbench/contrib/debug/common/debug'; import { ITextQueryBuilderOptions } from 'vs/workbench/contrib/search/common/queryBuilder'; import { ITerminalDimensions } from 'vs/workbench/contrib/terminal/common/terminal'; -import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; +import { ExtensionActivationError } from 'vs/workbench/services/extensions/common/extensions'; import { IRPCProtocol, createExtHostContextProxyIdentifier as createExtId, createMainContextProxyIdentifier as createMainId } from 'vs/workbench/services/extensions/node/proxyIdentifier'; import { IProgressOptions, IProgressStep } from 'vs/platform/progress/common/progress'; import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles'; import * as vscode from 'vscode'; import { IMarkdownString } from 'vs/base/common/htmlContent'; import { ResolvedAuthority } from 'vs/platform/remote/common/remoteAuthorityResolver'; -import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { IRemoteConsoleLog } from 'vs/base/node/console'; import * as codeInset from 'vs/workbench/contrib/codeinset/common/codeInset'; import * as callHierarchy from 'vs/workbench/contrib/callHierarchy/common/callHierarchy'; @@ -61,7 +61,7 @@ export interface IEnvironment { export interface IStaticWorkspaceData { id: string; name: string; - configuration?: UriComponents; + configuration?: UriComponents | null; } export interface IWorkspaceData extends IStaticWorkspaceData { @@ -72,7 +72,7 @@ export interface IInitData { commit?: string; parentPid: number; environment: IEnvironment; - workspace?: IStaticWorkspaceData; + workspace?: IStaticWorkspaceData | null; resolvedExtensions: ExtensionIdentifier[]; hostExtensions: ExtensionIdentifier[]; extensions: IExtensionDescription[]; @@ -372,7 +372,7 @@ export interface MainThreadProgressShape extends IDisposable { } export interface MainThreadTerminalServiceShape extends IDisposable { - $createTerminal(name?: string, shellPath?: string, shellArgs?: string[], cwd?: string | UriComponents, 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 | null }, waitOnExit?: boolean, strictEnv?: boolean): Promise<{ id: number, name: string }>; $createTerminalRenderer(name: string): Promise; $dispose(terminalId: number): void; $hide(terminalId: number): void; @@ -573,11 +573,10 @@ export interface MainThreadTaskShape extends IDisposable { } export interface MainThreadExtensionServiceShape extends IDisposable { - $localShowMessage(severity: Severity, msg: string): void; $activateExtension(extensionId: ExtensionIdentifier, activationEvent: string | null): Promise; $onWillActivateExtension(extensionId: ExtensionIdentifier): void; $onDidActivateExtension(extensionId: ExtensionIdentifier, startup: boolean, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationEvent: string | null): void; - $onExtensionActivationFailed(extensionId: ExtensionIdentifier): void; + $onExtensionActivationError(extensionId: ExtensionIdentifier, error: ExtensionActivationError): Promise; $onExtensionRuntimeError(extensionId: ExtensionIdentifier, error: SerializedError): void; } @@ -641,15 +640,15 @@ export interface MainThreadDebugServiceShape extends IDisposable { $registerDebugTypes(debugTypes: string[]): void; $sessionCached(sessionID: string): void; $acceptDAMessage(handle: number, message: DebugProtocol.ProtocolMessage): void; - $acceptDAError(handle: number, name: string, message: string, stack: string): void; - $acceptDAExit(handle: number, code: number, signal: string): void; + $acceptDAError(handle: number, name: string, message: string, stack: string | undefined): void; + $acceptDAExit(handle: number, code: number | undefined, signal: string | undefined): void; $registerDebugConfigurationProvider(type: string, hasProvideMethod: boolean, hasResolveMethod: boolean, hasProvideDaMethod: boolean, handle: number): Promise; $registerDebugAdapterDescriptorFactory(type: string, handle: number): Promise; $registerDebugAdapterTrackerFactory(type: string, handle: number); $unregisterDebugConfigurationProvider(handle: number): void; $unregisterDebugAdapterDescriptorFactory(handle: number): void; $unregisterDebugAdapterTrackerFactory(handle: number): void; - $startDebugging(folder: UriComponents | undefined, nameOrConfig: string | vscode.DebugConfiguration): Promise; + $startDebugging(folder: UriComponents | undefined, nameOrConfig: string | vscode.DebugConfiguration, parentSessionID: string | undefined): Promise; $customDebugAdapterRequest(id: DebugSessionUUID, command: string, args: any): Promise; $appendDebugConsole(value: string): void; $startBreakpointEvents(): void; @@ -1067,7 +1066,7 @@ export interface ExtHostDebugServiceShape { $startDASession(handle: number, session: IDebugSessionDto): Promise; $stopDASession(handle: number): Promise; $sendDAMessage(handle: number, message: DebugProtocol.ProtocolMessage): void; - $resolveDebugConfiguration(handle: number, folder: UriComponents | undefined, debugConfiguration: IConfig): Promise; + $resolveDebugConfiguration(handle: number, folder: UriComponents | undefined, debugConfiguration: IConfig): Promise; $provideDebugConfigurations(handle: number, folder: UriComponents | undefined): Promise; $legacyDebugAdapterExecutable(handle: number, folderUri: UriComponents | undefined): Promise; // TODO@AW legacy $provideDebugAdapter(handle: number, session: IDebugSessionDto): Promise; diff --git a/src/vs/workbench/api/node/extHostComments.ts b/src/vs/workbench/api/node/extHostComments.ts index 753a868eb27..9f400a165b8 100644 --- a/src/vs/workbench/api/node/extHostComments.ts +++ b/src/vs/workbench/api/node/extHostComments.ts @@ -14,8 +14,7 @@ import { ExtHostCommentsShape, IMainContext, MainContext, MainThreadCommentsShap import { CommandsConverter, ExtHostCommands } from './extHostCommands'; import { IRange } from 'vs/editor/common/core/range'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; -import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; +import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { Event, Emitter } from 'vs/base/common/event'; interface HandlerData { @@ -383,8 +382,6 @@ export class ExtHostCommentThread implements vscode.CommentThread { this._proxy.$updateCommentThreadLabel(this._commentController.handle, this.handle, this._label); } - private _comments: vscode.Comment[] = []; - get comments(): vscode.Comment[] { return this._comments; } @@ -435,7 +432,8 @@ export class ExtHostCommentThread implements vscode.CommentThread { private _commentController: ExtHostCommentController, private _threadId: string, private _resource: vscode.Uri, - private _range: vscode.Range + private _range: vscode.Range, + private _comments: vscode.Comment[] ) { this._proxy.$createCommentThread( this._commentController.handle, @@ -517,7 +515,7 @@ class ExtHostCommentController implements vscode.CommentController { private _threads: Map = new Map(); commentingRangeProvider?: vscode.CommentingRangeProvider; - emptyCommentThreadFactory: vscode.EmptyCommentThreadFactory; + emptyCommentThreadFactory?: vscode.EmptyCommentThreadFactory; private _commentReactionProvider?: vscode.CommentReactionProvider; @@ -544,8 +542,8 @@ class ExtHostCommentController implements vscode.CommentController { this._proxy.$registerCommentController(this.handle, _id, _label); } - createCommentThread(id: string, resource: vscode.Uri, range: vscode.Range): vscode.CommentThread { - const commentThread = new ExtHostCommentThread(this._proxy, this._commandsConverter, this, id, resource, range); + createCommentThread(id: string, resource: vscode.Uri, range: vscode.Range, comments: vscode.Comment[]): vscode.CommentThread { + const commentThread = new ExtHostCommentThread(this._proxy, this._commandsConverter, this, id, resource, range, comments); this._threads.set(commentThread.handle, commentThread); return commentThread; } diff --git a/src/vs/workbench/api/node/extHostDebugService.ts b/src/vs/workbench/api/node/extHostDebugService.ts index 394527812e6..c2100b974fc 100644 --- a/src/vs/workbench/api/node/extHostDebugService.ts +++ b/src/vs/workbench/api/node/extHostDebugService.ts @@ -30,9 +30,9 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; import { CancellationToken } from 'vs/base/common/cancellation'; import { ExtHostCommands } from 'vs/workbench/api/node/extHostCommands'; -import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry'; - +import { IProcessEnvironment } from 'vs/base/common/platform'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; export class ExtHostDebugService implements ExtHostDebugServiceShape { @@ -77,7 +77,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { private _variableResolver: IConfigurationResolverService; - private _integratedTerminalInstance: vscode.Terminal; + private _integratedTerminalInstance?: vscode.Terminal; private _terminalDisposedListener: IDisposable; @@ -241,8 +241,8 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { return this._debugServiceProxy.$unregisterBreakpoints(ids, fids); } - public startDebugging(folder: vscode.WorkspaceFolder | undefined, nameOrConfig: string | vscode.DebugConfiguration): Promise { - return this._debugServiceProxy.$startDebugging(folder ? folder.uri : undefined, nameOrConfig); + public startDebugging(folder: vscode.WorkspaceFolder | undefined, nameOrConfig: string | vscode.DebugConfiguration, parentSession?: vscode.DebugSession): Promise { + return this._debugServiceProxy.$startDebugging(folder ? folder.uri : undefined, nameOrConfig, parentSession ? parentSession.id : undefined); } public registerDebugConfigurationProvider(type: string, provider: vscode.DebugConfigurationProvider): vscode.Disposable { @@ -324,7 +324,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { // React on terminal disposed and check if that is the debug terminal #12956 this._terminalDisposedListener = this._terminalService.onDidCloseTerminal(terminal => { if (this._integratedTerminalInstance && this._integratedTerminalInstance === terminal) { - this._integratedTerminalInstance = null; + this._integratedTerminalInstance = undefined; } }); } @@ -341,16 +341,17 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { } }).then(needNewTerminal => { - if (needNewTerminal) { + if (needNewTerminal || !this._integratedTerminalInstance) { this._integratedTerminalInstance = this._terminalService.createTerminal(args.title || nls.localize('debug.terminal.title', "debuggee")); } + const terminal: vscode.Terminal = this._integratedTerminalInstance; - this._integratedTerminalInstance.show(); + terminal.show(); return this._integratedTerminalInstance.processId.then(shellProcessId => { const command = prepareCommand(args, config); - this._integratedTerminalInstance.sendText(command, true); + terminal.sendText(command, true); return shellProcessId; }); @@ -363,13 +364,13 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { return terminalLauncher.runInTerminal(args, config); } } - return undefined; + return Promise.resolve(undefined); } public async $substituteVariables(folderUri: UriComponents | undefined, config: IConfig): Promise { - const [workspaceFolders, configProvider] = await Promise.all([this._workspaceService.getWorkspaceFolders2(), this._configurationService.getConfigProvider()]); if (!this._variableResolver) { - this._variableResolver = new ExtHostVariableResolverService(workspaceFolders, this._editorsService, configProvider); + const [workspaceFolders, configProvider] = await Promise.all([this._workspaceService.getWorkspaceFolders2(), this._configurationService.getConfigProvider()]); + this._variableResolver = new ExtHostVariableResolverService(workspaceFolders || [], this._editorsService, configProvider); } let ws: IWorkspaceFolder | undefined; const folder = await this.getFolder(folderUri); @@ -390,9 +391,10 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { const mythis = this; const session = await this.getSession(sessionDto); - return this.getAdapterDescriptor(this.getAdapterFactoryByType(session.type), session).then(x => { - const adapter = this.convertToDto(x); + return this.getAdapterDescriptor(this.getAdapterFactoryByType(session.type), session).then(daDescriptor => { + + const adapter = this.convertToDto(daDescriptor); let da: AbstractDebugAdapter | undefined = undefined; switch (adapter.type) { @@ -413,8 +415,10 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { break; } - if (da) { - this._debugAdapters.set(debugAdapterHandle, da); + const debugAdapter = da; + + if (debugAdapter) { + this._debugAdapters.set(debugAdapterHandle, debugAdapter); return this.getDebugAdapterTrackers(session).then(tracker => { @@ -422,9 +426,9 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { this._debugAdaptersTrackers.set(debugAdapterHandle, tracker); } - da.onMessage(message => { + debugAdapter.onMessage(message => { - if (tracker) { + if (tracker && tracker.onDidSendMessage) { tracker.onDidSendMessage(message); } @@ -433,24 +437,24 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { mythis._debugServiceProxy.$acceptDAMessage(debugAdapterHandle, message); }); - da.onError(err => { - if (tracker) { + debugAdapter.onError(err => { + if (tracker && tracker.onError) { tracker.onError(err); } this._debugServiceProxy.$acceptDAError(debugAdapterHandle, err.name, err.message, err.stack); }); - da.onExit(code => { - if (tracker) { + debugAdapter.onExit((code: number) => { + if (tracker && tracker.onExit) { tracker.onExit(code, undefined); } - this._debugServiceProxy.$acceptDAExit(debugAdapterHandle, code, null); + this._debugServiceProxy.$acceptDAExit(debugAdapterHandle, code, undefined); }); - if (tracker) { + if (tracker && tracker.onWillStartSession) { tracker.onWillStartSession(); } - return da.startSession(); + return debugAdapter.startSession(); }); } @@ -458,13 +462,13 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { }); } - public $sendDAMessage(debugAdapterHandle: number, message: DebugProtocol.ProtocolMessage): Promise { + public $sendDAMessage(debugAdapterHandle: number, message: DebugProtocol.ProtocolMessage): void { // VS Code -> DA message = convertToDAPaths(message, false); const tracker = this._debugAdaptersTrackers.get(debugAdapterHandle); // TODO@AW: same handle? - if (tracker) { + if (tracker && tracker.onWillReceiveMessage) { tracker.onWillReceiveMessage(message); } @@ -472,14 +476,13 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { if (da) { da.sendMessage(message); } - return undefined; } public $stopDASession(debugAdapterHandle: number): Promise { const tracker = this._debugAdaptersTrackers.get(debugAdapterHandle); this._debugAdaptersTrackers.delete(debugAdapterHandle); - if (tracker) { + if (tracker && tracker.onWillStopSession) { tracker.onWillStopSession(); } @@ -488,7 +491,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { if (da) { return da.stopSession(); } else { - return undefined; + return Promise.resolve(void 0); } } @@ -500,8 +503,8 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { if (delta.added) { for (const bpd of delta.added) { - - if (!this._breakpoints.has(bpd.id)) { + const id = bpd.id; + if (id && !this._breakpoints.has(id)) { let bp: vscode.Breakpoint; if (bpd.type === 'function') { bp = new FunctionBreakpoint(bpd.functionName, bpd.enabled, bpd.condition, bpd.hitCondition, bpd.logMessage); @@ -509,8 +512,8 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { const uri = URI.revive(bpd.uri); bp = new SourceBreakpoint(new Location(uri, new Position(bpd.line, bpd.character)), bpd.enabled, bpd.condition, bpd.hitCondition, bpd.logMessage); } - (bp as any)._id = bpd.id; - this._breakpoints.set(bpd.id, bp); + (bp as any)._id = id; + this._breakpoints.set(id, bp); a.push(bp); } } @@ -528,24 +531,26 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { if (delta.changed) { for (const bpd of delta.changed) { - const bp = this._breakpoints.get(bpd.id); - if (bp) { - if (bp instanceof FunctionBreakpoint && bpd.type === 'function') { - const fbp = bp; - fbp.enabled = bpd.enabled; - fbp.condition = bpd.condition; - fbp.hitCondition = bpd.hitCondition; - fbp.logMessage = bpd.logMessage; - fbp.functionName = bpd.functionName; - } else if (bp instanceof SourceBreakpoint && bpd.type === 'source') { - const sbp = bp; - sbp.enabled = bpd.enabled; - sbp.condition = bpd.condition; - sbp.hitCondition = bpd.hitCondition; - sbp.logMessage = bpd.logMessage; - sbp.location = new Location(URI.revive(bpd.uri), new Position(bpd.line, bpd.character)); + if (bpd.id) { + const bp = this._breakpoints.get(bpd.id); + if (bp) { + if (bp instanceof FunctionBreakpoint && bpd.type === 'function') { + const fbp = bp; + fbp.enabled = bpd.enabled; + fbp.condition = bpd.condition; + fbp.hitCondition = bpd.hitCondition; + fbp.logMessage = bpd.logMessage; + fbp.functionName = bpd.functionName; + } else if (bp instanceof SourceBreakpoint && bpd.type === 'source') { + const sbp = bp; + sbp.enabled = bpd.enabled; + sbp.condition = bpd.condition; + sbp.hitCondition = bpd.hitCondition; + sbp.logMessage = bpd.logMessage; + sbp.location = new Location(URI.revive(bpd.uri), new Position(bpd.line, bpd.character)); + } + c.push(bp); } - c.push(bp); } } } @@ -553,41 +558,57 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { this.fireBreakpointChanges(a, r, c); } - public async $provideDebugConfigurations(configProviderHandle: number, folderUri: UriComponents | undefined): Promise { - const provider = this.getConfigProviderByHandle(configProviderHandle); - if (!provider) { - return Promise.reject(new Error('no handler found')); - } - if (!provider.provideDebugConfigurations) { - return Promise.reject(new Error('handler has no method provideDebugConfigurations')); - } - const folder = await this.getFolder(folderUri); - return asPromise(() => provider.provideDebugConfigurations(folder, CancellationToken.None)); + public $provideDebugConfigurations(configProviderHandle: number, folderUri: UriComponents | undefined): Promise { + return asPromise(async () => { + const provider = this.getConfigProviderByHandle(configProviderHandle); + if (!provider) { + throw new Error('no DebugConfigurationProvider found'); + } + if (!provider.provideDebugConfigurations) { + throw new Error('DebugConfigurationProvider has no method provideDebugConfigurations'); + } + const folder = await this.getFolder(folderUri); + return provider.provideDebugConfigurations(folder, CancellationToken.None); + }).then(debugConfigurations => { + if (!debugConfigurations) { + throw new Error('nothing returned from DebugConfigurationProvider.provideDebugConfigurations'); + } + return debugConfigurations; + }); } - public async $resolveDebugConfiguration(configProviderHandle: number, folderUri: UriComponents | undefined, debugConfiguration: vscode.DebugConfiguration): Promise { - const provider = this.getConfigProviderByHandle(configProviderHandle); - if (!provider) { - return Promise.reject(new Error('no handler found')); - } - if (!provider.resolveDebugConfiguration) { - return Promise.reject(new Error('handler has no method resolveDebugConfiguration')); - } - const folder = await this.getFolder(folderUri); - return asPromise(() => provider.resolveDebugConfiguration(folder, debugConfiguration, CancellationToken.None)); + public $resolveDebugConfiguration(configProviderHandle: number, folderUri: UriComponents | undefined, debugConfiguration: vscode.DebugConfiguration): Promise { + return asPromise(async () => { + const provider = this.getConfigProviderByHandle(configProviderHandle); + if (!provider) { + throw new Error('no DebugConfigurationProvider found'); + } + if (!provider.resolveDebugConfiguration) { + throw new Error('DebugConfigurationProvider has no method resolveDebugConfiguration'); + } + const folder = await this.getFolder(folderUri); + return provider.resolveDebugConfiguration(folder, debugConfiguration, CancellationToken.None); + }); } - // TODO@AW legacy - public async $legacyDebugAdapterExecutable(configProviderHandle: number, folderUri: UriComponents | undefined): Promise { - const provider = this.getConfigProviderByHandle(configProviderHandle); - if (!provider) { - return Promise.reject(new Error('no handler found')); - } - if (!provider.debugAdapterExecutable) { - return Promise.reject(new Error('handler has no method debugAdapterExecutable')); - } - const folder = await this.getFolder(folderUri); - return asPromise(() => provider.debugAdapterExecutable(folder, CancellationToken.None)).then(x => this.convertToDto(x)); + // TODO@AW deprecated and legacy + public $legacyDebugAdapterExecutable(configProviderHandle: number, folderUri: UriComponents | undefined): Promise { + return asPromise(async () => { + const provider = this.getConfigProviderByHandle(configProviderHandle); + if (!provider) { + throw new Error('no DebugConfigurationProvider found'); + } + if (!provider.debugAdapterExecutable) { + throw new Error('DebugConfigurationProvider has no method debugAdapterExecutable'); + } + const folder = await this.getFolder(folderUri); + return provider.debugAdapterExecutable(folder, CancellationToken.None); + }).then(executable => { + if (!executable) { + throw new Error('nothing returned from DebugConfigurationProvider.debugAdapterExecutable'); + } + return this.convertToDto(executable); + }); } public async $provideDebugAdapter(adapterProviderHandle: number, sessionDto: IDebugSessionDto): Promise { @@ -629,7 +650,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { // private & dto helpers - private convertToDto(x: vscode.DebugAdapterDescriptor): IAdapterDescriptor { + private convertToDto(x: vscode.DebugAdapterDescriptor | undefined): IAdapterDescriptor { if (x instanceof DebugAdapterExecutable) { return { type: 'executable', @@ -649,11 +670,11 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { implementation: x.implementation }; } else */ { - throw new Error('unexpected type'); + throw new Error('convertToDto unexpected type'); } } - private getAdapterFactoryByType(type: string): vscode.DebugAdapterDescriptorFactory { + private getAdapterFactoryByType(type: string): vscode.DebugAdapterDescriptorFactory | undefined { const results = this._adapterFactories.filter(p => p.type === type); if (results.length > 0) { return results[0].factory; @@ -661,7 +682,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { return undefined; } - private getAdapterProviderByHandle(handle: number): vscode.DebugAdapterDescriptorFactory { + private getAdapterProviderByHandle(handle: number): vscode.DebugAdapterDescriptorFactory | undefined { const results = this._adapterFactories.filter(p => p.handle === handle); if (results.length > 0) { return results[0].factory; @@ -669,7 +690,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { return undefined; } - private getConfigProviderByHandle(handle: number): vscode.DebugConfigurationProvider { + private getConfigProviderByHandle(handle: number): vscode.DebugConfigurationProvider | undefined { const results = this._configProviders.filter(p => p.handle === handle); if (results.length > 0) { return results[0].provider; @@ -694,18 +715,18 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { return false; } - private getDebugAdapterTrackers(session: ExtHostDebugSession): Promise { + private getDebugAdapterTrackers(session: ExtHostDebugSession): Promise { const config = session.configuration; const type = config.type; const promises = this._trackerFactories .filter(tuple => tuple.type === type || tuple.type === '*') - .map(tuple => asPromise(() => tuple.factory.createDebugAdapterTracker(session)).then(p => p).catch(err => null)); + .map(tuple => asPromise>(() => tuple.factory.createDebugAdapterTracker(session)).then(p => p, err => null)); return Promise.race([ - Promise.all(promises).then(trackers => { - trackers = trackers.filter(t => t); // filter null + Promise.all(promises).then(result => { + const trackers = result.filter(t => !!t); // filter null if (trackers.length > 0) { return new MultiTracker(trackers); } @@ -723,7 +744,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { }); } - private async getAdapterDescriptor(adapterProvider: vscode.DebugAdapterDescriptorFactory, session: ExtHostDebugSession): Promise { + private async getAdapterDescriptor(adapterProvider: vscode.DebugAdapterDescriptorFactory | undefined, session: ExtHostDebugSession): Promise { // a "debugServer" attribute in the launch config takes precedence const serverPort = session.configuration.debugServer; @@ -732,16 +753,25 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { } // TODO@AW legacy - const pairs = this._configProviders.filter(p => p.type === session.type); - if (pairs.length > 0) { - if (pairs[0].provider.debugAdapterExecutable) { - return asPromise(() => pairs[0].provider.debugAdapterExecutable(session.workspaceFolder, CancellationToken.None)); - } + const pair = this._configProviders.filter(p => p.type === session.type).pop(); + if (pair && pair.provider.debugAdapterExecutable) { + const func = pair.provider.debugAdapterExecutable; + return asPromise(() => func(session.workspaceFolder, CancellationToken.None)).then(executable => { + if (executable) { + return executable; + } + return undefined; + }); } if (adapterProvider) { const extensionRegistry = await this._extensionService.getExtensionRegistry(); - return asPromise(() => adapterProvider.createDebugAdapterDescriptor(session, this.daExecutableFromPackage(session, extensionRegistry))); + return asPromise(() => adapterProvider.createDebugAdapterDescriptor(session, this.daExecutableFromPackage(session, extensionRegistry))).then(daDescriptor => { + if (daDescriptor) { + return daDescriptor; + } + return undefined; + }); } // try deprecated command based extension API "adapterExecutableCommand" to determine the executable @@ -788,7 +818,10 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { private async getSession(dto: IDebugSessionDto): Promise { if (dto) { if (typeof dto === 'string') { - return this._debugSessions.get(dto); + const ds = this._debugSessions.get(dto); + if (ds) { + return ds; + } } else { let ds = this._debugSessions.get(dto.id); if (!ds) { @@ -800,7 +833,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { return ds; } } - return undefined; + throw new Error('cannot find session'); } private getFolder(_folderUri: UriComponents | undefined): Promise { @@ -808,7 +841,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { const folderURI = URI.revive(_folderUri); return this._workspaceService.resolveWorkspaceFolder(folderURI); } - return undefined; + return Promise.resolve(undefined); } } @@ -869,7 +902,7 @@ export class ExtHostVariableResolverService extends AbstractVariableResolverServ constructor(folders: vscode.WorkspaceFolder[], editorService: ExtHostDocumentsAndEditors, configurationService: ExtHostConfigProvider) { super({ - getFolderUri: (folderName: string): URI => { + getFolderUri: (folderName: string): URI | undefined => { const found = folders.filter(f => f.name === folderName); if (found && found.length > 0) { return found[0].uri; @@ -879,7 +912,7 @@ export class ExtHostVariableResolverService extends AbstractVariableResolverServ getWorkspaceFolderCount: (): number => { return folders.length; }, - getConfigurationValue: (folderUri: URI, section: string) => { + getConfigurationValue: (folderUri: URI, section: string): string | undefined => { return configurationService.getConfiguration(undefined, folderUri).get(section); }, getExecPath: (): string | undefined => { @@ -902,14 +935,14 @@ export class ExtHostVariableResolverService extends AbstractVariableResolverServ } return undefined; }, - getLineNumber: (): string => { + getLineNumber: (): string | undefined => { const activeEditor = editorService.activeEditor(); if (activeEditor) { return String(activeEditor.selection.end.line + 1); } return undefined; } - }, process.env); + }, process.env as IProcessEnvironment); } } diff --git a/src/vs/workbench/api/node/extHostDocumentSaveParticipant.ts b/src/vs/workbench/api/node/extHostDocumentSaveParticipant.ts index 2225d4dbc06..420ef18bb89 100644 --- a/src/vs/workbench/api/node/extHostDocumentSaveParticipant.ts +++ b/src/vs/workbench/api/node/extHostDocumentSaveParticipant.ts @@ -14,8 +14,8 @@ import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments'; import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles'; import * as vscode from 'vscode'; import { LinkedList } from 'vs/base/common/linkedList'; -import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import { ILogService } from 'vs/platform/log/common/log'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; type Listener = [Function, any, IExtensionDescription]; diff --git a/src/vs/workbench/api/node/extHostExtensionActivator.ts b/src/vs/workbench/api/node/extHostExtensionActivator.ts index a248e0e9f69..801af835289 100644 --- a/src/vs/workbench/api/node/extHostExtensionActivator.ts +++ b/src/vs/workbench/api/node/extHostExtensionActivator.ts @@ -5,9 +5,9 @@ import * as nls from 'vs/nls'; import { IDisposable } from 'vs/base/common/lifecycle'; -import Severity from 'vs/base/common/severity'; import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { ExtensionActivationError, MissingDependencyError } from 'vs/workbench/services/extensions/common/extensions'; const NO_OP_VOID_PROMISE = Promise.resolve(undefined); @@ -173,8 +173,7 @@ export class FailedExtension extends ActivatedExtension { } export interface IExtensionsActivatorHost { - showMessage(severity: Severity, message: string): void; - + onExtensionActivationError(extensionId: ExtensionIdentifier, error: ExtensionActivationError): void; actualActivateExtension(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise; } @@ -283,7 +282,7 @@ export class ExtensionsActivator { if (dep && dep.activationFailed) { // Error condition 2: a dependency has already failed activation - this._host.showMessage(Severity.Error, nls.localize('failedDep1', "Cannot activate extension '{0}' because it depends on extension '{1}', which failed to activate.", currentExtension.displayName || currentExtension.identifier.value, depId)); + this._host.onExtensionActivationError(currentExtension.identifier, nls.localize('failedDep1', "Cannot activate extension '{0}' because it depends on extension '{1}', which failed to activate.", currentExtension.displayName || currentExtension.identifier.value, depId)); const error = new Error(`Dependency ${depId} failed to activate`); (error).detail = dep.activationFailedError; this._activatedExtensions.set(ExtensionIdentifier.toKey(currentExtension.identifier), new FailedExtension(error)); @@ -306,7 +305,7 @@ export class ExtensionsActivator { } // Error condition 1: unknown dependency - this._host.showMessage(Severity.Error, nls.localize('unknownDep', "Cannot activate extension '{0}' because it depends on extension '{1}', which is not installed or disabled. Please install or enable '{1}' and reload the window.", currentExtension.displayName || currentExtension.identifier.value, depId)); + this._host.onExtensionActivationError(currentExtension.identifier, new MissingDependencyError(depId)); const error = new Error(`Unknown dependency '${depId}'`); this._activatedExtensions.set(ExtensionIdentifier.toKey(currentExtension.identifier), new FailedExtension(error)); return; @@ -373,7 +372,7 @@ export class ExtensionsActivator { } const newlyActivatingExtension = this._host.actualActivateExtension(extensionId, reason).then(undefined, (err) => { - this._host.showMessage(Severity.Error, nls.localize('activationError', "Activating extension '{0}' failed: {1}.", extensionId.value, err.message)); + this._host.onExtensionActivationError(extensionId, nls.localize('activationError', "Activating extension '{0}' failed: {1}.", extensionId.value, err.message)); console.error('Activating extension `' + extensionId.value + '` failed: ', err.message); console.log('Here is the error stack: ', err.stack); // Treat the extension as being empty diff --git a/src/vs/workbench/api/node/extHostExtensionService.ts b/src/vs/workbench/api/node/extHostExtensionService.ts index 9510f14cca7..54f8d747785 100644 --- a/src/vs/workbench/api/node/extHostExtensionService.ts +++ b/src/vs/workbench/api/node/extHostExtensionService.ts @@ -9,7 +9,6 @@ import { originalFSPath } from 'vs/base/common/resources'; import { Barrier } from 'vs/base/common/async'; import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; import { TernarySearchTree } from 'vs/base/common/map'; -import Severity from 'vs/base/common/severity'; import { URI } from 'vs/base/common/uri'; import * as pfs from 'vs/base/node/pfs'; import { ILogService } from 'vs/platform/log/common/log'; @@ -20,14 +19,14 @@ import { ActivatedExtension, EmptyExtension, ExtensionActivatedByAPI, ExtensionA import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService'; import { ExtHostStorage } from 'vs/workbench/api/node/extHostStorage'; import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace'; -import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; +import { ExtensionActivationError } from 'vs/workbench/services/extensions/common/extensions'; import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry'; import { connectProxyResolver } from 'vs/workbench/services/extensions/node/proxyResolver'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import * as errors from 'vs/base/common/errors'; import { ResolvedAuthority } from 'vs/platform/remote/common/remoteAuthorityResolver'; import * as vscode from 'vscode'; -import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { IWorkspace } from 'vs/platform/workspace/common/workspace'; import { Schemas } from 'vs/base/common/network'; @@ -208,19 +207,8 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { initData.hostExtensions.forEach((extensionId) => hostExtensions.add(ExtensionIdentifier.toKey(extensionId))); this._activator = new ExtensionsActivator(this._registry, initData.resolvedExtensions, initData.hostExtensions, { - showMessage: (severity: Severity, message: string): void => { - this._mainThreadExtensionsProxy.$localShowMessage(severity, message); - - switch (severity) { - case Severity.Error: - console.error(message); - break; - case Severity.Warning: - console.warn(message); - break; - default: - console.log(message); - } + onExtensionActivationError: (extensionId: ExtensionIdentifier, error: ExtensionActivationError): void => { + this._mainThreadExtensionsProxy.$onExtensionActivationError(extensionId, error); }, actualActivateExtension: async (extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise => { @@ -383,7 +371,6 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { this._logExtensionActivationTimes(extensionDescription, reason, 'success', activationTimes); return activatedExtension; }, (err) => { - this._mainThreadExtensionsProxy.$onExtensionActivationFailed(extensionDescription.identifier); this._logExtensionActivationTimes(extensionDescription, reason, 'failure'); throw err; }); diff --git a/src/vs/workbench/api/node/extHostFileSystemEventService.ts b/src/vs/workbench/api/node/extHostFileSystemEventService.ts index 6d709b3a107..130c7a9711a 100644 --- a/src/vs/workbench/api/node/extHostFileSystemEventService.ts +++ b/src/vs/workbench/api/node/extHostFileSystemEventService.ts @@ -8,11 +8,11 @@ import { AsyncEmitter, Emitter, Event } from 'vs/base/common/event'; import { IRelativePattern, parse } from 'vs/base/common/glob'; import { URI, UriComponents } from 'vs/base/common/uri'; import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/node/extHostDocumentsAndEditors'; -import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import * as vscode from 'vscode'; import { ExtHostFileSystemEventServiceShape, FileSystemEvents, IMainContext, MainContext, ResourceFileEditDto, ResourceTextEditDto, MainThreadTextEditorsShape } from './extHost.protocol'; import * as typeConverter from './extHostTypeConverters'; import { Disposable, WorkspaceEdit } from './extHostTypes'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; class FileSystemWatcher implements vscode.FileSystemWatcher { diff --git a/src/vs/workbench/api/node/extHostLanguageFeatures.ts b/src/vs/workbench/api/node/extHostLanguageFeatures.ts index 0e9b41017b0..923d8403541 100644 --- a/src/vs/workbench/api/node/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/node/extHostLanguageFeatures.ts @@ -22,10 +22,9 @@ import { IRange, Range as EditorRange } from 'vs/editor/common/core/range'; import { isFalsyOrEmpty, isNonEmptyArray, coalesce } from 'vs/base/common/arrays'; import { isObject } from 'vs/base/common/types'; import { ISelection, Selection } from 'vs/editor/common/core/selection'; -import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import { ILogService } from 'vs/platform/log/common/log'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { ExtHostWebview } from 'vs/workbench/api/node/extHostWebview'; import * as codeInset from 'vs/workbench/contrib/codeinset/common/codeInset'; import { generateUuid } from 'vs/base/common/uuid'; diff --git a/src/vs/workbench/api/node/extHostMessageService.ts b/src/vs/workbench/api/node/extHostMessageService.ts index 635f076610d..4f423a4d7b3 100644 --- a/src/vs/workbench/api/node/extHostMessageService.ts +++ b/src/vs/workbench/api/node/extHostMessageService.ts @@ -6,7 +6,7 @@ import Severity from 'vs/base/common/severity'; import * as vscode from 'vscode'; import { MainContext, MainThreadMessageServiceShape, MainThreadMessageOptions, IMainContext } from './extHost.protocol'; -import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; function isMessageItem(item: any): item is vscode.MessageItem { return item && item.title; diff --git a/src/vs/workbench/api/node/extHostProgress.ts b/src/vs/workbench/api/node/extHostProgress.ts index c365acee488..eb82d3d6a5c 100644 --- a/src/vs/workbench/api/node/extHostProgress.ts +++ b/src/vs/workbench/api/node/extHostProgress.ts @@ -6,11 +6,11 @@ import { ProgressOptions } from 'vscode'; import { MainThreadProgressShape, ExtHostProgressShape } from './extHost.protocol'; import { ProgressLocation } from './extHostTypeConverters'; -import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import { Progress, IProgressStep } from 'vs/platform/progress/common/progress'; import { localize } from 'vs/nls'; import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation'; import { debounce } from 'vs/base/common/decorators'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; export class ExtHostProgress implements ExtHostProgressShape { diff --git a/src/vs/workbench/api/node/extHostSCM.ts b/src/vs/workbench/api/node/extHostSCM.ts index f0a48e6c2a5..e0a534bcf21 100644 --- a/src/vs/workbench/api/node/extHostSCM.ts +++ b/src/vs/workbench/api/node/extHostSCM.ts @@ -8,7 +8,6 @@ import { Event, Emitter } from 'vs/base/common/event'; import { debounce } from 'vs/base/common/decorators'; 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, CommandDto } from './extHost.protocol'; import { sortedDiff } from 'vs/base/common/arrays'; @@ -17,7 +16,7 @@ import * as vscode from 'vscode'; import { ISplice } from 'vs/base/common/sequence'; import { ILogService } from 'vs/platform/log/common/log'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; type ProviderHandle = number; type GroupHandle = number; diff --git a/src/vs/workbench/api/node/extHostTask.ts b/src/vs/workbench/api/node/extHostTask.ts index f0184406cab..7930973a847 100644 --- a/src/vs/workbench/api/node/extHostTask.ts +++ b/src/vs/workbench/api/node/extHostTask.ts @@ -11,7 +11,6 @@ import { asPromise } from 'vs/base/common/async'; import { Event, Emitter } from 'vs/base/common/event'; import { win32 } from 'vs/base/node/processes'; -import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import { MainContext, MainThreadTaskShape, ExtHostTaskShape, IMainContext } from 'vs/workbench/api/node/extHost.protocol'; @@ -32,15 +31,16 @@ import { ExtHostTerminalService, ExtHostTerminal } from 'vs/workbench/api/node/e import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; namespace TaskDefinitionDTO { - export function from(value: vscode.TaskDefinition): TaskDefinitionDTO { + export function from(value: vscode.TaskDefinition): TaskDefinitionDTO | undefined { if (value === undefined || value === null) { return undefined; } return value; } - export function to(value: TaskDefinitionDTO): vscode.TaskDefinition { + export function to(value: TaskDefinitionDTO): vscode.TaskDefinition | undefined { if (value === undefined || value === null) { return undefined; } @@ -49,13 +49,13 @@ namespace TaskDefinitionDTO { } namespace TaskPresentationOptionsDTO { - export function from(value: vscode.TaskPresentationOptions): TaskPresentationOptionsDTO { + export function from(value: vscode.TaskPresentationOptions): TaskPresentationOptionsDTO | undefined { if (value === undefined || value === null) { return undefined; } return value; } - export function to(value: TaskPresentationOptionsDTO): vscode.TaskPresentationOptions { + export function to(value: TaskPresentationOptionsDTO): vscode.TaskPresentationOptions | undefined { if (value === undefined || value === null) { return undefined; } @@ -64,13 +64,13 @@ namespace TaskPresentationOptionsDTO { } namespace ProcessExecutionOptionsDTO { - export function from(value: vscode.ProcessExecutionOptions): ProcessExecutionOptionsDTO { + export function from(value: vscode.ProcessExecutionOptions): ProcessExecutionOptionsDTO | undefined { if (value === undefined || value === null) { return undefined; } return value; } - export function to(value: ProcessExecutionOptionsDTO): vscode.ProcessExecutionOptions { + export function to(value: ProcessExecutionOptionsDTO): vscode.ProcessExecutionOptions | undefined { if (value === undefined || value === null) { return undefined; } @@ -79,11 +79,15 @@ namespace ProcessExecutionOptionsDTO { } namespace ProcessExecutionDTO { - export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO): value is ProcessExecutionDTO { - const candidate = value as ProcessExecutionDTO; - return candidate && !!candidate.process; + export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO | undefined): value is ProcessExecutionDTO { + if (value) { + const candidate = value as ProcessExecutionDTO; + return candidate && !!candidate.process; + } else { + return false; + } } - export function from(value: vscode.ProcessExecution): ProcessExecutionDTO { + export function from(value: vscode.ProcessExecution): ProcessExecutionDTO | undefined { if (value === undefined || value === null) { return undefined; } @@ -96,7 +100,7 @@ namespace ProcessExecutionDTO { } return result; } - export function to(value: ProcessExecutionDTO): types.ProcessExecution { + export function to(value: ProcessExecutionDTO): types.ProcessExecution | undefined { if (value === undefined || value === null) { return undefined; } @@ -105,13 +109,13 @@ namespace ProcessExecutionDTO { } namespace ShellExecutionOptionsDTO { - export function from(value: vscode.ShellExecutionOptions): ShellExecutionOptionsDTO { + export function from(value: vscode.ShellExecutionOptions): ShellExecutionOptionsDTO | undefined { if (value === undefined || value === null) { return undefined; } return value; } - export function to(value: ShellExecutionOptionsDTO): vscode.ShellExecutionOptions { + export function to(value: ShellExecutionOptionsDTO): vscode.ShellExecutionOptions | undefined { if (value === undefined || value === null) { return undefined; } @@ -120,11 +124,15 @@ namespace ShellExecutionOptionsDTO { } namespace ShellExecutionDTO { - export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO): value is ShellExecutionDTO { - const candidate = value as ShellExecutionDTO; - return candidate && (!!candidate.commandLine || !!candidate.command); + export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO | undefined): value is ShellExecutionDTO { + if (value) { + const candidate = value as ShellExecutionDTO; + return candidate && (!!candidate.commandLine || !!candidate.command); + } else { + return false; + } } - export function from(value: vscode.ShellExecution): ShellExecutionDTO { + export function from(value: vscode.ShellExecution): ShellExecutionDTO | undefined { if (value === undefined || value === null) { return undefined; } @@ -141,22 +149,26 @@ namespace ShellExecutionDTO { } return result; } - export function to(value: ShellExecutionDTO): types.ShellExecution { - if (value === undefined || value === null) { + export function to(value: ShellExecutionDTO): types.ShellExecution | undefined { + if (value === undefined || value === null || (value.command === undefined && value.commandLine === undefined)) { return undefined; } if (value.commandLine) { return new types.ShellExecution(value.commandLine, value.options); } else { - return new types.ShellExecution(value.command, value.args ? value.args : [], value.options); + return new types.ShellExecution(value.command!, value.args ? value.args : [], value.options); } } } namespace CustomExecutionDTO { - export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO): value is CustomExecutionDTO { - let candidate = value as CustomExecutionDTO; - return candidate && candidate.customExecution === 'customExecution'; + export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO | undefined): value is CustomExecutionDTO { + if (value) { + let candidate = value as CustomExecutionDTO; + return candidate && candidate.customExecution === 'customExecution'; + } else { + return false; + } } export function from(value: vscode.CustomExecution): CustomExecutionDTO { @@ -173,8 +185,8 @@ namespace TaskHandleDTO { folder = value.scope.uri; } return { - id: value._id, - workspaceFolder: folder + id: value._id!, + workspaceFolder: folder! }; } } @@ -195,7 +207,7 @@ namespace TaskDTO { return result; } - export function from(value: vscode.Task, extension: IExtensionDescription): TaskDTO { + export function from(value: vscode.Task, extension: IExtensionDescription): TaskDTO | undefined { if (value === undefined || value === null) { return undefined; } @@ -207,7 +219,7 @@ namespace TaskDTO { } else if ((value).execution2 && (value).execution2 instanceof types.CustomExecution) { execution = CustomExecutionDTO.from((value).execution2); } - const definition: TaskDefinitionDTO = TaskDefinitionDTO.from(value.definition); + const definition: TaskDefinitionDTO | undefined = TaskDefinitionDTO.from(value.definition); let scope: number | UriComponents; if (value.scope) { if (typeof value.scope === 'number') { @@ -224,7 +236,7 @@ namespace TaskDTO { } const group = (value.group as types.TaskGroup) ? (value.group as types.TaskGroup).id : undefined; const result: TaskDTO = { - _id: (value as types.Task)._id, + _id: (value as types.Task)._id!, definition, name: value.name, source: { @@ -232,7 +244,7 @@ namespace TaskDTO { label: value.source, scope: scope }, - execution, + execution: execution!, isBackground: value.isBackground, group: group, presentationOptions: TaskPresentationOptionsDTO.from(value.presentationOptions), @@ -242,7 +254,7 @@ namespace TaskDTO { }; return result; } - export async function to(value: TaskDTO, workspace: IExtHostWorkspaceProvider): Promise { + export async function to(value: TaskDTO | undefined, workspace: IExtHostWorkspaceProvider): Promise { if (value === undefined || value === null) { return undefined; } @@ -252,7 +264,7 @@ namespace TaskDTO { } else if (ShellExecutionDTO.is(value.execution)) { execution = ShellExecutionDTO.to(value.execution); } - const definition: vscode.TaskDefinition = TaskDefinitionDTO.to(value.definition); + const definition: vscode.TaskDefinition | undefined = TaskDefinitionDTO.to(value.definition); let scope: vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder | undefined; if (value.source) { if (value.source.scope !== undefined) { @@ -268,7 +280,7 @@ namespace TaskDTO { if (!definition || !scope) { return undefined; } - const 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; } @@ -276,7 +288,7 @@ namespace TaskDTO { result.group = types.TaskGroup.from(value.group); } if (value.presentationOptions) { - result.presentationOptions = TaskPresentationOptionsDTO.to(value.presentationOptions); + result.presentationOptions = TaskPresentationOptionsDTO.to(value.presentationOptions)!; } if (value._id) { result._id = value._id; @@ -286,11 +298,11 @@ namespace TaskDTO { } namespace TaskFilterDTO { - export function from(value: vscode.TaskFilter): TaskFilterDTO { + export function from(value: vscode.TaskFilter | undefined): TaskFilterDTO | undefined { return value; } - export function to(value: TaskFilterDTO): vscode.TaskFilter { + export function to(value: TaskFilterDTO): vscode.TaskFilter | undefined { if (!value) { return undefined; } @@ -320,7 +332,11 @@ class TaskExecutionImpl implements vscode.TaskExecution { namespace TaskExecutionDTO { export async function to(value: TaskExecutionDTO, tasks: ExtHostTask, workspaceProvider: IExtHostWorkspaceProvider): Promise { - return new TaskExecutionImpl(tasks, value.id, await TaskDTO.to(value.task, workspaceProvider)); + const task = await TaskDTO.to(value.task, workspaceProvider); + if (!task) { + throw new Error('Unexpected: Task cannot be created.'); + } + return new TaskExecutionImpl(tasks, value.id, task); } export function from(value: vscode.TaskExecution): TaskExecutionDTO { return { @@ -358,7 +374,7 @@ class CustomExecutionData implements IDisposable { } private onDidCloseTerminal(terminal: vscode.Terminal): void { - if (this.terminal === terminal) { + if ((this.terminal === terminal) && this._cancellationSource) { this._cancellationSource.cancel(); } } @@ -624,27 +640,30 @@ export class ExtHostTask implements ExtHostTaskShape { const taskIdPromises: Promise[] = []; const fetchPromise = asPromise(() => handler.provider.provideTasks(CancellationToken.None)).then(value => { const taskDTOs: TaskDTO[] = []; - for (let task of value) { - if (!task.definition || !validTypes[task.definition.type]) { - console.warn(`The task [${task.source}, ${task.name}] uses an undefined task type. The task will be ignored in the future.`); - } + if (value) { + for (let task of value) { + if (!task.definition || !validTypes[task.definition.type]) { + console.warn(`The task [${task.source}, ${task.name}] uses an undefined task type. The task will be ignored in the future.`); + } - const taskDTO: TaskDTO = TaskDTO.from(task, handler.extension); - taskDTOs.push(taskDTO); + const taskDTO: TaskDTO | undefined = TaskDTO.from(task, handler.extension); + if (taskDTO) { + taskDTOs.push(taskDTO); - if (CustomExecutionDTO.is(taskDTO.execution)) { - taskIdPromises.push(new Promise((resolve) => { - // The ID is calculated on the main thread task side, so, let's call into it here. - // We need the task id's pre-computed for custom task executions because when OnDidStartTask - // is invoked, we have to be able to map it back to our data. - this._proxy.$createTaskId(taskDTO).then((taskId) => { - this._providedCustomExecutions.set(taskId, new CustomExecutionData((task).execution2, this._terminalService)); - resolve(); - }); - })); + if (CustomExecutionDTO.is(taskDTO.execution)) { + taskIdPromises.push(new Promise((resolve) => { + // The ID is calculated on the main thread task side, so, let's call into it here. + // We need the task id's pre-computed for custom task executions because when OnDidStartTask + // is invoked, we have to be able to map it back to our data. + this._proxy.$createTaskId(taskDTO).then((taskId) => { + this._providedCustomExecutions.set(taskId, new CustomExecutionData((task).execution2, this._terminalService)); + resolve(); + }); + })); + } + } } } - return { tasks: taskDTOs, extension: handler.extension @@ -664,11 +683,14 @@ export class ExtHostTask implements ExtHostTaskShape { const configProvider = await this._configurationService.getConfigProvider(); const uri: URI = URI.revive(uriComponents); const result = { - process: undefined as string, + process: undefined as string, variables: Object.create(null) }; const workspaceFolder = await this._workspaceProvider.resolveWorkspaceFolder(uri); const workspaceFolders = await this._workspaceProvider.getWorkspaceFolders2(); + if (!workspaceFolders || !workspaceFolder) { + throw new Error('Unexpected: Tasks can only be run in a workspace folder'); + } const resolver = new ExtHostVariableResolverService(workspaceFolders, this._editorService, configProvider); const ws: IWorkspaceFolder = { uri: workspaceFolder.uri, @@ -704,16 +726,24 @@ export class ExtHostTask implements ExtHostTaskShape { private async getTaskExecution(execution: TaskExecutionDTO | string, task?: vscode.Task): Promise { if (typeof execution === 'string') { - return this._taskExecutions.get(execution); + const taskExecution = this._taskExecutions.get(execution); + if (!taskExecution) { + throw new Error('Unexpected: The specified task is missing an execution'); + } + return taskExecution; } - let result: TaskExecutionImpl = this._taskExecutions.get(execution.id); + let result: TaskExecutionImpl | undefined = this._taskExecutions.get(execution.id); if (result) { return result; } - result = new TaskExecutionImpl(this, execution.id, task ? task : await TaskDTO.to(execution.task, this._workspaceProvider)); - this._taskExecutions.set(execution.id, result); - return result; + const taskToCreate = task ? task : await TaskDTO.to(execution.task, this._workspaceProvider); + if (!taskToCreate) { + throw new Error('Unexpected: Task does not exist.'); + } + const createdResult: TaskExecutionImpl = new TaskExecutionImpl(this, execution.id, taskToCreate); + this._taskExecutions.set(execution.id, createdResult); + return createdResult; } private customExecutionComplete(execution: TaskExecutionDTO): void { diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts index 115b263be66..9bb24ef880d 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -74,9 +74,9 @@ export class BaseExtHostTerminal { } export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Terminal { - private _pidPromise: Promise; - private _pidPromiseComplete: (value: number) => any; + private _pidPromise: Promise; private _cols: number | undefined; + private _pidPromiseComplete: ((value: number | undefined) => any) | null; private _rows: number | undefined; private readonly _onData = new Emitter(); @@ -90,7 +90,7 @@ export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Termi constructor( proxy: MainThreadTerminalServiceShape, - private _name: string, + private _name?: string, id?: number, pid?: number ) { @@ -108,7 +108,7 @@ export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Termi shellPath?: string, shellArgs?: string[], cwd?: string | URI, - env?: { [key: string]: string }, + env?: { [key: string]: string | null }, waitOnExit?: boolean, strictEnv?: boolean ): void { @@ -119,7 +119,7 @@ export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Termi } public get name(): string { - return this._name; + return this._name || ''; } public set name(name: string) { @@ -127,7 +127,7 @@ export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Termi } public get dimensions(): vscode.TerminalDimensions | undefined { - if (this._cols === undefined && this._rows === undefined) { + if (this._cols === undefined || this._rows === undefined) { return undefined; } return { @@ -146,7 +146,7 @@ export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Termi return true; } - public get processId(): Promise { + public get processId(): Promise { return this._pidPromise; } @@ -165,7 +165,7 @@ export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Termi this._queueApiRequest(this._proxy.$hide, []); } - public _setProcessId(processId: number): void { + public _setProcessId(processId: number | undefined): void { // The event may fire 2 times when the panel is restored if (this._pidPromiseComplete) { this._pidPromiseComplete(processId); @@ -203,15 +203,15 @@ export class ExtHostTerminalRenderer extends BaseExtHostTerminal implements vsco } private _dimensions: vscode.TerminalDimensions | undefined; - public get dimensions(): vscode.TerminalDimensions { return this._dimensions; } - public set dimensions(dimensions: vscode.TerminalDimensions) { + public get dimensions(): vscode.TerminalDimensions | undefined { return this._dimensions; } + public set dimensions(dimensions: vscode.TerminalDimensions | undefined) { this._checkDisposed(); this._dimensions = dimensions; this._queueApiRequest(this._proxy.$terminalRendererSetDimensions, [dimensions]); } - private _maximumDimensions: vscode.TerminalDimensions; - public get maximumDimensions(): vscode.TerminalDimensions { + private _maximumDimensions: vscode.TerminalDimensions | undefined; + public get maximumDimensions(): vscode.TerminalDimensions | undefined { if (!this._maximumDimensions) { return undefined; } @@ -259,20 +259,21 @@ export class ExtHostTerminalRenderer extends BaseExtHostTerminal implements vsco if (this._maximumDimensions && this._maximumDimensions.columns === columns && this._maximumDimensions.rows === rows) { return; } - this._maximumDimensions = { columns, rows }; - this._onDidChangeMaximumDimensions.fire(this.maximumDimensions); + const newValue = { columns, rows }; + this._maximumDimensions = newValue; + this._onDidChangeMaximumDimensions.fire(newValue); } } export class ExtHostTerminalService implements ExtHostTerminalServiceShape { private _proxy: MainThreadTerminalServiceShape; - private _activeTerminal: ExtHostTerminal; + private _activeTerminal: ExtHostTerminal | undefined; private _terminals: ExtHostTerminal[] = []; private _terminalProcesses: { [id: number]: TerminalProcess } = {}; private _terminalRenderers: ExtHostTerminalRenderer[] = []; private _getTerminalPromises: { [id: number]: Promise } = {}; - public get activeTerminal(): ExtHostTerminal { return this._activeTerminal; } + public get activeTerminal(): ExtHostTerminal | undefined { return this._activeTerminal; } public get terminals(): ExtHostTerminal[] { return this._terminals; } private readonly _onDidCloseTerminal: Emitter = new Emitter(); @@ -326,6 +327,9 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { } const terminal = this._getTerminalById(id); + if (!terminal) { + throw new Error(`Cannot resolve terminal renderer for terminal id ${id}`); + } const renderer = new ExtHostTerminalRenderer(this._proxy, terminal.name, terminal, terminal._id); this._terminalRenderers.push(renderer); @@ -339,6 +343,7 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { if (original !== this._activeTerminal) { this._onDidChangeActiveTerminal.fire(this._activeTerminal); } + return; } this._performTerminalIdAction(id, terminal => { if (terminal) { @@ -364,7 +369,7 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { if (terminal.setDimensions(cols, rows)) { this._onDidChangeTerminalDimensions.fire({ terminal: terminal, - dimensions: terminal.dimensions + dimensions: terminal.dimensions as vscode.TerminalDimensions }); } } @@ -449,8 +454,8 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { // this._configHelper.mergeDefaultShellPathAndArgs(shellLaunchConfig); const platformKey = platform.isWindows ? 'windows' : platform.isMacintosh ? 'osx' : 'linux'; - const shellConfigValue: string = terminalConfig.get(`shell.${platformKey}`); - const shellArgsConfigValue: string = terminalConfig.get(`shellArgs.${platformKey}`); + const shellConfigValue: string | undefined = terminalConfig.get(`shell.${platformKey}`); + const shellArgsConfigValue: string | undefined = terminalConfig.get(`shellArgs.${platformKey}`); shellLaunchConfig.executable = shellConfigValue; shellLaunchConfig.args = shellArgsConfigValue; @@ -470,20 +475,24 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { // Merge process env with the env from config const env = { ...process.env }; - terminalEnvironment.mergeEnvironments(env, envFromConfig); - terminalEnvironment.mergeEnvironments(env, shellLaunchConfig.env); + Object.keys(env).filter(k => env[k] === undefined).forEach(k => { + delete env[k]; + }); + const castedEnv = env as platform.IProcessEnvironment; + terminalEnvironment.mergeEnvironments(castedEnv, envFromConfig); + terminalEnvironment.mergeEnvironments(castedEnv, shellLaunchConfig.env); // Sanitize the environment, removing any undesirable VS Code and Electron environment // variables - sanitizeProcessEnvironment(env, 'VSCODE_IPC_HOOK_CLI'); + sanitizeProcessEnvironment(castedEnv, 'VSCODE_IPC_HOOK_CLI'); // Continue env initialization, merging in the env from the launch // config and adding keys that are needed to create the process - terminalEnvironment.addTerminalEnvironmentKeys(env, pkg.version, platform.locale, terminalConfig.get('setLocaleVariables')); + terminalEnvironment.addTerminalEnvironmentKeys(castedEnv, pkg.version, platform.locale, terminalConfig.get('setLocaleVariables') as boolean); // Fork the process and listen for messages - this._logService.debug(`Terminal process launching on ext host`, shellLaunchConfig, initialCwd, cols, rows, env); - const p = new TerminalProcess(shellLaunchConfig, initialCwd, cols, rows, env, terminalConfig.get('windowsEnableConpty')); + this._logService.debug(`Terminal process launching on ext host`, shellLaunchConfig, initialCwd, cols, rows, castedEnv); + const p = new TerminalProcess(shellLaunchConfig, initialCwd, cols, rows, castedEnv, terminalConfig.get('windowsEnableConpty') as boolean); p.onProcessIdReady(pid => this._proxy.$sendProcessPid(id, pid)); p.onProcessTitleChanged(title => this._proxy.$sendProcessTitle(id, title)); p.onProcessData(data => this._proxy.$sendProcessData(id, data)); @@ -560,20 +569,20 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { }); } - private _getTerminalById(id: number): ExtHostTerminal { + private _getTerminalById(id: number): ExtHostTerminal | null { return this._getTerminalObjectById(this._terminals, id); } - private _getTerminalRendererById(id: number): ExtHostTerminalRenderer { + private _getTerminalRendererById(id: number): ExtHostTerminalRenderer | null { return this._getTerminalObjectById(this._terminalRenderers, id); } - private _getTerminalObjectById(array: T[], id: number): T { + private _getTerminalObjectById(array: T[], id: number): T | null { const index = this._getTerminalObjectIndexById(array, id); return index !== null ? array[index] : null; } - private _getTerminalObjectIndexById(array: T[], id: number): number { + private _getTerminalObjectIndexById(array: T[], id: number): number | null { let index: number | null = null; array.some((item, i) => { const thisId = item._id; diff --git a/src/vs/workbench/api/node/extHostTreeViews.ts b/src/vs/workbench/api/node/extHostTreeViews.ts index b135cd62d8d..c91d8afeb43 100644 --- a/src/vs/workbench/api/node/extHostTreeViews.ts +++ b/src/vs/workbench/api/node/extHostTreeViews.ts @@ -17,7 +17,8 @@ import { TreeItemCollapsibleState, ThemeIcon, MarkdownString } from 'vs/workbenc import { isUndefinedOrNull, isString } from 'vs/base/common/types'; import { equals, coalesce } from 'vs/base/common/arrays'; import { ILogService } from 'vs/platform/log/common/log'; -import { IExtensionDescription, checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; +import { checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; type TreeItemHandle = string; diff --git a/src/vs/workbench/api/node/extHostWebview.ts b/src/vs/workbench/api/node/extHostWebview.ts index d5e5d2264db..368ce2439cc 100644 --- a/src/vs/workbench/api/node/extHostWebview.ts +++ b/src/vs/workbench/api/node/extHostWebview.ts @@ -10,7 +10,7 @@ import { EditorViewColumn } from 'vs/workbench/api/shared/editor'; import * as vscode from 'vscode'; import { ExtHostWebviewsShape, IMainContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelViewState, WebviewInsetHandle } from './extHost.protocol'; import { Disposable } from './extHostTypes'; -import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; type IconPath = URI | { light: URI, dark: URI }; diff --git a/src/vs/workbench/api/node/extHostWorkspace.ts b/src/vs/workbench/api/node/extHostWorkspace.ts index b11c7f6f2ae..6408931e2bd 100644 --- a/src/vs/workbench/api/node/extHostWorkspace.ts +++ b/src/vs/workbench/api/node/extHostWorkspace.ts @@ -20,10 +20,9 @@ import { IRawFileMatch2, resultIsMatch } from 'vs/workbench/services/search/comm import { Workspace, WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { Range, RelativePattern } from 'vs/workbench/api/node/extHostTypes'; import { ITextQueryBuilderOptions } from 'vs/workbench/contrib/search/common/queryBuilder'; -import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import * as vscode from 'vscode'; import { ExtHostWorkspaceShape, IWorkspaceData, MainThreadMessageServiceShape, MainThreadWorkspaceShape, IMainContext, MainContext, IStaticWorkspaceData } from './extHost.protocol'; -import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { Barrier } from 'vs/base/common/async'; export interface IExtHostWorkspaceProvider { diff --git a/src/vs/workbench/api/shared/tasks.ts b/src/vs/workbench/api/shared/tasks.ts index 0abf582d133..656bc4ab864 100644 --- a/src/vs/workbench/api/shared/tasks.ts +++ b/src/vs/workbench/api/shared/tasks.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { UriComponents } from 'vs/base/common/uri'; -import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; export interface TaskDefinitionDTO { type: string; @@ -84,7 +84,7 @@ export interface TaskHandleDTO { export interface TaskDTO { _id: string; name?: string; - execution: ProcessExecutionDTO | ShellExecutionDTO | CustomExecutionDTO; + execution: ProcessExecutionDTO | ShellExecutionDTO | CustomExecutionDTO | undefined; definition: TaskDefinitionDTO; isBackground?: boolean; source: TaskSourceDTO; @@ -102,7 +102,7 @@ export interface TaskSetDTO { export interface TaskExecutionDTO { id: string; - task: TaskDTO; + task: TaskDTO | undefined; } export interface TaskProcessStartedDTO { diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts new file mode 100644 index 00000000000..85c35890384 --- /dev/null +++ b/src/vs/workbench/browser/layout.ts @@ -0,0 +1,1141 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; +import { Event, Emitter } from 'vs/base/common/event'; +import { EventType, addDisposableListener, addClass, removeClass, isAncestor, getClientArea, position, size } from 'vs/base/browser/dom'; +import { onDidChangeFullscreen, isFullscreen, getZoomFactor } from 'vs/base/browser/browser'; +import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { isWindows, isLinux, isMacintosh } from 'vs/base/common/platform'; +import { IResourceInput } from 'vs/platform/editor/common/editor'; +import { IUntitledResourceInput, IResourceDiffInput } from 'vs/workbench/common/editor'; +import { SidebarPart } from 'vs/workbench/browser/parts/sidebar/sidebarPart'; +import { PanelPart } from 'vs/workbench/browser/parts/panel/panelPart'; +import { PanelRegistry, Extensions as PanelExtensions } from 'vs/workbench/browser/panel'; +import { Position, Parts, IWorkbenchLayoutService, ILayoutOptions } from 'vs/workbench/services/layout/browser/layoutService'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; +import { IStorageService, StorageScope, IWillSaveStateEvent, WillSaveStateReason } from 'vs/platform/storage/common/storage'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; +import { ITitleService } from 'vs/workbench/services/title/common/titleService'; +import { IInstantiationService, ServicesAccessor, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; +import { LifecyclePhase, StartupKind, ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { IWindowService, IPath, MenuBarVisibility, getTitleBarStyle } from 'vs/platform/windows/common/windows'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IEditorService, IResourceEditor } from 'vs/workbench/services/editor/common/editorService'; +import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { Sizing, Direction, Grid, View } from 'vs/base/browser/ui/grid/grid'; +import { WorkbenchLegacyLayout } from 'vs/workbench/browser/legacyLayout'; +import { IDimension } from 'vs/platform/layout/browser/layoutService'; +import { Part } from 'vs/workbench/browser/part'; +import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar'; +import { IActivityBarService } from 'vs/workbench/services/activityBar/browser/activityBarService'; +import { coalesce } from 'vs/base/common/arrays'; + +enum Settings { + MENUBAR_VISIBLE = 'window.menuBarVisibility', + ACTIVITYBAR_VISIBLE = 'workbench.activityBar.visible', + STATUSBAR_VISIBLE = 'workbench.statusBar.visible', + + SIDEBAR_POSITION = 'workbench.sideBar.location', + PANEL_POSITION = 'workbench.panel.defaultLocation', + + ZEN_MODE_RESTORE = 'zenMode.restore' +} + +enum Storage { + SIDEBAR_HIDDEN = 'workbench.sidebar.hidden', + + PANEL_HIDDEN = 'workbench.panel.hidden', + PANEL_POSITION = 'workbench.panel.location', + + ZEN_MODE_ENABLED = 'workbench.zenmode.active', + CENTERED_LAYOUT_ENABLED = 'workbench.centerededitorlayout.active', +} + +export abstract class Layout extends Disposable implements IWorkbenchLayoutService { + + _serviceBrand: ServiceIdentifier; + + private readonly _onTitleBarVisibilityChange: Emitter = this._register(new Emitter()); + get onTitleBarVisibilityChange(): Event { return this._onTitleBarVisibilityChange.event; } + + private readonly _onZenMode: Emitter = this._register(new Emitter()); + get onZenModeChange(): Event { return this._onZenMode.event; } + + private readonly _onLayout = this._register(new Emitter()); + get onLayout(): Event { return this._onLayout.event; } + + private _dimension: IDimension; + get dimension(): IDimension { return this._dimension; } + + private _container: HTMLElement = document.createElement('div'); + get container(): HTMLElement { return this._container; } + + get hasWorkbench(): boolean { return true; } + + private parts: Map = new Map(); + + private workbenchGrid: Grid | WorkbenchLegacyLayout; + + private disposed: boolean; + + private titleBarPartView: View; + private activityBarPartView: View; + private sideBarPartView: View; + private panelPartView: View; + private editorPartView: View; + private statusBarPartView: View; + + private environmentService: IEnvironmentService; + private configurationService: IConfigurationService; + private lifecycleService: ILifecycleService; + private storageService: IStorageService; + private windowService: IWindowService; + private editorService: IEditorService; + private editorGroupService: IEditorGroupsService; + private panelService: IPanelService; + private titleService: ITitleService; + private viewletService: IViewletService; + private contextService: IWorkspaceContextService; + private backupFileService: IBackupFileService; + + protected readonly state = { + fullscreen: false, + + menuBar: { + visibility: 'default' as MenuBarVisibility, + toggled: false + }, + + activityBar: { + hidden: false + }, + + sideBar: { + hidden: false, + position: Position.LEFT, + width: 300, + viewletToRestore: undefined as string | undefined + }, + + editor: { + hidden: false, + centered: false, + restoreCentered: false, + restoreEditors: false, + editorsToOpen: [] as Promise | IResourceEditor[] + }, + + panel: { + hidden: false, + position: Position.BOTTOM, + height: 350, + width: 350, + panelToRestore: undefined as string | undefined + }, + + statusBar: { + hidden: false + }, + + zenMode: { + active: false, + restore: false, + transitionedToFullScreen: false, + transitionedToCenteredEditorLayout: false, + wasSideBarVisible: false, + wasPanelVisible: false, + transitionDisposeables: [] as IDisposable[] + } + }; + + constructor( + protected readonly parent: HTMLElement + ) { + super(); + } + + protected initLayout(accessor: ServicesAccessor): void { + + // Services + this.environmentService = accessor.get(IEnvironmentService); + this.configurationService = accessor.get(IConfigurationService); + this.lifecycleService = accessor.get(ILifecycleService); + this.windowService = accessor.get(IWindowService); + this.contextService = accessor.get(IWorkspaceContextService); + this.storageService = accessor.get(IStorageService); + + // Parts + this.editorService = accessor.get(IEditorService); + this.editorGroupService = accessor.get(IEditorGroupsService); + this.panelService = accessor.get(IPanelService); + this.viewletService = accessor.get(IViewletService); + this.titleService = accessor.get(ITitleService); + accessor.get(IStatusbarService); // not used, but called to ensure instantiated + accessor.get(IActivityBarService); // not used, but called to ensure instantiated + + // Listeners + this.registerLayoutListeners(); + + // State + this.initLayoutState(accessor.get(ILifecycleService)); + } + + private registerLayoutListeners(): void { + + // Storage + this._register(this.storageService.onWillSaveState(e => this.saveLayoutState(e))); + + // Restore editor if hidden and it changes + this._register(this.editorService.onDidVisibleEditorsChange(() => this.setEditorHidden(false))); + this._register(this.editorGroupService.onDidActivateGroup(() => this.setEditorHidden(false))); + + // Configuration changes + this._register(this.configurationService.onDidChangeConfiguration(() => this.doUpdateLayoutConfiguration())); + + // Fullscreen changes + this._register(onDidChangeFullscreen(() => this.onFullscreenChanged())); + + // Group changes + this._register(this.editorGroupService.onDidAddGroup(() => this.centerEditorLayout(this.state.editor.centered))); + this._register(this.editorGroupService.onDidRemoveGroup(() => this.centerEditorLayout(this.state.editor.centered))); + + // Prevent workbench from scrolling #55456 + this._register(addDisposableListener(this.container, EventType.SCROLL, () => this.container.scrollTop = 0)); + + // Menubar visibility changes + if ((isWindows || isLinux) && getTitleBarStyle(this.configurationService, this.environmentService) === 'custom') { + this._register(this.titleService.onMenubarVisibilityChange(visible => this.onMenubarToggled(visible))); + } + } + + private onMenubarToggled(visible: boolean) { + if (visible !== this.state.menuBar.toggled) { + this.state.menuBar.toggled = visible; + + if (this.state.fullscreen && (this.state.menuBar.visibility === 'toggle' || this.state.menuBar.visibility === 'default')) { + this._onTitleBarVisibilityChange.fire(); + this.layout(); + } + } + } + + private onFullscreenChanged(): void { + this.state.fullscreen = isFullscreen(); + + // Apply as CSS class + if (this.state.fullscreen) { + addClass(this.container, 'fullscreen'); + } else { + removeClass(this.container, 'fullscreen'); + + if (this.state.zenMode.transitionedToFullScreen && this.state.zenMode.active) { + this.toggleZenMode(); + } + } + + // Changing fullscreen state of the window has an impact on custom title bar visibility, so we need to update + if (getTitleBarStyle(this.configurationService, this.environmentService) === 'custom') { + this._onTitleBarVisibilityChange.fire(); + this.layout(); // handle title bar when fullscreen changes + } + } + + private doUpdateLayoutConfiguration(skipLayout?: boolean): void { + + // Sidebar position + const newSidebarPositionValue = this.configurationService.getValue(Settings.SIDEBAR_POSITION); + const newSidebarPosition = (newSidebarPositionValue === 'right') ? Position.RIGHT : Position.LEFT; + if (newSidebarPosition !== this.getSideBarPosition()) { + this.setSideBarPosition(newSidebarPosition); + } + + // Panel position + this.updatePanelPosition(); + + if (!this.state.zenMode.active) { + + // Statusbar visibility + const newStatusbarHiddenValue = !this.configurationService.getValue(Settings.STATUSBAR_VISIBLE); + if (newStatusbarHiddenValue !== this.state.statusBar.hidden) { + this.setStatusBarHidden(newStatusbarHiddenValue, skipLayout); + } + + // Activitybar visibility + const newActivityBarHiddenValue = !this.configurationService.getValue(Settings.ACTIVITYBAR_VISIBLE); + if (newActivityBarHiddenValue !== this.state.activityBar.hidden) { + this.setActivityBarHidden(newActivityBarHiddenValue, skipLayout); + } + } + + // Menubar visibility + const newMenubarVisibility = this.configurationService.getValue(Settings.MENUBAR_VISIBLE); + this.setMenubarVisibility(newMenubarVisibility, !!skipLayout); + } + + private setSideBarPosition(position: Position): void { + const activityBar = this.getPart(Parts.ACTIVITYBAR_PART); + const sideBar = this.getPart(Parts.SIDEBAR_PART); + const wasHidden = this.state.sideBar.hidden; + + if (this.state.sideBar.hidden) { + this.setSideBarHidden(false, true /* Skip Layout */); + } + + const newPositionValue = (position === Position.LEFT) ? 'left' : 'right'; + const oldPositionValue = (this.state.sideBar.position === Position.LEFT) ? 'left' : 'right'; + this.state.sideBar.position = position; + + // Adjust CSS + removeClass(activityBar.getContainer(), oldPositionValue); + removeClass(sideBar.getContainer(), oldPositionValue); + addClass(activityBar.getContainer(), newPositionValue); + addClass(sideBar.getContainer(), newPositionValue); + + // Update Styles + activityBar.updateStyles(); + sideBar.updateStyles(); + + // Layout + if (this.workbenchGrid instanceof Grid) { + if (!wasHidden) { + this.state.sideBar.width = this.workbenchGrid.getViewSize(this.sideBarPartView); + } + + this.workbenchGrid.removeView(this.sideBarPartView); + this.workbenchGrid.removeView(this.activityBarPartView); + + if (!this.state.panel.hidden && this.state.panel.position === Position.BOTTOM) { + this.workbenchGrid.removeView(this.panelPartView); + } + + this.layout(); + } else { + this.workbenchGrid.layout(); + } + } + + private initLayoutState(lifecycleService: ILifecycleService): void { + + // Fullscreen + this.state.fullscreen = isFullscreen(); + + // Menubar visibility + this.state.menuBar.visibility = this.configurationService.getValue(Settings.MENUBAR_VISIBLE); + + // Activity bar visibility + this.state.activityBar.hidden = !this.configurationService.getValue(Settings.ACTIVITYBAR_VISIBLE); + + // Sidebar visibility + this.state.sideBar.hidden = this.storageService.getBoolean(Storage.SIDEBAR_HIDDEN, StorageScope.WORKSPACE, this.contextService.getWorkbenchState() === WorkbenchState.EMPTY); + + // Sidebar position + this.state.sideBar.position = (this.configurationService.getValue(Settings.SIDEBAR_POSITION) === 'right') ? Position.RIGHT : Position.LEFT; + + // Sidebar viewlet + if (!this.state.sideBar.hidden) { + + // Only restore last viewlet if window was reloaded or we are in development mode + let viewletToRestore: string; + if (!this.environmentService.isBuilt || lifecycleService.startupKind === StartupKind.ReloadedWindow) { + viewletToRestore = this.storageService.get(SidebarPart.activeViewletSettingsKey, StorageScope.WORKSPACE, this.viewletService.getDefaultViewletId()); + } else { + viewletToRestore = this.viewletService.getDefaultViewletId(); + } + + if (viewletToRestore) { + this.state.sideBar.viewletToRestore = viewletToRestore; + } else { + this.state.sideBar.hidden = true; // we hide sidebar if there is no viewlet to restore + } + } + + // Editor centered layout + this.state.editor.restoreCentered = this.storageService.getBoolean(Storage.CENTERED_LAYOUT_ENABLED, StorageScope.WORKSPACE, false); + + // Editors to open + this.state.editor.editorsToOpen = this.resolveEditorsToOpen(); + + // Panel visibility + this.state.panel.hidden = this.storageService.getBoolean(Storage.PANEL_HIDDEN, StorageScope.WORKSPACE, true); + + // Panel position + this.updatePanelPosition(); + + // Panel to restore + if (!this.state.panel.hidden) { + const panelRegistry = Registry.as(PanelExtensions.Panels); + + let panelToRestore = this.storageService.get(PanelPart.activePanelSettingsKey, StorageScope.WORKSPACE, panelRegistry.getDefaultPanelId()); + if (!panelRegistry.hasPanel(panelToRestore)) { + panelToRestore = panelRegistry.getDefaultPanelId(); // fallback to default if panel is unknown + } + + if (panelToRestore) { + this.state.panel.panelToRestore = panelToRestore; + } else { + this.state.panel.hidden = true; // we hide panel if there is no panel to restore + } + } + + // Statusbar visibility + this.state.statusBar.hidden = !this.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); + } + + private resolveEditorsToOpen(): Promise | IResourceEditor[] { + const configuration = this.windowService.getConfiguration(); + const hasInitialFilesToOpen = this.hasInitialFilesToOpen(); + + // Only restore editors if we are not instructed to open files initially + this.state.editor.restoreEditors = !hasInitialFilesToOpen; + + // Files to open, diff or create + if (hasInitialFilesToOpen) { + + // Files to diff is exclusive + const filesToDiff = this.toInputs(configuration.filesToDiff, false); + if (filesToDiff && filesToDiff.length === 2) { + return [{ + leftResource: filesToDiff[0].resource, + rightResource: filesToDiff[1].resource, + options: { pinned: true }, + forceFile: true + }]; + } + + 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; + if (!isEmpty) { + return []; // do not open any empty untitled file if we restored editors from previous session + } + + return this.backupFileService.hasBackups().then(hasBackups => { + if (hasBackups) { + return []; // do not open any empty untitled file if we have backups to restore + } + + return [{}]; + }); + } + + return []; + } + + private hasInitialFilesToOpen(): boolean { + const configuration = this.windowService.getConfiguration(); + + return !!( + (configuration.filesToCreate && configuration.filesToCreate.length > 0) || + (configuration.filesToOpen && configuration.filesToOpen.length > 0) || + (configuration.filesToDiff && configuration.filesToDiff.length > 0)); + } + + private toInputs(paths: IPath[] | undefined, isNew: boolean): Array { + if (!paths || !paths.length) { + return []; + } + + return coalesce(paths.map(p => { + const resource = p.fileUri; + if (!resource) { + return undefined; + } + + let input: IResourceInput | IUntitledResourceInput; + if (isNew) { + input = { filePath: resource.fsPath, options: { pinned: true } } as IUntitledResourceInput; + } else { + input = { resource, options: { pinned: true }, forceFile: true } as IResourceInput; + } + + if (!isNew && typeof p.lineNumber === 'number') { + input.options!.selection = { + startLineNumber: p.lineNumber, + startColumn: p.columnNumber || 1 + }; + } + + return input; + })); + } + + private updatePanelPosition() { + const defaultPanelPosition = this.configurationService.getValue(Settings.PANEL_POSITION); + const panelPosition = this.storageService.get(Storage.PANEL_POSITION, StorageScope.WORKSPACE, defaultPanelPosition); + + this.state.panel.position = (panelPosition === 'right') ? Position.RIGHT : Position.BOTTOM; + } + + registerPart(part: Part): void { + this.parts.set(part.getId(), part); + } + + protected getPart(key: Parts): Part { + const part = this.parts.get(key); + if (!part) { + throw new Error(`Unknown part ${key}`); + } + + return part; + } + + isRestored(): boolean { + return this.lifecycleService.phase >= LifecyclePhase.Restored; + } + + hasFocus(part: Parts): boolean { + const activeElement = document.activeElement; + if (!activeElement) { + return false; + } + + const container = this.getContainer(part); + + return isAncestor(activeElement, container); + } + + getContainer(part: Parts): HTMLElement { + switch (part) { + case Parts.TITLEBAR_PART: + return this.getPart(Parts.TITLEBAR_PART).getContainer(); + case Parts.ACTIVITYBAR_PART: + return this.getPart(Parts.ACTIVITYBAR_PART).getContainer(); + case Parts.SIDEBAR_PART: + return this.getPart(Parts.SIDEBAR_PART).getContainer(); + case Parts.PANEL_PART: + return this.getPart(Parts.PANEL_PART).getContainer(); + case Parts.EDITOR_PART: + return this.getPart(Parts.EDITOR_PART).getContainer(); + case Parts.STATUSBAR_PART: + return this.getPart(Parts.STATUSBAR_PART).getContainer(); + } + } + + isVisible(part: Parts): boolean { + switch (part) { + case Parts.TITLEBAR_PART: + if (getTitleBarStyle(this.configurationService, this.environmentService) === 'native') { + return false; + } else if (!this.state.fullscreen) { + return true; + } else if (isMacintosh) { + return false; + } else if (this.state.menuBar.visibility === 'visible') { + return true; + } else if (this.state.menuBar.visibility === 'toggle' || this.state.menuBar.visibility === 'default') { + return this.state.menuBar.toggled; + } + + return false; + case Parts.SIDEBAR_PART: + return !this.state.sideBar.hidden; + case Parts.PANEL_PART: + return !this.state.panel.hidden; + case Parts.STATUSBAR_PART: + return !this.state.statusBar.hidden; + case Parts.ACTIVITYBAR_PART: + return !this.state.activityBar.hidden; + case Parts.EDITOR_PART: + return this.workbenchGrid instanceof Grid ? !this.state.editor.hidden : true; + } + + return true; // any other part cannot be hidden + } + + getTitleBarOffset(): number { + let offset = 0; + if (this.isVisible(Parts.TITLEBAR_PART)) { + if (this.workbenchGrid instanceof Grid) { + offset = this.getPart(Parts.TITLEBAR_PART).maximumHeight; + } else { + offset = this.workbenchGrid.partLayoutInfo.titlebar.height; + + if (isMacintosh || this.state.menuBar.visibility === 'hidden') { + offset /= getZoomFactor(); + } + } + } + + return offset; + } + + getWorkbenchElement(): HTMLElement { + return this.container; + } + + toggleZenMode(skipLayout?: boolean, restoring = false): void { + this.state.zenMode.active = !this.state.zenMode.active; + this.state.zenMode.transitionDisposeables = dispose(this.state.zenMode.transitionDisposeables); + + const setLineNumbers = (lineNumbers: any) => this.editorService.visibleTextEditorWidgets.forEach(editor => editor.updateOptions({ lineNumbers })); + + // Check if zen mode transitioned to full screen and if now we are out of zen mode + // -> we need to go out of full screen (same goes for the centered editor layout) + let toggleFullScreen = false; + + // Zen Mode Active + if (this.state.zenMode.active) { + const config: { + fullScreen: boolean; + centerLayout: boolean; + hideTabs: boolean; + hideActivityBar: boolean; + hideStatusBar: boolean; + hideLineNumbers: boolean; + } = this.configurationService.getValue('zenMode'); + + toggleFullScreen = !this.state.fullscreen && config.fullScreen; + + this.state.zenMode.transitionedToFullScreen = restoring ? config.fullScreen : toggleFullScreen; + this.state.zenMode.transitionedToCenteredEditorLayout = !this.isEditorLayoutCentered() && config.centerLayout; + this.state.zenMode.wasSideBarVisible = this.isVisible(Parts.SIDEBAR_PART); + this.state.zenMode.wasPanelVisible = this.isVisible(Parts.PANEL_PART); + + this.setPanelHidden(true, true); + this.setSideBarHidden(true, true); + + if (config.hideActivityBar) { + this.setActivityBarHidden(true, true); + } + + if (config.hideStatusBar) { + this.setStatusBarHidden(true, true); + } + + if (config.hideLineNumbers) { + setLineNumbers('off'); + this.state.zenMode.transitionDisposeables.push(this.editorService.onDidVisibleEditorsChange(() => setLineNumbers('off'))); + } + + if (config.hideTabs && this.editorGroupService.partOptions.showTabs) { + this.state.zenMode.transitionDisposeables.push(this.editorGroupService.enforcePartOptions({ showTabs: false })); + } + + if (config.centerLayout) { + this.centerEditorLayout(true, true); + } + } + + // Zen Mode Inactive + else { + if (this.state.zenMode.wasPanelVisible) { + this.setPanelHidden(false, true); + } + + if (this.state.zenMode.wasSideBarVisible) { + this.setSideBarHidden(false, true); + } + + if (this.state.zenMode.transitionedToCenteredEditorLayout) { + this.centerEditorLayout(false, true); + } + + setLineNumbers(this.configurationService.getValue('editor.lineNumbers')); + + // Status bar and activity bar visibility come from settings -> update their visibility. + this.doUpdateLayoutConfiguration(true); + + this.editorGroupService.activeGroup.focus(); + + toggleFullScreen = this.state.zenMode.transitionedToFullScreen && this.state.fullscreen; + } + + if (!skipLayout) { + this.layout(); + } + + if (toggleFullScreen) { + this.windowService.toggleFullScreen(); + } + + // Event + this._onZenMode.fire(this.state.zenMode.active); + } + + private setStatusBarHidden(hidden: boolean, skipLayout?: boolean): void { + this.state.statusBar.hidden = hidden; + + // Adjust CSS + if (hidden) { + addClass(this.container, 'nostatusbar'); + } else { + removeClass(this.container, 'nostatusbar'); + } + + // Layout + if (!skipLayout) { + if (this.workbenchGrid instanceof Grid) { + this.layout(); + } else { + this.workbenchGrid.layout(); + } + } + } + + protected createWorkbenchLayout(instantiationService: IInstantiationService): void { + const titleBar = this.getPart(Parts.TITLEBAR_PART); + const editorPart = this.getPart(Parts.EDITOR_PART); + const activityBar = this.getPart(Parts.ACTIVITYBAR_PART); + const panelPart = this.getPart(Parts.PANEL_PART); + const sideBar = this.getPart(Parts.SIDEBAR_PART); + const statusBar = this.getPart(Parts.STATUSBAR_PART); + + if (this.configurationService.getValue('workbench.useExperimentalGridLayout')) { + + // Create view wrappers for all parts + this.titleBarPartView = new View(titleBar); + this.sideBarPartView = new View(sideBar); + this.activityBarPartView = new View(activityBar); + this.editorPartView = new View(editorPart); + this.panelPartView = new View(panelPart); + this.statusBarPartView = new View(statusBar); + + this.workbenchGrid = new Grid(this.editorPartView, { proportionalLayout: false }); + + this.container.prepend(this.workbenchGrid.element); + } else { + this.workbenchGrid = instantiationService.createInstance( + WorkbenchLegacyLayout, + this.parent, + this.container, + { + titlebar: titleBar, + activitybar: activityBar, + editor: editorPart, + sidebar: sideBar, + panel: panelPart, + statusbar: statusBar, + } + ); + } + } + + layout(options?: ILayoutOptions): void { + if (!this.disposed) { + this._dimension = getClientArea(this.parent); + + if (this.workbenchGrid instanceof Grid) { + position(this.container, 0, 0, 0, 0, 'relative'); + size(this.container, this._dimension.width, this._dimension.height); + + // Layout the grid widget + this.workbenchGrid.layout(this._dimension.width, this._dimension.height); + + // Layout grid views + this.layoutGrid(); + } else { + this.workbenchGrid.layout(options); + } + + // Emit as event + this._onLayout.fire(this._dimension); + } + } + + private layoutGrid(): void { + if (!(this.workbenchGrid instanceof Grid)) { + return; + } + + let panelInGrid = this.workbenchGrid.hasView(this.panelPartView); + let sidebarInGrid = this.workbenchGrid.hasView(this.sideBarPartView); + let activityBarInGrid = this.workbenchGrid.hasView(this.activityBarPartView); + let statusBarInGrid = this.workbenchGrid.hasView(this.statusBarPartView); + let titlebarInGrid = this.workbenchGrid.hasView(this.titleBarPartView); + + // Add parts to grid + if (!statusBarInGrid) { + this.workbenchGrid.addView(this.statusBarPartView, Sizing.Split, this.editorPartView, Direction.Down); + statusBarInGrid = true; + } + + if (!titlebarInGrid && getTitleBarStyle(this.configurationService, this.environmentService) === 'custom') { + this.workbenchGrid.addView(this.titleBarPartView, Sizing.Split, this.editorPartView, Direction.Up); + titlebarInGrid = true; + } + + if (!activityBarInGrid) { + this.workbenchGrid.addView(this.activityBarPartView, Sizing.Split, panelInGrid && this.state.sideBar.position === this.state.panel.position ? this.panelPartView : this.editorPartView, this.state.sideBar.position === Position.RIGHT ? Direction.Right : Direction.Left); + activityBarInGrid = true; + } + + if (!sidebarInGrid) { + this.workbenchGrid.addView(this.sideBarPartView, this.state.sideBar.width !== undefined ? this.state.sideBar.width : Sizing.Split, this.activityBarPartView, this.state.sideBar.position === Position.LEFT ? Direction.Right : Direction.Left); + sidebarInGrid = true; + } + + if (!panelInGrid) { + this.workbenchGrid.addView(this.panelPartView, this.getPanelDimension(this.state.panel.position) !== undefined ? this.getPanelDimension(this.state.panel.position) : Sizing.Split, this.editorPartView, this.state.panel.position === Position.BOTTOM ? Direction.Down : Direction.Right); + panelInGrid = true; + } + + // Hide parts + if (this.state.panel.hidden) { + this.panelPartView.hide(); + } + + if (this.state.statusBar.hidden) { + this.statusBarPartView.hide(); + } + + if (!this.isVisible(Parts.TITLEBAR_PART)) { + this.titleBarPartView.hide(); + } + + if (this.state.activityBar.hidden) { + this.activityBarPartView.hide(); + } + + if (this.state.sideBar.hidden) { + this.sideBarPartView.hide(); + } + + if (this.state.editor.hidden) { + this.editorPartView.hide(); + } + + // Show visible parts + if (!this.state.editor.hidden) { + this.editorPartView.show(); + } + + if (!this.state.statusBar.hidden) { + this.statusBarPartView.show(); + } + + if (this.isVisible(Parts.TITLEBAR_PART)) { + this.titleBarPartView.show(); + } + + if (!this.state.activityBar.hidden) { + this.activityBarPartView.show(); + } + + if (!this.state.sideBar.hidden) { + this.sideBarPartView.show(); + } + + if (!this.state.panel.hidden) { + this.panelPartView.show(); + } + } + + private getPanelDimension(position: Position): number { + return position === Position.BOTTOM ? this.state.panel.height : this.state.panel.width; + } + + isEditorLayoutCentered(): boolean { + return this.state.editor.centered; + } + + centerEditorLayout(active: boolean, skipLayout?: boolean): void { + this.state.editor.centered = active; + + this.storageService.store(Storage.CENTERED_LAYOUT_ENABLED, active, StorageScope.WORKSPACE); + + let smartActive = active; + if (this.editorGroupService.groups.length > 1 && this.configurationService.getValue('workbench.editor.centeredLayoutAutoResize')) { + smartActive = false; // Respect the auto resize setting - do not go into centered layout if there is more than 1 group. + } + + // Enter Centered Editor Layout + if (this.editorGroupService.isLayoutCentered() !== smartActive) { + this.editorGroupService.centerLayout(smartActive); + + if (!skipLayout) { + this.layout(); + } + } + } + + resizePart(part: Parts, sizeChange: number): void { + let view: View; + switch (part) { + case Parts.SIDEBAR_PART: + view = this.sideBarPartView; + case Parts.PANEL_PART: + view = this.panelPartView; + case Parts.EDITOR_PART: + view = this.editorPartView; + if (this.workbenchGrid instanceof Grid) { + this.workbenchGrid.resizeView(view, this.workbenchGrid.getViewSize(view) + sizeChange); + } else { + this.workbenchGrid.resizePart(part, sizeChange); + } + break; + default: + return; // Cannot resize other parts + } + } + + setActivityBarHidden(hidden: boolean, skipLayout?: boolean): void { + this.state.activityBar.hidden = hidden; + + // Layout + if (!skipLayout) { + if (this.workbenchGrid instanceof Grid) { + this.layout(); + } else { + this.workbenchGrid.layout(); + } + } + } + + setEditorHidden(hidden: boolean, skipLayout?: boolean): void { + if (!(this.workbenchGrid instanceof Grid) || hidden === this.state.editor.hidden) { + return; + } + + this.state.editor.hidden = hidden; + + // The editor and the panel cannot be hidden at the same time + if (this.state.editor.hidden && this.state.panel.hidden) { + this.setPanelHidden(false, true); + } + + if (!skipLayout) { + this.layout(); + } + } + + setSideBarHidden(hidden: boolean, skipLayout?: boolean): void { + this.state.sideBar.hidden = hidden; + + // Adjust CSS + if (hidden) { + addClass(this.container, 'nosidebar'); + } else { + removeClass(this.container, 'nosidebar'); + } + + // If sidebar becomes hidden, also hide the current active Viewlet if any + if (hidden && this.viewletService.getActiveViewlet()) { + this.viewletService.hideActiveViewlet(); + + // Pass Focus to Editor or Panel if Sidebar is now hidden + const activePanel = this.panelService.getActivePanel(); + if (this.hasFocus(Parts.PANEL_PART) && activePanel) { + activePanel.focus(); + } else { + this.editorGroupService.activeGroup.focus(); + } + } + + // If sidebar becomes visible, show last active Viewlet or default viewlet + else if (!hidden && !this.viewletService.getActiveViewlet()) { + const viewletToOpen = this.viewletService.getLastActiveViewletId(); + if (viewletToOpen) { + const viewlet = this.viewletService.openViewlet(viewletToOpen, true); + if (!viewlet) { + this.viewletService.openViewlet(this.viewletService.getDefaultViewletId(), true); + } + } + } + + // Remember in settings + const defaultHidden = this.contextService.getWorkbenchState() === WorkbenchState.EMPTY; + if (hidden !== defaultHidden) { + this.storageService.store(Storage.SIDEBAR_HIDDEN, hidden ? 'true' : 'false', StorageScope.WORKSPACE); + } else { + this.storageService.remove(Storage.SIDEBAR_HIDDEN, StorageScope.WORKSPACE); + } + + // Layout + if (!skipLayout) { + if (this.workbenchGrid instanceof Grid) { + this.layout(); + } else { + this.workbenchGrid.layout(); + } + } + } + + setPanelHidden(hidden: boolean, skipLayout?: boolean): void { + this.state.panel.hidden = hidden; + + // Adjust CSS + if (hidden) { + addClass(this.container, 'nopanel'); + } else { + removeClass(this.container, 'nopanel'); + } + + // If panel part becomes hidden, also hide the current active panel if any + if (hidden && this.panelService.getActivePanel()) { + this.panelService.hideActivePanel(); + this.editorGroupService.activeGroup.focus(); // Pass focus to editor group if panel part is now hidden + } + + // If panel part becomes visible, show last active panel or default panel + else if (!hidden && !this.panelService.getActivePanel()) { + const panelToOpen = this.panelService.getLastActivePanelId(); + if (panelToOpen) { + const focus = !skipLayout; + this.panelService.openPanel(panelToOpen, focus); + } + } + + // Remember in settings + if (!hidden) { + this.storageService.store(Storage.PANEL_HIDDEN, 'false', StorageScope.WORKSPACE); + } else { + this.storageService.remove(Storage.PANEL_HIDDEN, StorageScope.WORKSPACE); + } + + // The editor and panel cannot be hidden at the same time + if (hidden && this.state.editor.hidden) { + this.setEditorHidden(false, true); + } + + // Layout + if (!skipLayout) { + if (this.workbenchGrid instanceof Grid) { + this.layout(); + } else { + this.workbenchGrid.layout(); + } + } + } + + toggleMaximizedPanel(): void { + if (this.workbenchGrid instanceof Grid) { + this.workbenchGrid.maximizeViewSize(this.panelPartView); + } else { + this.workbenchGrid.layout({ toggleMaximizedPanel: true, source: Parts.PANEL_PART }); + } + } + + isPanelMaximized(): boolean { + if (this.workbenchGrid instanceof Grid) { + try { + return this.workbenchGrid.getViewSize2(this.panelPartView).height === this.getPart(Parts.PANEL_PART).maximumHeight; + } catch (e) { + return false; + } + } else { + return this.workbenchGrid.isPanelMaximized(); + } + } + + getSideBarPosition(): Position { + return this.state.sideBar.position; + } + + setMenubarVisibility(visibility: MenuBarVisibility, skipLayout: boolean): void { + if (this.state.menuBar.visibility !== visibility) { + this.state.menuBar.visibility = visibility; + + // Layout + if (!skipLayout) { + if (this.workbenchGrid instanceof Grid) { + const dimensions = getClientArea(this.parent); + this.workbenchGrid.layout(dimensions.width, dimensions.height); + } else { + this.workbenchGrid.layout(); + } + } + } + } + + getMenubarVisibility(): MenuBarVisibility { + return this.state.menuBar.visibility; + } + + getPanelPosition(): Position { + return this.state.panel.position; + } + + setPanelPosition(position: Position): void { + const panelPart = this.getPart(Parts.PANEL_PART); + const wasHidden = this.state.panel.hidden; + + if (this.state.panel.hidden) { + this.setPanelHidden(false, true /* Skip Layout */); + } else { + this.savePanelDimension(); + } + + const newPositionValue = (position === Position.BOTTOM) ? 'bottom' : 'right'; + const oldPositionValue = (this.state.panel.position === Position.BOTTOM) ? 'bottom' : 'right'; + this.state.panel.position = position; + + function positionToString(position: Position): string { + switch (position) { + case Position.LEFT: return 'left'; + case Position.RIGHT: return 'right'; + case Position.BOTTOM: return 'bottom'; + } + } + + this.storageService.store(Storage.PANEL_POSITION, positionToString(this.state.panel.position), StorageScope.WORKSPACE); + + // Adjust CSS + removeClass(panelPart.getContainer(), oldPositionValue); + addClass(panelPart.getContainer(), newPositionValue); + + // Update Styles + panelPart.updateStyles(); + + // Layout + if (this.workbenchGrid instanceof Grid) { + if (!wasHidden) { + this.savePanelDimension(); + } + + this.workbenchGrid.removeView(this.panelPartView); + this.layout(); + } else { + this.workbenchGrid.layout(); + } + } + + private savePanelDimension(): void { + if (!(this.workbenchGrid instanceof Grid)) { + return; + } + + if (this.state.panel.position === Position.BOTTOM) { + this.state.panel.height = this.workbenchGrid.getViewSize(this.panelPartView); + } else { + this.state.panel.width = this.workbenchGrid.getViewSize(this.panelPartView); + } + } + + private saveLayoutState(e: IWillSaveStateEvent): void { + + // Zen Mode + if (this.state.zenMode.active) { + this.storageService.store(Storage.ZEN_MODE_ENABLED, true, StorageScope.WORKSPACE); + } else { + this.storageService.remove(Storage.ZEN_MODE_ENABLED, StorageScope.WORKSPACE); + } + + if (e.reason === WillSaveStateReason.SHUTDOWN && this.state.zenMode.active) { + if (!this.configurationService.getValue(Settings.ZEN_MODE_RESTORE)) { + this.toggleZenMode(true); // We will not restore zen mode, need to clear all zen mode state changes + } + } + } + + dispose(): void { + super.dispose(); + + this.disposed = true; + } +} diff --git a/src/vs/workbench/browser/nodeless.simpleservices.ts b/src/vs/workbench/browser/nodeless.simpleservices.ts index 527ddcd40ce..b6536905862 100644 --- a/src/vs/workbench/browser/nodeless.simpleservices.ts +++ b/src/vs/workbench/browser/nodeless.simpleservices.ts @@ -15,27 +15,26 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; // tslint:disable-next-line: import-patterns no-standalone-editor import { SimpleConfigurationService as StandaloneEditorConfigurationService, SimpleDialogService as StandaloneEditorDialogService, StandaloneKeybindingService, SimpleResourcePropertiesService } from 'vs/editor/standalone/browser/simpleServices'; -import { IDialogService, IFileDialogService, IPickAndOpenOptions, ISaveDialogOptions, IOpenDialogOptions } from 'vs/platform/dialogs/common/dialogs'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IDownloadService } from 'vs/platform/download/common/download'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IEnvironmentService, IExtensionHostDebugParams, IDebugParams } from 'vs/platform/environment/common/environment'; -import { IExtensionGalleryService, IQueryOptions, IGalleryExtension, InstallOperation, StatisticType, ITranslation, IGalleryExtensionVersion, IExtensionIdentifier, IReportedExtension, IExtensionManagementService, ILocalExtension, IGalleryMetadata } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionGalleryService, IQueryOptions, IGalleryExtension, InstallOperation, StatisticType, ITranslation, IGalleryExtensionVersion, IExtensionIdentifier, IReportedExtension, IExtensionManagementService, ILocalExtension, IGalleryMetadata, IExtensionTipsService, ExtensionRecommendationReason, IExtensionRecommendation } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IPager } from 'vs/base/common/paging'; import { IExtensionManifest, ExtensionType, ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { NullExtensionService, IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IURLHandler, IURLService } from 'vs/platform/url/common/url'; -import { IJSONEditingService, IJSONValue } from 'vs/workbench/services/configuration/common/jsonEditing'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { ITelemetryService, ITelemetryData, ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { AbstractLifecycleService } from 'vs/platform/lifecycle/common/lifecycleService'; -import { ILogService, NullLogService, LogLevel } from 'vs/platform/log/common/log'; +import { ILogService, LogLevel, ConsoleLogService } from 'vs/platform/log/common/log'; import { ShutdownReason, ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IMenubarService, IMenubarData } from 'vs/platform/menubar/common/menubar'; import { IProductService } from 'vs/platform/product/common/product'; import { IRemoteAuthorityResolverService, ResolvedAuthority } from 'vs/platform/remote/common/remoteAuthorityResolver'; -import { joinPath, isEqualOrParent, isEqual } from 'vs/base/common/resources'; +import { joinPath, isEqualOrParent, isEqual, dirname } from 'vs/base/common/resources'; import { basename } from 'vs/base/common/path'; import { ISearchService, ITextQueryProps, ISearchProgressItem, ISearchComplete, IFileQueryProps, SearchProviderType, ISearchResultProvider, ITextQuery, IFileMatch, QueryType, FileMatch, pathIncludedInQuery } from 'vs/workbench/services/search/common/search'; import { IModelService } from 'vs/editor/common/services/modelService'; @@ -47,7 +46,7 @@ import { editorMatchesToTextSearchResults, addContextToEditorMatches } from 'vs/ import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { InMemoryStorageService, IStorageService } from 'vs/platform/storage/common/storage'; import { ITextMateService, IGrammar as ITextMategrammar } from 'vs/workbench/services/textMate/common/textMateService'; -import { LanguageId } from 'vs/editor/common/modes'; +import { LanguageId, TokenizationRegistry } from 'vs/editor/common/modes'; import { IUpdateService, State } from 'vs/platform/update/common/update'; import { IWindowConfiguration, IPath, IPathsToWaitFor, IWindowService, INativeOpenDialogOptions, IEnterWorkspaceResult, IURIToOpen, IMessageBoxResult, IWindowsService } from 'vs/platform/windows/common/windows'; import { IProcessEnvironment, isWindows } from 'vs/base/common/platform'; @@ -60,6 +59,7 @@ import { IWorkspaceContextService, Workspace, toWorkspaceFolders, IWorkspaceFold import { ITextResourcePropertiesService } from 'vs/editor/common/services/resourceConfiguration'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { Color, RGBA } from 'vs/base/common/color'; export const workspaceResource = URI.file(isWindows ? 'C:\\simpleWorkspace' : '/simpleWorkspace'); @@ -351,6 +351,45 @@ registerSingleton(IExtensionGalleryService, SimpleExtensionGalleryService, true) //#region Extension Management +//#region Extension Tips + +export class SimpleExtensionTipsService implements IExtensionTipsService { + _serviceBrand: any; + + onRecommendationChange = Event.None; + + getAllRecommendationsWithReason(): { [id: string]: { reasonId: ExtensionRecommendationReason; reasonText: string; }; } { + return Object.create(null); + } + + getFileBasedRecommendations(): IExtensionRecommendation[] { + return []; + } + + getOtherRecommendations(): Promise { + return Promise.resolve([]); + } + + getWorkspaceRecommendations(): Promise { + return Promise.resolve([]); + } + + getKeymapRecommendations(): IExtensionRecommendation[] { + return []; + } + + toggleIgnoredRecommendation(extensionId: string, shouldIgnore: boolean): void { + } + + getAllIgnoredRecommendations(): { global: string[]; workspace: string[]; } { + return Object.create(null); + } +} + +registerSingleton(IExtensionTipsService, SimpleExtensionTipsService, true); + +//#endregion + export class SimpleExtensionManagementService implements IExtensionManagementService { _serviceBrand: any; @@ -429,81 +468,15 @@ export class SimpleExtensionURLHandler implements IExtensionUrlHandler { _serviceBrand: any; - registerExtensionHandler(extensionId: ExtensionIdentifier, handler: IURLHandler): void { - throw new Error('Method not implemented.'); - } + registerExtensionHandler(extensionId: ExtensionIdentifier, handler: IURLHandler): void { } - unregisterExtensionHandler(extensionId: ExtensionIdentifier): void { - throw new Error('Method not implemented.'); - } + unregisterExtensionHandler(extensionId: ExtensionIdentifier): void { } } registerSingleton(IExtensionUrlHandler, SimpleExtensionURLHandler, true); //#endregion -//#region File Dialog - -export class SimpleFileDialogService implements IFileDialogService { - - _serviceBrand: any; - - defaultFilePath(schemeFilter?: string): URI { - throw new Error('Method not implemented.'); - } - - defaultFolderPath(schemeFilter?: string): URI { - throw new Error('Method not implemented.'); - } - - defaultWorkspacePath(schemeFilter?: string): URI { - throw new Error('Method not implemented.'); - } - - pickFileFolderAndOpen(options: IPickAndOpenOptions): Promise { - throw new Error('Method not implemented.'); - } - - pickFileAndOpen(options: IPickAndOpenOptions): Promise { - throw new Error('Method not implemented.'); - } - - pickFolderAndOpen(options: IPickAndOpenOptions): Promise { - throw new Error('Method not implemented.'); - } - - pickWorkspaceAndOpen(options: IPickAndOpenOptions): Promise { - throw new Error('Method not implemented.'); - } - - showSaveDialog(options: ISaveDialogOptions): Promise { - throw new Error('Method not implemented.'); - } - - showOpenDialog(options: IOpenDialogOptions): Promise { - throw new Error('Method not implemented.'); - } -} - -registerSingleton(IFileDialogService, SimpleFileDialogService, true); - -//#endregion - -//#region JSON Editing - -export class SimpleJSONEditingService implements IJSONEditingService { - - _serviceBrand: any; - - write(resource: URI, value: IJSONValue, save: boolean): Promise { - return Promise.resolve(); - } -} - -registerSingleton(IJSONEditingService, SimpleJSONEditingService, true); - -//#endregion - //#region Keybinding export class SimpleKeybindingService extends StandaloneKeybindingService { @@ -572,7 +545,7 @@ registerSingleton(ILifecycleService, SimpleLifecycleService); //#region Log -export class SimpleLogService extends NullLogService { } +export class SimpleLogService extends ConsoleLogService { } //#endregion @@ -789,13 +762,36 @@ export class SimpleRemoteFileService implements IFileService { moveFile(_source: URI, _target: URI, _overwrite?: boolean): Promise { return Promise.resolve(null!); } - copyFile(_source: URI, _target: URI, _overwrite?: boolean): Promise { throw new Error('not implemented'); } + copyFile(_source: URI, _target: URI, _overwrite?: boolean): Promise { + const parent = fileMap.get(dirname(_target)); + if (!parent) { + return Promise.resolve(undefined); + } - createFile(_resource: URI, _content?: string, _options?: ICreateFileOptions): Promise { throw new Error('not implemented'); } + return this.resolveContent(_source).then(content => { + return Promise.resolve(createFile(parent, basename(_target.path), content.value)); + }); + } + + createFile(_resource: URI, _content?: string, _options?: ICreateFileOptions): Promise { + const parent = fileMap.get(dirname(_resource)); + if (!parent) { + return Promise.reject(new Error(`Unable to create file in ${dirname(_resource).path}`)); + } + + return Promise.resolve(createFile(parent, basename(_resource.path))); + } readFolder(_resource: URI) { return Promise.resolve([]); } - createFolder(_resource: URI): Promise { throw new Error('not implemented'); } + createFolder(_resource: URI): Promise { + const parent = fileMap.get(dirname(_resource)); + if (!parent) { + return Promise.reject(new Error(`Unable to create folder in ${dirname(_resource).path}`)); + } + + return Promise.resolve(createFolder(parent, basename(_resource.path))); + } registerProvider(_scheme: string, _provider) { return { dispose() { } }; } @@ -814,50 +810,52 @@ export class SimpleRemoteFileService implements IFileService { dispose(): void { } } +function createFile(parent: IFileStat, name: string, content: string = ''): IFileStat { + const file: IFileStat = { + resource: joinPath(parent.resource, name), + etag: Date.now().toString(), + mtime: Date.now(), + isDirectory: false, + name + }; + + // @ts-ignore + parent.children.push(file); + + fileMap.set(file.resource, file); + + contentMap.set(file.resource, { + resource: joinPath(parent.resource, name), + etag: Date.now().toString(), + mtime: Date.now(), + value: content, + encoding: 'utf8', + name + } as IContent); + + return file; +} + +function createFolder(parent: IFileStat, name: string): IFileStat { + const folder: IFileStat = { + resource: joinPath(parent.resource, name), + etag: Date.now().toString(), + mtime: Date.now(), + isDirectory: true, + name, + children: [] + }; + + // @ts-ignore + parent.children.push(folder); + + fileMap.set(folder.resource, folder); + + return folder; +} + function initFakeFileSystem(): void { - function createFile(parent: IFileStat, name: string, content: string): void { - const file: IFileStat = { - resource: joinPath(parent.resource, name), - etag: Date.now().toString(), - mtime: Date.now(), - isDirectory: false, - name - }; - - // @ts-ignore - parent.children.push(file); - - fileMap.set(file.resource, file); - - contentMap.set(file.resource, { - resource: joinPath(parent.resource, name), - etag: Date.now().toString(), - mtime: Date.now(), - value: content, - encoding: 'utf8', - name - } as IContent); - } - - function createFolder(parent: IFileStat, name: string): IFileStat { - const folder: IFileStat = { - resource: joinPath(parent.resource, name), - etag: Date.now().toString(), - mtime: Date.now(), - isDirectory: true, - name, - children: [] - }; - - // @ts-ignore - parent.children.push(folder); - - fileMap.set(folder.resource, folder); - - return folder; - } - const root: IFileStat = { resource: workspaceResource, etag: Date.now().toString(), @@ -1055,7 +1053,7 @@ registerSingleton(IFileService, SimpleRemoteFileService); //#region Request -export const IRequestService = createDecorator('requestService2'); +export const IRequestService = createDecorator('requestService'); export interface IRequestService { _serviceBrand: any; @@ -1136,7 +1134,6 @@ export class SimpleSearchService implements ISearchService { } // Don't support other resource schemes than files for now - // todo@remote // why is that? we should search for resources from other // schemes else if (resource.scheme !== Schemas.file) { @@ -1217,6 +1214,8 @@ registerSingleton(ITelemetryService, SimpleTelemetryService); //#region Textmate +TokenizationRegistry.setColorMap([null, new Color(new RGBA(212, 212, 212, 1)), new Color(new RGBA(30, 30, 30, 1))]); + export class SimpleTextMateService implements ITextMateService { _serviceBrand: any; @@ -1619,7 +1618,7 @@ export class SimpleWindowsService implements IWindowsService { } getWindows(): Promise<{ id: number; workspace?: IWorkspaceIdentifier; folderUri?: ISingleFolderWorkspaceIdentifier; title: string; filename?: string; }[]> { - throw new Error('not implemented'); + return Promise.resolve([]); } getWindowCount(): Promise { diff --git a/src/vs/workbench/browser/parts/editor/editorStatus.ts b/src/vs/workbench/browser/parts/editor/editorStatus.ts index 60538abc8db..10373ebede2 100644 --- a/src/vs/workbench/browser/parts/editor/editorStatus.ts +++ b/src/vs/workbench/browser/parts/editor/editorStatus.ts @@ -15,7 +15,7 @@ import { Action } from 'vs/base/common/actions'; import { language, LANGUAGE_DEFAULT } from 'vs/base/common/platform'; import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput'; import { IFileEditorInput, EncodingMode, IEncodingSupport, toResource, SideBySideEditorInput, IEditor as IBaseEditor, IEditorInput } from 'vs/workbench/common/editor'; -import { IDisposable, combinedDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IDisposable, combinedDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; import { IEditorAction } from 'vs/editor/common/editorCommon'; import { EndOfLineSequence, ITextModel } from 'vs/editor/common/model'; @@ -100,25 +100,14 @@ interface IEditorSelectionStatus { class StateChange { _stateChangeBrand: void; - indentation: boolean; - selectionStatus: boolean; - mode: boolean; - encoding: boolean; - EOL: boolean; - tabFocusMode: boolean; - screenReaderMode: boolean; - metadata: boolean; - - constructor() { - this.indentation = false; - this.selectionStatus = false; - this.mode = false; - this.encoding = false; - this.EOL = false; - this.tabFocusMode = false; - this.screenReaderMode = false; - this.metadata = false; - } + indentation: boolean = false; + selectionStatus: boolean = false; + mode: boolean = false; + encoding: boolean = false; + EOL: boolean = false; + tabFocusMode: boolean = false; + screenReaderMode: boolean = false; + metadata: boolean = false; combine(other: StateChange) { this.indentation = this.indentation || other.indentation; @@ -130,6 +119,17 @@ class StateChange { this.screenReaderMode = this.screenReaderMode || other.screenReaderMode; this.metadata = this.metadata || other.metadata; } + + public hasChanges(): boolean { + return this.indentation + || this.selectionStatus + || this.mode + || this.encoding + || this.EOL + || this.tabFocusMode + || this.screenReaderMode + || this.metadata; + } } interface StateDelta { @@ -144,29 +144,29 @@ interface StateDelta { } class State { - private _selectionStatus: string | null; - get selectionStatus(): string | null { return this._selectionStatus; } + private _selectionStatus: string | null | undefined; + get selectionStatus(): string | null | undefined { return this._selectionStatus; } - private _mode: string | null; - get mode(): string | null { return this._mode; } + private _mode: string | null | undefined; + get mode(): string | null | undefined { return this._mode; } - private _encoding: string | null; - get encoding(): string | null { return this._encoding; } + private _encoding: string | null | undefined; + get encoding(): string | null | undefined { return this._encoding; } - private _EOL: string | null; - get EOL(): string | null { return this._EOL; } + private _EOL: string | null | undefined; + get EOL(): string | null | undefined { return this._EOL; } - private _indentation: string | null; - get indentation(): string | null { return this._indentation; } + private _indentation: string | null | undefined; + get indentation(): string | null | undefined { return this._indentation; } - private _tabFocusMode: boolean | null; - get tabFocusMode(): boolean | null { return this._tabFocusMode; } + private _tabFocusMode: boolean | null | undefined; + get tabFocusMode(): boolean | null | undefined { return this._tabFocusMode; } - private _screenReaderMode: boolean | null; - get screenReaderMode(): boolean | null { return this._screenReaderMode; } + private _screenReaderMode: boolean | null | undefined; + get screenReaderMode(): boolean | null | undefined { return this._screenReaderMode; } - private _metadata: string | null; - get metadata(): string | null { return this._metadata; } + private _metadata: string | null | undefined; + get metadata(): string | null | undefined { return this._metadata; } constructor() { this._selectionStatus = null; @@ -178,71 +178,59 @@ class State { this._metadata = null; } - update(update: StateDelta): StateChange | null { - const e = new StateChange(); - let somethingChanged = false; + update(update: StateDelta): StateChange { + const change = new StateChange(); - if (typeof update.selectionStatus !== 'undefined') { + if ('selectionStatus' in update) { if (this._selectionStatus !== update.selectionStatus) { this._selectionStatus = update.selectionStatus; - somethingChanged = true; - e.selectionStatus = true; + change.selectionStatus = true; } } - if (typeof update.indentation !== 'undefined') { + if ('indentation' in update) { if (this._indentation !== update.indentation) { this._indentation = update.indentation; - somethingChanged = true; - e.indentation = true; + change.indentation = true; } } - if (typeof update.mode !== 'undefined') { + if ('mode' in update) { if (this._mode !== update.mode) { this._mode = update.mode; - somethingChanged = true; - e.mode = true; + change.mode = true; } } - if (typeof update.encoding !== 'undefined') { + if ('encoding' in update) { if (this._encoding !== update.encoding) { this._encoding = update.encoding; - somethingChanged = true; - e.encoding = true; + change.encoding = true; } } - if (typeof update.EOL !== 'undefined') { + if ('EOL' in update) { if (this._EOL !== update.EOL) { this._EOL = update.EOL; - somethingChanged = true; - e.EOL = true; + change.EOL = true; } } - if (typeof update.tabFocusMode !== 'undefined') { + if ('tabFocusMode' in update) { if (this._tabFocusMode !== update.tabFocusMode) { this._tabFocusMode = update.tabFocusMode; - somethingChanged = true; - e.tabFocusMode = true; + change.tabFocusMode = true; } } - if (typeof update.screenReaderMode !== 'undefined') { + if ('screenReaderMode' in update) { if (this._screenReaderMode !== update.screenReaderMode) { this._screenReaderMode = update.screenReaderMode; - somethingChanged = true; - e.screenReaderMode = true; + change.screenReaderMode = true; } } - if (typeof update.metadata !== 'undefined') { + if ('metadata' in update) { if (this._metadata !== update.metadata) { this._metadata = update.metadata; - somethingChanged = true; - e.metadata = true; + change.metadata = true; } } - if (somethingChanged) { - return e; - } - return null; + return change; } } @@ -256,29 +244,46 @@ const nlsTabFocusMode = nls.localize('tabFocusModeEnabled', "Tab Moves Focus"); const nlsScreenReaderDetected = nls.localize('screenReaderDetected', "Screen Reader Optimized"); const nlsScreenReaderDetectedTitle = nls.localize('screenReaderDetectedExtra', "If you are not using a Screen Reader, please change the setting `editor.accessibilitySupport` to \"off\"."); -function setDisplay(el: HTMLElement, desiredValue: string): void { - if (el.style.display !== desiredValue) { - el.style.display = desiredValue; + +class StatusBarItem { + private _showing = true; + + constructor( + private readonly element: HTMLElement, + title: string, + ) { + this.setVisible(false); + this.element.title = title; + } + + public set textContent(value: string) { + this.element.textContent = value; + } + + public set onclick(value: () => void) { + this.element.onclick = value; + } + + public setVisible(shouldShow: boolean): void { + if (shouldShow !== this._showing) { + this._showing = shouldShow; + this.element.style.display = shouldShow ? '' : 'none'; + } } } -function show(el: HTMLElement): void { - setDisplay(el, ''); -} -function hide(el: HTMLElement): void { - setDisplay(el, 'none'); -} + export class EditorStatus implements IStatusbarItem { private state: State; private element: HTMLElement; - private tabFocusModeElement: HTMLElement; - private screenRedearModeElement: HTMLElement; - private indentationElement: HTMLElement; - private selectionElement: HTMLElement; - private encodingElement: HTMLElement; - private eolElement: HTMLElement; - private modeElement: HTMLElement; - private metadataElement: HTMLElement; + private tabFocusModeElement: StatusBarItem; + private screenRedearModeElement: StatusBarItem; + private indentationElement: StatusBarItem; + private selectionElement: StatusBarItem; + private encodingElement: StatusBarItem; + private eolElement: StatusBarItem; + private modeElement: StatusBarItem; + private metadataElement: StatusBarItem; private toDispose: IDisposable[]; private activeEditorListeners: IDisposable[]; private delayedRender: IDisposable | null; @@ -304,59 +309,57 @@ export class EditorStatus implements IStatusbarItem { render(container: HTMLElement): IDisposable { this.element = append(container, $('.editor-statusbar-item')); - this.tabFocusModeElement = append(this.element, $('a.editor-status-tabfocusmode.status-bar-info')); - this.tabFocusModeElement.title = nls.localize('disableTabMode', "Disable Accessibility Mode"); + this.tabFocusModeElement = new StatusBarItem( + append(this.element, $('a.editor-status-tabfocusmode.status-bar-info')), + nls.localize('disableTabMode', "Disable Accessibility Mode")); this.tabFocusModeElement.onclick = () => this.onTabFocusModeClick(); this.tabFocusModeElement.textContent = nlsTabFocusMode; - hide(this.tabFocusModeElement); - this.screenRedearModeElement = append(this.element, $('a.editor-status-screenreadermode.status-bar-info')); + this.screenRedearModeElement = new StatusBarItem( + append(this.element, $('a.editor-status-screenreadermode.status-bar-info')), + nlsScreenReaderDetectedTitle); this.screenRedearModeElement.textContent = nlsScreenReaderDetected; - this.screenRedearModeElement.title = nlsScreenReaderDetectedTitle; this.screenRedearModeElement.onclick = () => this.onScreenReaderModeClick(); - hide(this.screenRedearModeElement); - this.selectionElement = append(this.element, $('a.editor-status-selection')); - this.selectionElement.title = nls.localize('gotoLine', "Go to Line"); + this.selectionElement = new StatusBarItem( + append(this.element, $('a.editor-status-selection')), + nls.localize('gotoLine', "Go to Line")); this.selectionElement.onclick = () => this.onSelectionClick(); - hide(this.selectionElement); - this.indentationElement = append(this.element, $('a.editor-status-indentation')); - this.indentationElement.title = nls.localize('selectIndentation', "Select Indentation"); + this.indentationElement = new StatusBarItem( + append(this.element, $('a.editor-status-indentation')), + nls.localize('selectIndentation', "Select Indentation")); this.indentationElement.onclick = () => this.onIndentationClick(); - hide(this.indentationElement); - this.encodingElement = append(this.element, $('a.editor-status-encoding')); - this.encodingElement.title = nls.localize('selectEncoding', "Select Encoding"); + this.encodingElement = new StatusBarItem( + append(this.element, $('a.editor-status-encoding')), + nls.localize('selectEncoding', "Select Encoding")); this.encodingElement.onclick = () => this.onEncodingClick(); - hide(this.encodingElement); - this.eolElement = append(this.element, $('a.editor-status-eol')); - this.eolElement.title = nls.localize('selectEOL', "Select End of Line Sequence"); + this.eolElement = new StatusBarItem( + append(this.element, $('a.editor-status-eol')), + nls.localize('selectEOL', "Select End of Line Sequence")); this.eolElement.onclick = () => this.onEOLClick(); - hide(this.eolElement); - this.modeElement = append(this.element, $('a.editor-status-mode')); - this.modeElement.title = nls.localize('selectLanguageMode', "Select Language Mode"); + this.modeElement = new StatusBarItem( + append(this.element, $('a.editor-status-mode')), + nls.localize('selectLanguageMode', "Select Language Mode")); this.modeElement.onclick = () => this.onModeClick(); - hide(this.modeElement); - this.metadataElement = append(this.element, $('span.editor-status-metadata')); - this.metadataElement.title = nls.localize('fileInfo', "File Information"); - hide(this.metadataElement); + this.metadataElement = new StatusBarItem( + append(this.element, $('span.editor-status-metadata')), + nls.localize('fileInfo', "File Information")); this.delayedRender = null; this.toRender = null; this.toDispose.push( - { - dispose: () => { - if (this.delayedRender) { - this.delayedRender.dispose(); - this.delayedRender = null; - } + toDisposable(() => { + if (this.delayedRender) { + this.delayedRender.dispose(); + this.delayedRender = null; } - }, + }), this.editorService.onDidActiveEditorChange(() => this.updateStatusBar()), this.untitledEditorService.onDidChangeEncoding(r => this.onResourceEncodingChange(r)), this.textFileService.models.onModelEncodingChanged(e => this.onResourceEncodingChange(e.resource)), @@ -368,7 +371,7 @@ export class EditorStatus implements IStatusbarItem { private updateState(update: StateDelta): void { const changed = this.state.update(update); - if (!changed) { + if (!changed.hasChanges()) { // Nothing really changed return; } @@ -390,72 +393,64 @@ export class EditorStatus implements IStatusbarItem { private _renderNow(changed: StateChange): void { if (changed.tabFocusMode) { - if (this.state.tabFocusMode && this.state.tabFocusMode === true) { - show(this.tabFocusModeElement); - } else { - hide(this.tabFocusModeElement); - } + this.tabFocusModeElement.setVisible(!!this.state.tabFocusMode); } if (changed.screenReaderMode) { - if (this.state.screenReaderMode && this.state.screenReaderMode === true) { - show(this.screenRedearModeElement); - } else { - hide(this.screenRedearModeElement); - } + this.screenRedearModeElement.setVisible(!!this.state.screenReaderMode); } if (changed.indentation) { if (this.state.indentation) { this.indentationElement.textContent = this.state.indentation; - show(this.indentationElement); + this.indentationElement.setVisible(true); } else { - hide(this.indentationElement); + this.indentationElement.setVisible(false); } } if (changed.selectionStatus) { if (this.state.selectionStatus && !this.state.screenReaderMode) { this.selectionElement.textContent = this.state.selectionStatus; - show(this.selectionElement); + this.selectionElement.setVisible(true); } else { - hide(this.selectionElement); + this.selectionElement.setVisible(false); } } if (changed.encoding) { if (this.state.encoding) { this.encodingElement.textContent = this.state.encoding; - show(this.encodingElement); + this.encodingElement.setVisible(true); } else { - hide(this.encodingElement); + this.encodingElement.setVisible(false); } } if (changed.EOL) { if (this.state.EOL) { this.eolElement.textContent = this.state.EOL === '\r\n' ? nlsEOLCRLF : nlsEOLLF; - show(this.eolElement); + this.eolElement.setVisible(true); } else { - hide(this.eolElement); + this.eolElement.setVisible(false); } } if (changed.mode) { if (this.state.mode) { this.modeElement.textContent = this.state.mode; - show(this.modeElement); + this.modeElement.setVisible(true); } else { - hide(this.modeElement); + this.modeElement.setVisible(false); } } if (changed.metadata) { if (this.state.metadata) { this.metadataElement.textContent = this.state.metadata; - show(this.metadataElement); + this.metadataElement.setVisible(true); } else { - hide(this.metadataElement); + this.metadataElement.setVisible(false); } } } diff --git a/src/vs/workbench/browser/parts/views/views.ts b/src/vs/workbench/browser/parts/views/views.ts index 0c9ce5746a6..64ef1a1f105 100644 --- a/src/vs/workbench/browser/parts/views/views.ts +++ b/src/vs/workbench/browser/parts/views/views.ts @@ -262,7 +262,7 @@ export class ContributableViewsModel extends Disposable { return this.isViewDescriptorVisible(viewDescriptor); } - setVisible(id: string, visible: boolean): void { + setVisible(id: string, visible: boolean, size?: number): void { const { visibleIndex, viewDescriptor, state } = this.find(id); if (!viewDescriptor.canToggleVisibility) { @@ -279,6 +279,10 @@ export class ContributableViewsModel extends Disposable { state.visibleGlobal = visible; } + if (typeof size === 'number') { + state.size = size; + } + if (visible) { this._onDidAdd.fire([{ index: visibleIndex, viewDescriptor, size: state.size, collapsed: state.collapsed }]); } else { diff --git a/src/vs/workbench/browser/workbench.ts b/src/vs/workbench/browser/workbench.ts index ec9696bcc0e..447f339ad12 100644 --- a/src/vs/workbench/browser/workbench.ts +++ b/src/vs/workbench/browser/workbench.ts @@ -7,37 +7,27 @@ import 'vs/workbench/browser/style'; import { localize } from 'vs/nls'; import { setFileNameComparer } from 'vs/base/common/comparers'; -import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; import { Event, Emitter, setGlobalLeakWarningThreshold } from 'vs/base/common/event'; -import { EventType, addDisposableListener, addClasses, addClass, removeClass, isAncestor, getClientArea, position, size, removeClasses } from 'vs/base/browser/dom'; +import { addClasses, addClass, removeClasses } from 'vs/base/browser/dom'; import { runWhenIdle, IdleValue } from 'vs/base/common/async'; -import { getZoomLevel, onDidChangeFullscreen, isFullscreen, getZoomFactor } from 'vs/base/browser/browser'; +import { getZoomLevel } from 'vs/base/browser/browser'; 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 } from 'vs/base/common/platform'; -import { IResourceInput } from 'vs/platform/editor/common/editor'; +import { isWindows, isLinux } from 'vs/base/common/platform'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; -import { IEditorInputFactoryRegistry, Extensions as EditorExtensions, IUntitledResourceInput, IResourceDiffInput } from 'vs/workbench/common/editor'; -import { SidebarPart } from 'vs/workbench/browser/parts/sidebar/sidebarPart'; -import { PanelPart } from 'vs/workbench/browser/parts/panel/panelPart'; +import { IEditorInputFactoryRegistry, Extensions as EditorExtensions } from 'vs/workbench/common/editor'; import { IActionBarRegistry, Extensions as ActionBarExtensions } from 'vs/workbench/browser/actions'; -import { PanelRegistry, Extensions as PanelExtensions } from 'vs/workbench/browser/panel'; import { getServices } from 'vs/platform/instantiation/common/extensions'; -import { Position, Parts, IWorkbenchLayoutService, ILayoutOptions } from 'vs/workbench/services/layout/browser/layoutService'; -import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; -import { IStorageService, StorageScope, IWillSaveStateEvent, WillSaveStateReason } from 'vs/platform/storage/common/storage'; +import { Position, Parts, IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; +import { IStorageService } from 'vs/platform/storage/common/storage'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; 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 { IInstantiationService, ServicesAccessor, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { LifecyclePhase, StartupKind, ILifecycleService, WillShutdownEvent } from 'vs/platform/lifecycle/common/lifecycle'; -import { IWindowService, IPath, MenuBarVisibility, getTitleBarStyle } from 'vs/platform/windows/common/windows'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { LifecyclePhase, ILifecycleService, WillShutdownEvent } from 'vs/platform/lifecycle/common/lifecycle'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { NotificationService } from 'vs/workbench/services/notification/common/notificationService'; import { NotificationsCenter } from 'vs/workbench/browser/parts/notifications/notificationsCenter'; @@ -47,51 +37,17 @@ import { registerNotificationCommands } from 'vs/workbench/browser/parts/notific import { NotificationsToasts } from 'vs/workbench/browser/parts/notifications/notificationsToasts'; import { IEditorService, IResourceEditor } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; -import { Sizing, Direction, Grid, View } from 'vs/base/browser/ui/grid/grid'; -import { WorkbenchLegacyLayout } from 'vs/workbench/browser/legacyLayout'; import { setARIAContainer } from 'vs/base/browser/ui/aria/aria'; import { restoreFontInfo, readFontInfo, saveFontInfo } from 'vs/editor/browser/config/configuration'; import { BareFontInfo } from 'vs/editor/common/config/fontInfo'; import { ILogService } from 'vs/platform/log/common/log'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { WorkbenchContextKeysHandler } from 'vs/workbench/browser/contextkeys'; -import { IDimension } from 'vs/platform/layout/browser/layoutService'; -import { Part } from 'vs/workbench/browser/part'; -import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar'; -import { IActivityBarService } from 'vs/workbench/services/activityBar/browser/activityBarService'; import { coalesce } from 'vs/base/common/arrays'; import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; +import { Layout } from 'vs/workbench/browser/layout'; -export interface IWorkbenchOptions { - hasInitialFilesToOpen: boolean; -} - -enum Settings { - MENUBAR_VISIBLE = 'window.menuBarVisibility', - ACTIVITYBAR_VISIBLE = 'workbench.activityBar.visible', - STATUSBAR_VISIBLE = 'workbench.statusBar.visible', - - SIDEBAR_POSITION = 'workbench.sideBar.location', - PANEL_POSITION = 'workbench.panel.defaultLocation', - - FONT_ALIASING = 'workbench.fontAliasing', - ZEN_MODE_RESTORE = 'zenMode.restore' -} - -enum Storage { - SIDEBAR_HIDDEN = 'workbench.sidebar.hidden', - - PANEL_HIDDEN = 'workbench.panel.hidden', - PANEL_POSITION = 'workbench.panel.location', - - ZEN_MODE_ENABLED = 'workbench.zenmode.active', - CENTERED_LAYOUT_ENABLED = 'workbench.centerededitorlayout.active', -} - -export class Workbench extends Disposable implements IWorkbenchLayoutService { - - _serviceBrand: ServiceIdentifier; +export class Workbench extends Layout { private readonly _onShutdown = this._register(new Emitter()); get onShutdown(): Event { return this._onShutdown.event; } @@ -99,14 +55,12 @@ export class Workbench extends Disposable implements IWorkbenchLayoutService { private readonly _onWillShutdown = this._register(new Emitter()); get onWillShutdown(): Event { return this._onWillShutdown.event; } - private workbench: HTMLElement = document.createElement('div'); - constructor( - private readonly parent: HTMLElement, + parent: HTMLElement, private readonly serviceCollection: ServiceCollection, logService: ILogService ) { - super(); + super(parent); this.registerErrorHandler(logService); } @@ -127,7 +81,7 @@ export class Workbench extends Disposable implements IWorkbenchLayoutService { setUnexpectedErrorHandler(error => this.handleUnexpectedError(error, logService)); // Inform user about loading issues from the loader - (self).require.config({ + (window).require.config({ onError: err => { if (err.errorCode === 'load') { onUnexpectedError(new Error(localize('loaderErrorNative', "Failed to load a required file. Please restart the application to try again. Details: {0}", JSON.stringify(err)))); @@ -185,7 +139,7 @@ export class Workbench extends Disposable implements IWorkbenchLayoutService { this.initLayout(accessor); // Registries - this.initRegistries(accessor); + this.startRegistries(accessor); // Context Keys this._register(instantiationService.createInstance(WorkbenchContextKeysHandler)); @@ -230,17 +184,15 @@ export class Workbench extends Disposable implements IWorkbenchLayoutService { serviceCollection.set(contributedService.id, contributedService.descriptor); } - const instantationServie = new InstantiationService(serviceCollection, true); + const instantiationService = new InstantiationService(serviceCollection, true); // Wrap up - instantationServie.invokeFunction(accessor => { + instantiationService.invokeFunction(accessor => { const lifecycleService = accessor.get(ILifecycleService); - // TODO@Ben TODO@Sandeep TODO@Martin debt around cyclic dependencies + // TODO@Ben TODO@Sandeep debt around cyclic dependencies const fileService = accessor.get(IFileService); - const instantiationService = accessor.get(IInstantiationService); const configurationService = accessor.get(IConfigurationService) as any; - const themeService = accessor.get(IWorkbenchThemeService) as any; if (typeof configurationService.acquireFileService === 'function') { configurationService.acquireFileService(fileService); @@ -250,18 +202,14 @@ export class Workbench extends Disposable implements IWorkbenchLayoutService { configurationService.acquireInstantiationService(instantiationService); } - if (typeof themeService.acquireFileService === 'function') { - themeService.acquireFileService(fileService); - } - // Signal to lifecycle that services are set lifecycleService.phase = LifecyclePhase.Ready; }); - return instantationServie; + return instantiationService; } - private initRegistries(accessor: ServicesAccessor): void { + private startRegistries(accessor: ServicesAccessor): void { Registry.as(ActionBarExtensions.Actionbar).start(accessor); Registry.as(WorkbenchExtensions.Workbench).start(accessor); Registry.as(EditorExtensions.EditorInputFactories).start(accessor); @@ -289,7 +237,7 @@ export class Workbench extends Disposable implements IWorkbenchLayoutService { private fontAliasing: 'default' | 'antialiased' | 'none' | 'auto'; private setFontAliasing(configurationService: IConfigurationService) { - const aliasing = configurationService.getValue<'default' | 'antialiased' | 'none' | 'auto'>(Settings.FONT_ALIASING); + const aliasing = configurationService.getValue<'default' | 'antialiased' | 'none' | 'auto'>('workbench.fontAliasing'); if (this.fontAliasing === aliasing) { return; } @@ -298,11 +246,11 @@ export class Workbench extends Disposable implements IWorkbenchLayoutService { // Remove all const fontAliasingValues: (typeof aliasing)[] = ['antialiased', 'none', 'auto']; - removeClasses(this.workbench, ...fontAliasingValues.map(value => `monaco-font-aliasing-${value}`)); + removeClasses(this.container, ...fontAliasingValues.map(value => `monaco-font-aliasing-${value}`)); // Add specific if (fontAliasingValues.some(option => option === aliasing)) { - addClass(this.workbench, `monaco-font-aliasing-${aliasing}`); + addClass(this.container, `monaco-font-aliasing-${aliasing}`); } } @@ -319,7 +267,7 @@ export class Workbench extends Disposable implements IWorkbenchLayoutService { this.state.fullscreen ? 'fullscreen' : undefined ]); - addClasses(this.workbench, ...workbenchClasses); + addClasses(this.container, ...workbenchClasses); addClasses(document.body, platformClass); // used by our fonts // Apply font aliasing @@ -344,7 +292,7 @@ export class Workbench extends Disposable implements IWorkbenchLayoutService { // TODO@Ben cleanup once moved to grid // Insert all workbench parts at the beginning. Issue #52531 // This is primarily for the title bar to allow overriding -webkit-app-region - this.workbench.insertBefore(partContainer, this.workbench.lastChild); + this.container.insertBefore(partContainer, this.container.lastChild); } this.getPart(id).create(partContainer, options); @@ -354,7 +302,7 @@ export class Workbench extends Disposable implements IWorkbenchLayoutService { this.createNotificationsHandlers(instantiationService, notificationService); // Add Workbench to DOM - this.parent.appendChild(this.workbench); + this.parent.appendChild(this.container); } private createPart(id: string, role: string, classes: string[]): HTMLElement { @@ -369,8 +317,8 @@ export class Workbench extends Disposable implements IWorkbenchLayoutService { private createNotificationsHandlers(instantiationService: IInstantiationService, notificationService: NotificationService): void { // Instantiate Notification components - const notificationsCenter = this._register(instantiationService.createInstance(NotificationsCenter, this.workbench, notificationService.model)); - const notificationsToasts = this._register(instantiationService.createInstance(NotificationsToasts, this.workbench, notificationService.model)); + const notificationsCenter = this._register(instantiationService.createInstance(NotificationsCenter, this.container, notificationService.model)); + const notificationsToasts = this._register(instantiationService.createInstance(NotificationsToasts, this.container, notificationService.model)); this._register(instantiationService.createInstance(NotificationsAlerts, notificationService.model)); const notificationsStatus = instantiationService.createInstance(NotificationsStatus, notificationService.model); @@ -392,7 +340,7 @@ export class Workbench extends Disposable implements IWorkbenchLayoutService { logService: ILogService, lifecycleService: ILifecycleService ): Promise { - const restorePromises: Promise[] = []; + const restorePromises: Promise[] = []; // Restore editors mark('willRestoreEditors'); @@ -466,1078 +414,4 @@ export class Workbench extends Disposable implements IWorkbenchLayoutService { mark('didStartWorkbench'); }); } - - //#region ILayoutService - - private readonly _onTitleBarVisibilityChange: Emitter = this._register(new Emitter()); - get onTitleBarVisibilityChange(): Event { return this._onTitleBarVisibilityChange.event; } - - private readonly _onZenMode: Emitter = this._register(new Emitter()); - get onZenModeChange(): Event { return this._onZenMode.event; } - - private readonly _onLayout = this._register(new Emitter()); - get onLayout(): Event { return this._onLayout.event; } - - private _dimension: IDimension; - get dimension(): IDimension { return this._dimension; } - - get container(): HTMLElement { return this.workbench; } - - get hasWorkbench(): boolean { return true; } - - private parts: Map = new Map(); - - private workbenchGrid: Grid | WorkbenchLegacyLayout; - - private disposed: boolean; - - private titleBarPartView: View; - private activityBarPartView: View; - private sideBarPartView: View; - private panelPartView: View; - private editorPartView: View; - private statusBarPartView: View; - - private environmentService: IEnvironmentService; - private configurationService: IConfigurationService; - private lifecycleService: ILifecycleService; - private storageService: IStorageService; - private windowService: IWindowService; - private editorService: IEditorService; - private editorGroupService: IEditorGroupsService; - private panelService: IPanelService; - private titleService: ITitleService; - private viewletService: IViewletService; - private contextService: IWorkspaceContextService; - private backupFileService: IBackupFileService; - - private readonly state = { - fullscreen: false, - - menuBar: { - visibility: 'default' as MenuBarVisibility, - toggled: false - }, - - activityBar: { - hidden: false - }, - - sideBar: { - hidden: false, - position: Position.LEFT, - width: 300, - viewletToRestore: undefined as string | undefined - }, - - editor: { - hidden: false, - centered: false, - restoreCentered: false, - restoreEditors: false, - editorsToOpen: [] as Promise | IResourceEditor[] - }, - - panel: { - hidden: false, - position: Position.BOTTOM, - height: 350, - width: 350, - panelToRestore: undefined as string | undefined - }, - - statusBar: { - hidden: false - }, - - zenMode: { - active: false, - restore: false, - transitionedToFullScreen: false, - transitionedToCenteredEditorLayout: false, - wasSideBarVisible: false, - wasPanelVisible: false, - transitionDisposeables: [] as IDisposable[] - } - }; - - private initLayout(accessor: ServicesAccessor) { - - // Services - this.environmentService = accessor.get(IEnvironmentService); - this.configurationService = accessor.get(IConfigurationService); - this.lifecycleService = accessor.get(ILifecycleService); - this.windowService = accessor.get(IWindowService); - this.contextService = accessor.get(IWorkspaceContextService); - this.storageService = accessor.get(IStorageService); - - // Parts - this.editorService = accessor.get(IEditorService); - this.editorGroupService = accessor.get(IEditorGroupsService); - this.panelService = accessor.get(IPanelService); - this.viewletService = accessor.get(IViewletService); - this.titleService = accessor.get(ITitleService); - accessor.get(IStatusbarService); // not used, but called to ensure instantiated - accessor.get(IActivityBarService); // not used, but called to ensure instantiated - - // Listeners - this.registerLayoutListeners(); - - // State - this.initLayoutState(accessor.get(ILifecycleService)); - } - - private registerLayoutListeners(): void { - - // Storage - this._register(this.storageService.onWillSaveState(e => this.saveLayoutState(e))); - - // Restore editor if hidden and it changes - this._register(this.editorService.onDidVisibleEditorsChange(() => this.setEditorHidden(false))); - this._register(this.editorGroupService.onDidActivateGroup(() => this.setEditorHidden(false))); - - // Configuration changes - this._register(this.configurationService.onDidChangeConfiguration(() => this.doUpdateLayoutConfiguration())); - - // Fullscreen changes - this._register(onDidChangeFullscreen(() => this.onFullscreenChanged())); - - // Group changes - this._register(this.editorGroupService.onDidAddGroup(() => this.centerEditorLayout(this.state.editor.centered))); - this._register(this.editorGroupService.onDidRemoveGroup(() => this.centerEditorLayout(this.state.editor.centered))); - - // Prevent workbench from scrolling #55456 - this._register(addDisposableListener(this.workbench, EventType.SCROLL, () => this.workbench.scrollTop = 0)); - - // Menubar visibility changes - if ((isWindows || isLinux) && getTitleBarStyle(this.configurationService, this.environmentService) === 'custom') { - this._register(this.titleService.onMenubarVisibilityChange(visible => this.onMenubarToggled(visible))); - } - } - - private onMenubarToggled(visible: boolean) { - if (visible !== this.state.menuBar.toggled) { - this.state.menuBar.toggled = visible; - - if (this.state.fullscreen && (this.state.menuBar.visibility === 'toggle' || this.state.menuBar.visibility === 'default')) { - this._onTitleBarVisibilityChange.fire(); - this.layout(); - } - } - } - - private onFullscreenChanged(): void { - this.state.fullscreen = isFullscreen(); - - // Apply as CSS class - if (this.state.fullscreen) { - addClass(this.workbench, 'fullscreen'); - } else { - removeClass(this.workbench, 'fullscreen'); - - if (this.state.zenMode.transitionedToFullScreen && this.state.zenMode.active) { - this.toggleZenMode(); - } - } - - // Changing fullscreen state of the window has an impact on custom title bar visibility, so we need to update - if (getTitleBarStyle(this.configurationService, this.environmentService) === 'custom') { - this._onTitleBarVisibilityChange.fire(); - this.layout(); // handle title bar when fullscreen changes - } - } - - private doUpdateLayoutConfiguration(skipLayout?: boolean): void { - - // Sidebar position - const newSidebarPositionValue = this.configurationService.getValue(Settings.SIDEBAR_POSITION); - const newSidebarPosition = (newSidebarPositionValue === 'right') ? Position.RIGHT : Position.LEFT; - if (newSidebarPosition !== this.getSideBarPosition()) { - this.setSideBarPosition(newSidebarPosition); - } - - // Panel position - this.updatePanelPosition(); - - if (!this.state.zenMode.active) { - - // Statusbar visibility - const newStatusbarHiddenValue = !this.configurationService.getValue(Settings.STATUSBAR_VISIBLE); - if (newStatusbarHiddenValue !== this.state.statusBar.hidden) { - this.setStatusBarHidden(newStatusbarHiddenValue, skipLayout); - } - - // Activitybar visibility - const newActivityBarHiddenValue = !this.configurationService.getValue(Settings.ACTIVITYBAR_VISIBLE); - if (newActivityBarHiddenValue !== this.state.activityBar.hidden) { - this.setActivityBarHidden(newActivityBarHiddenValue, skipLayout); - } - } - - // Menubar visibility - const newMenubarVisibility = this.configurationService.getValue(Settings.MENUBAR_VISIBLE); - this.setMenubarVisibility(newMenubarVisibility, !!skipLayout); - } - - private setSideBarPosition(position: Position): void { - const activityBar = this.getPart(Parts.ACTIVITYBAR_PART); - const sideBar = this.getPart(Parts.SIDEBAR_PART); - const wasHidden = this.state.sideBar.hidden; - - if (this.state.sideBar.hidden) { - this.setSideBarHidden(false, true /* Skip Layout */); - } - - const newPositionValue = (position === Position.LEFT) ? 'left' : 'right'; - const oldPositionValue = (this.state.sideBar.position === Position.LEFT) ? 'left' : 'right'; - this.state.sideBar.position = position; - - // Adjust CSS - removeClass(activityBar.getContainer(), oldPositionValue); - removeClass(sideBar.getContainer(), oldPositionValue); - addClass(activityBar.getContainer(), newPositionValue); - addClass(sideBar.getContainer(), newPositionValue); - - // Update Styles - activityBar.updateStyles(); - sideBar.updateStyles(); - - // Layout - if (this.workbenchGrid instanceof Grid) { - if (!wasHidden) { - this.state.sideBar.width = this.workbenchGrid.getViewSize(this.sideBarPartView); - } - - this.workbenchGrid.removeView(this.sideBarPartView); - this.workbenchGrid.removeView(this.activityBarPartView); - - if (!this.state.panel.hidden && this.state.panel.position === Position.BOTTOM) { - this.workbenchGrid.removeView(this.panelPartView); - } - - this.layout(); - } else { - this.workbenchGrid.layout(); - } - } - - private initLayoutState(lifecycleService: ILifecycleService): void { - - // Fullscreen - this.state.fullscreen = isFullscreen(); - - // Menubar visibility - this.state.menuBar.visibility = this.configurationService.getValue(Settings.MENUBAR_VISIBLE); - - // Activity bar visibility - this.state.activityBar.hidden = !this.configurationService.getValue(Settings.ACTIVITYBAR_VISIBLE); - - // Sidebar visibility - this.state.sideBar.hidden = this.storageService.getBoolean(Storage.SIDEBAR_HIDDEN, StorageScope.WORKSPACE, this.contextService.getWorkbenchState() === WorkbenchState.EMPTY); - - // Sidebar position - this.state.sideBar.position = (this.configurationService.getValue(Settings.SIDEBAR_POSITION) === 'right') ? Position.RIGHT : Position.LEFT; - - // Sidebar viewlet - if (!this.state.sideBar.hidden) { - - // Only restore last viewlet if window was reloaded or we are in development mode - let viewletToRestore: string; - if (!this.environmentService.isBuilt || lifecycleService.startupKind === StartupKind.ReloadedWindow) { - viewletToRestore = this.storageService.get(SidebarPart.activeViewletSettingsKey, StorageScope.WORKSPACE, this.viewletService.getDefaultViewletId()); - } else { - viewletToRestore = this.viewletService.getDefaultViewletId(); - } - - if (viewletToRestore) { - this.state.sideBar.viewletToRestore = viewletToRestore; - } else { - this.state.sideBar.hidden = true; // we hide sidebar if there is no viewlet to restore - } - } - - // Editor centered layout - this.state.editor.restoreCentered = this.storageService.getBoolean(Storage.CENTERED_LAYOUT_ENABLED, StorageScope.WORKSPACE, false); - - // Editors to open - this.state.editor.editorsToOpen = this.resolveEditorsToOpen(); - - // Panel visibility - this.state.panel.hidden = this.storageService.getBoolean(Storage.PANEL_HIDDEN, StorageScope.WORKSPACE, true); - - // Panel position - this.updatePanelPosition(); - - // Panel to restore - if (!this.state.panel.hidden) { - const panelRegistry = Registry.as(PanelExtensions.Panels); - - let panelToRestore = this.storageService.get(PanelPart.activePanelSettingsKey, StorageScope.WORKSPACE, panelRegistry.getDefaultPanelId()); - if (!panelRegistry.hasPanel(panelToRestore)) { - panelToRestore = panelRegistry.getDefaultPanelId(); // fallback to default if panel is unknown - } - - if (panelToRestore) { - this.state.panel.panelToRestore = panelToRestore; - } else { - this.state.panel.hidden = true; // we hide panel if there is no panel to restore - } - } - - // Statusbar visibility - this.state.statusBar.hidden = !this.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); - } - - private resolveEditorsToOpen(): Promise | IResourceEditor[] { - const configuration = this.windowService.getConfiguration(); - const hasInitialFilesToOpen = this.hasInitialFilesToOpen(); - - // Only restore editors if we are not instructed to open files initially - this.state.editor.restoreEditors = !hasInitialFilesToOpen; - - // Files to open, diff or create - if (hasInitialFilesToOpen) { - - // Files to diff is exclusive - const filesToDiff = this.toInputs(configuration.filesToDiff, false); - if (filesToDiff && filesToDiff.length === 2) { - return [{ - leftResource: filesToDiff[0].resource, - rightResource: filesToDiff[1].resource, - options: { pinned: true }, - forceFile: true - }]; - } - - 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; - if (!isEmpty) { - return []; // do not open any empty untitled file if we restored editors from previous session - } - - return this.backupFileService.hasBackups().then(hasBackups => { - if (hasBackups) { - return []; // do not open any empty untitled file if we have backups to restore - } - - return [{}]; - }); - } - - return []; - } - - private hasInitialFilesToOpen(): boolean { - const configuration = this.windowService.getConfiguration(); - - return !!( - (configuration.filesToCreate && configuration.filesToCreate.length > 0) || - (configuration.filesToOpen && configuration.filesToOpen.length > 0) || - (configuration.filesToDiff && configuration.filesToDiff.length > 0)); - } - - private toInputs(paths: IPath[] | undefined, isNew: boolean): Array { - if (!paths || !paths.length) { - return []; - } - - return coalesce(paths.map(p => { - const resource = p.fileUri; - if (!resource) { - return undefined; - } - - let input: IResourceInput | IUntitledResourceInput; - if (isNew) { - input = { filePath: resource.fsPath, options: { pinned: true } } as IUntitledResourceInput; - } else { - input = { resource, options: { pinned: true }, forceFile: true } as IResourceInput; - } - - if (!isNew && typeof p.lineNumber === 'number') { - input.options!.selection = { - startLineNumber: p.lineNumber, - startColumn: p.columnNumber || 1 - }; - } - - return input; - })); - } - - private updatePanelPosition() { - const defaultPanelPosition = this.configurationService.getValue(Settings.PANEL_POSITION); - const panelPosition = this.storageService.get(Storage.PANEL_POSITION, StorageScope.WORKSPACE, defaultPanelPosition); - - this.state.panel.position = (panelPosition === 'right') ? Position.RIGHT : Position.BOTTOM; - } - - registerPart(part: Part): void { - this.parts.set(part.getId(), part); - } - - isRestored(): boolean { - return this.lifecycleService.phase >= LifecyclePhase.Restored; - } - - hasFocus(part: Parts): boolean { - const activeElement = document.activeElement; - if (!activeElement) { - return false; - } - - const container = this.getContainer(part); - - return isAncestor(activeElement, container); - } - - getContainer(part: Parts): HTMLElement { - switch (part) { - case Parts.TITLEBAR_PART: - return this.getPart(Parts.TITLEBAR_PART).getContainer(); - case Parts.ACTIVITYBAR_PART: - return this.getPart(Parts.ACTIVITYBAR_PART).getContainer(); - case Parts.SIDEBAR_PART: - return this.getPart(Parts.SIDEBAR_PART).getContainer(); - case Parts.PANEL_PART: - return this.getPart(Parts.PANEL_PART).getContainer(); - case Parts.EDITOR_PART: - return this.getPart(Parts.EDITOR_PART).getContainer(); - case Parts.STATUSBAR_PART: - return this.getPart(Parts.STATUSBAR_PART).getContainer(); - } - } - - isVisible(part: Parts): boolean { - switch (part) { - case Parts.TITLEBAR_PART: - if (getTitleBarStyle(this.configurationService, this.environmentService) === 'native') { - return false; - } else if (!this.state.fullscreen) { - return true; - } else if (isMacintosh) { - return false; - } else if (this.state.menuBar.visibility === 'visible') { - return true; - } else if (this.state.menuBar.visibility === 'toggle' || this.state.menuBar.visibility === 'default') { - return this.state.menuBar.toggled; - } - - return false; - case Parts.SIDEBAR_PART: - return !this.state.sideBar.hidden; - case Parts.PANEL_PART: - return !this.state.panel.hidden; - case Parts.STATUSBAR_PART: - return !this.state.statusBar.hidden; - case Parts.ACTIVITYBAR_PART: - return !this.state.activityBar.hidden; - case Parts.EDITOR_PART: - return this.workbenchGrid instanceof Grid ? !this.state.editor.hidden : true; - } - - return true; // any other part cannot be hidden - } - - getTitleBarOffset(): number { - let offset = 0; - if (this.isVisible(Parts.TITLEBAR_PART)) { - if (this.workbenchGrid instanceof Grid) { - offset = this.getPart(Parts.TITLEBAR_PART).maximumHeight; - } else { - offset = this.workbenchGrid.partLayoutInfo.titlebar.height; - - if (isMacintosh || this.state.menuBar.visibility === 'hidden') { - offset /= getZoomFactor(); - } - } - } - - return offset; - } - - getWorkbenchElement(): HTMLElement { - return this.workbench; - } - - toggleZenMode(skipLayout?: boolean, restoring = false): void { - this.state.zenMode.active = !this.state.zenMode.active; - this.state.zenMode.transitionDisposeables = dispose(this.state.zenMode.transitionDisposeables); - - const setLineNumbers = (lineNumbers: any) => this.editorService.visibleTextEditorWidgets.forEach(editor => editor.updateOptions({ lineNumbers })); - - // Check if zen mode transitioned to full screen and if now we are out of zen mode - // -> we need to go out of full screen (same goes for the centered editor layout) - let toggleFullScreen = false; - - // Zen Mode Active - if (this.state.zenMode.active) { - const config: { - fullScreen: boolean; - centerLayout: boolean; - hideTabs: boolean; - hideActivityBar: boolean; - hideStatusBar: boolean; - hideLineNumbers: boolean; - } = this.configurationService.getValue('zenMode'); - - toggleFullScreen = !this.state.fullscreen && config.fullScreen; - - this.state.zenMode.transitionedToFullScreen = restoring ? config.fullScreen : toggleFullScreen; - this.state.zenMode.transitionedToCenteredEditorLayout = !this.isEditorLayoutCentered() && config.centerLayout; - this.state.zenMode.wasSideBarVisible = this.isVisible(Parts.SIDEBAR_PART); - this.state.zenMode.wasPanelVisible = this.isVisible(Parts.PANEL_PART); - - this.setPanelHidden(true, true); - this.setSideBarHidden(true, true); - - if (config.hideActivityBar) { - this.setActivityBarHidden(true, true); - } - - if (config.hideStatusBar) { - this.setStatusBarHidden(true, true); - } - - if (config.hideLineNumbers) { - setLineNumbers('off'); - this.state.zenMode.transitionDisposeables.push(this.editorService.onDidVisibleEditorsChange(() => setLineNumbers('off'))); - } - - if (config.hideTabs && this.editorGroupService.partOptions.showTabs) { - this.state.zenMode.transitionDisposeables.push(this.editorGroupService.enforcePartOptions({ showTabs: false })); - } - - if (config.centerLayout) { - this.centerEditorLayout(true, true); - } - } - - // Zen Mode Inactive - else { - if (this.state.zenMode.wasPanelVisible) { - this.setPanelHidden(false, true); - } - - if (this.state.zenMode.wasSideBarVisible) { - this.setSideBarHidden(false, true); - } - - if (this.state.zenMode.transitionedToCenteredEditorLayout) { - this.centerEditorLayout(false, true); - } - - setLineNumbers(this.configurationService.getValue('editor.lineNumbers')); - - // Status bar and activity bar visibility come from settings -> update their visibility. - this.doUpdateLayoutConfiguration(true); - - this.editorGroupService.activeGroup.focus(); - - toggleFullScreen = this.state.zenMode.transitionedToFullScreen && this.state.fullscreen; - } - - if (!skipLayout) { - this.layout(); - } - - if (toggleFullScreen) { - this.windowService.toggleFullScreen(); - } - - // Event - this._onZenMode.fire(this.state.zenMode.active); - } - - private setStatusBarHidden(hidden: boolean, skipLayout?: boolean): void { - this.state.statusBar.hidden = hidden; - - // Adjust CSS - if (hidden) { - addClass(this.workbench, 'nostatusbar'); - } else { - removeClass(this.workbench, 'nostatusbar'); - } - - // Layout - if (!skipLayout) { - if (this.workbenchGrid instanceof Grid) { - this.layout(); - } else { - this.workbenchGrid.layout(); - } - } - } - - private createWorkbenchLayout(instantiationService: IInstantiationService): void { - const titleBar = this.getPart(Parts.TITLEBAR_PART); - const editorPart = this.getPart(Parts.EDITOR_PART); - const activityBar = this.getPart(Parts.ACTIVITYBAR_PART); - const panelPart = this.getPart(Parts.PANEL_PART); - const sideBar = this.getPart(Parts.SIDEBAR_PART); - const statusBar = this.getPart(Parts.STATUSBAR_PART); - - if (this.configurationService.getValue('workbench.useExperimentalGridLayout')) { - - // Create view wrappers for all parts - this.titleBarPartView = new View(titleBar); - this.sideBarPartView = new View(sideBar); - this.activityBarPartView = new View(activityBar); - this.editorPartView = new View(editorPart); - this.panelPartView = new View(panelPart); - this.statusBarPartView = new View(statusBar); - - this.workbenchGrid = new Grid(this.editorPartView, { proportionalLayout: false }); - - this.workbench.prepend(this.workbenchGrid.element); - } else { - this.workbenchGrid = instantiationService.createInstance( - WorkbenchLegacyLayout, - this.parent, - this.workbench, - { - titlebar: titleBar, - activitybar: activityBar, - editor: editorPart, - sidebar: sideBar, - panel: panelPart, - statusbar: statusBar, - } - ); - } - } - - layout(options?: ILayoutOptions): void { - if (!this.disposed) { - this._dimension = getClientArea(this.parent); - - if (this.workbenchGrid instanceof Grid) { - position(this.workbench, 0, 0, 0, 0, 'relative'); - size(this.workbench, this._dimension.width, this._dimension.height); - - // Layout the grid widget - this.workbenchGrid.layout(this._dimension.width, this._dimension.height); - - // Layout grid views - this.layoutGrid(); - } else { - this.workbenchGrid.layout(options); - } - - // Emit as event - this._onLayout.fire(this._dimension); - } - } - - private layoutGrid(): void { - if (!(this.workbenchGrid instanceof Grid)) { - return; - } - - let panelInGrid = this.workbenchGrid.hasView(this.panelPartView); - let sidebarInGrid = this.workbenchGrid.hasView(this.sideBarPartView); - let activityBarInGrid = this.workbenchGrid.hasView(this.activityBarPartView); - let statusBarInGrid = this.workbenchGrid.hasView(this.statusBarPartView); - let titlebarInGrid = this.workbenchGrid.hasView(this.titleBarPartView); - - // Add parts to grid - if (!statusBarInGrid) { - this.workbenchGrid.addView(this.statusBarPartView, Sizing.Split, this.editorPartView, Direction.Down); - statusBarInGrid = true; - } - - if (!titlebarInGrid && getTitleBarStyle(this.configurationService, this.environmentService) === 'custom') { - this.workbenchGrid.addView(this.titleBarPartView, Sizing.Split, this.editorPartView, Direction.Up); - titlebarInGrid = true; - } - - if (!activityBarInGrid) { - this.workbenchGrid.addView(this.activityBarPartView, Sizing.Split, panelInGrid && this.state.sideBar.position === this.state.panel.position ? this.panelPartView : this.editorPartView, this.state.sideBar.position === Position.RIGHT ? Direction.Right : Direction.Left); - activityBarInGrid = true; - } - - if (!sidebarInGrid) { - this.workbenchGrid.addView(this.sideBarPartView, this.state.sideBar.width !== undefined ? this.state.sideBar.width : Sizing.Split, this.activityBarPartView, this.state.sideBar.position === Position.LEFT ? Direction.Right : Direction.Left); - sidebarInGrid = true; - } - - if (!panelInGrid) { - this.workbenchGrid.addView(this.panelPartView, this.getPanelDimension(this.state.panel.position) !== undefined ? this.getPanelDimension(this.state.panel.position) : Sizing.Split, this.editorPartView, this.state.panel.position === Position.BOTTOM ? Direction.Down : Direction.Right); - panelInGrid = true; - } - - // Hide parts - if (this.state.panel.hidden) { - this.panelPartView.hide(); - } - - if (this.state.statusBar.hidden) { - this.statusBarPartView.hide(); - } - - if (!this.isVisible(Parts.TITLEBAR_PART)) { - this.titleBarPartView.hide(); - } - - if (this.state.activityBar.hidden) { - this.activityBarPartView.hide(); - } - - if (this.state.sideBar.hidden) { - this.sideBarPartView.hide(); - } - - if (this.state.editor.hidden) { - this.editorPartView.hide(); - } - - // Show visible parts - if (!this.state.editor.hidden) { - this.editorPartView.show(); - } - - if (!this.state.statusBar.hidden) { - this.statusBarPartView.show(); - } - - if (this.isVisible(Parts.TITLEBAR_PART)) { - this.titleBarPartView.show(); - } - - if (!this.state.activityBar.hidden) { - this.activityBarPartView.show(); - } - - if (!this.state.sideBar.hidden) { - this.sideBarPartView.show(); - } - - if (!this.state.panel.hidden) { - this.panelPartView.show(); - } - } - - private getPanelDimension(position: Position): number { - return position === Position.BOTTOM ? this.state.panel.height : this.state.panel.width; - } - - isEditorLayoutCentered(): boolean { - return this.state.editor.centered; - } - - centerEditorLayout(active: boolean, skipLayout?: boolean): void { - this.state.editor.centered = active; - - this.storageService.store(Storage.CENTERED_LAYOUT_ENABLED, active, StorageScope.WORKSPACE); - - let smartActive = active; - if (this.editorGroupService.groups.length > 1 && this.configurationService.getValue('workbench.editor.centeredLayoutAutoResize')) { - smartActive = false; // Respect the auto resize setting - do not go into centered layout if there is more than 1 group. - } - - // Enter Centered Editor Layout - if (this.editorGroupService.isLayoutCentered() !== smartActive) { - this.editorGroupService.centerLayout(smartActive); - - if (!skipLayout) { - this.layout(); - } - } - } - - resizePart(part: Parts, sizeChange: number): void { - let view: View; - switch (part) { - case Parts.SIDEBAR_PART: - view = this.sideBarPartView; - case Parts.PANEL_PART: - view = this.panelPartView; - case Parts.EDITOR_PART: - view = this.editorPartView; - if (this.workbenchGrid instanceof Grid) { - this.workbenchGrid.resizeView(view, this.workbenchGrid.getViewSize(view) + sizeChange); - } else { - this.workbenchGrid.resizePart(part, sizeChange); - } - break; - default: - return; // Cannot resize other parts - } - } - - setActivityBarHidden(hidden: boolean, skipLayout?: boolean): void { - this.state.activityBar.hidden = hidden; - - // Layout - if (!skipLayout) { - if (this.workbenchGrid instanceof Grid) { - this.layout(); - } else { - this.workbenchGrid.layout(); - } - } - } - - setEditorHidden(hidden: boolean, skipLayout?: boolean): void { - if (!(this.workbenchGrid instanceof Grid) || hidden === this.state.editor.hidden) { - return; - } - - this.state.editor.hidden = hidden; - - // The editor and the panel cannot be hidden at the same time - if (this.state.editor.hidden && this.state.panel.hidden) { - this.setPanelHidden(false, true); - } - - if (!skipLayout) { - this.layout(); - } - } - - setSideBarHidden(hidden: boolean, skipLayout?: boolean): void { - this.state.sideBar.hidden = hidden; - - // Adjust CSS - if (hidden) { - addClass(this.workbench, 'nosidebar'); - } else { - removeClass(this.workbench, 'nosidebar'); - } - - // If sidebar becomes hidden, also hide the current active Viewlet if any - if (hidden && this.viewletService.getActiveViewlet()) { - this.viewletService.hideActiveViewlet(); - - // Pass Focus to Editor or Panel if Sidebar is now hidden - const activePanel = this.panelService.getActivePanel(); - if (this.hasFocus(Parts.PANEL_PART) && activePanel) { - activePanel.focus(); - } else { - this.editorGroupService.activeGroup.focus(); - } - } - - // If sidebar becomes visible, show last active Viewlet or default viewlet - else if (!hidden && !this.viewletService.getActiveViewlet()) { - const viewletToOpen = this.viewletService.getLastActiveViewletId(); - if (viewletToOpen) { - const viewlet = this.viewletService.openViewlet(viewletToOpen, true); - if (!viewlet) { - this.viewletService.openViewlet(this.viewletService.getDefaultViewletId(), true); - } - } - } - - // Remember in settings - const defaultHidden = this.contextService.getWorkbenchState() === WorkbenchState.EMPTY; - if (hidden !== defaultHidden) { - this.storageService.store(Storage.SIDEBAR_HIDDEN, hidden ? 'true' : 'false', StorageScope.WORKSPACE); - } else { - this.storageService.remove(Storage.SIDEBAR_HIDDEN, StorageScope.WORKSPACE); - } - - // Layout - if (!skipLayout) { - if (this.workbenchGrid instanceof Grid) { - this.layout(); - } else { - this.workbenchGrid.layout(); - } - } - } - - setPanelHidden(hidden: boolean, skipLayout?: boolean): void { - this.state.panel.hidden = hidden; - - // Adjust CSS - if (hidden) { - addClass(this.workbench, 'nopanel'); - } else { - removeClass(this.workbench, 'nopanel'); - } - - // If panel part becomes hidden, also hide the current active panel if any - if (hidden && this.panelService.getActivePanel()) { - this.panelService.hideActivePanel(); - this.editorGroupService.activeGroup.focus(); // Pass focus to editor group if panel part is now hidden - } - - // If panel part becomes visible, show last active panel or default panel - else if (!hidden && !this.panelService.getActivePanel()) { - const panelToOpen = this.panelService.getLastActivePanelId(); - if (panelToOpen) { - const focus = !skipLayout; - this.panelService.openPanel(panelToOpen, focus); - } - } - - // Remember in settings - if (!hidden) { - this.storageService.store(Storage.PANEL_HIDDEN, 'false', StorageScope.WORKSPACE); - } else { - this.storageService.remove(Storage.PANEL_HIDDEN, StorageScope.WORKSPACE); - } - - // The editor and panel cannot be hidden at the same time - if (hidden && this.state.editor.hidden) { - this.setEditorHidden(false, true); - } - - // Layout - if (!skipLayout) { - if (this.workbenchGrid instanceof Grid) { - this.layout(); - } else { - this.workbenchGrid.layout(); - } - } - } - - toggleMaximizedPanel(): void { - if (this.workbenchGrid instanceof Grid) { - this.workbenchGrid.maximizeViewSize(this.panelPartView); - } else { - this.workbenchGrid.layout({ toggleMaximizedPanel: true, source: Parts.PANEL_PART }); - } - } - - isPanelMaximized(): boolean { - if (this.workbenchGrid instanceof Grid) { - try { - return this.workbenchGrid.getViewSize2(this.panelPartView).height === this.getPart(Parts.PANEL_PART).maximumHeight; - } catch (e) { - return false; - } - } else { - return this.workbenchGrid.isPanelMaximized(); - } - } - - getSideBarPosition(): Position { - return this.state.sideBar.position; - } - - setMenubarVisibility(visibility: MenuBarVisibility, skipLayout: boolean): void { - if (this.state.menuBar.visibility !== visibility) { - this.state.menuBar.visibility = visibility; - - // Layout - if (!skipLayout) { - if (this.workbenchGrid instanceof Grid) { - const dimensions = getClientArea(this.parent); - this.workbenchGrid.layout(dimensions.width, dimensions.height); - } else { - this.workbenchGrid.layout(); - } - } - } - } - - getMenubarVisibility(): MenuBarVisibility { - return this.state.menuBar.visibility; - } - - getPanelPosition(): Position { - return this.state.panel.position; - } - - setPanelPosition(position: Position): void { - const panelPart = this.getPart(Parts.PANEL_PART); - const wasHidden = this.state.panel.hidden; - - if (this.state.panel.hidden) { - this.setPanelHidden(false, true /* Skip Layout */); - } else { - this.savePanelDimension(); - } - - const newPositionValue = (position === Position.BOTTOM) ? 'bottom' : 'right'; - const oldPositionValue = (this.state.panel.position === Position.BOTTOM) ? 'bottom' : 'right'; - this.state.panel.position = position; - - function positionToString(position: Position): string { - switch (position) { - case Position.LEFT: return 'left'; - case Position.RIGHT: return 'right'; - case Position.BOTTOM: return 'bottom'; - } - } - - this.storageService.store(Storage.PANEL_POSITION, positionToString(this.state.panel.position), StorageScope.WORKSPACE); - - // Adjust CSS - removeClass(panelPart.getContainer(), oldPositionValue); - addClass(panelPart.getContainer(), newPositionValue); - - // Update Styles - panelPart.updateStyles(); - - // Layout - if (this.workbenchGrid instanceof Grid) { - if (!wasHidden) { - this.savePanelDimension(); - } - - this.workbenchGrid.removeView(this.panelPartView); - this.layout(); - } else { - this.workbenchGrid.layout(); - } - } - - private savePanelDimension(): void { - if (!(this.workbenchGrid instanceof Grid)) { - return; - } - - if (this.state.panel.position === Position.BOTTOM) { - this.state.panel.height = this.workbenchGrid.getViewSize(this.panelPartView); - } else { - this.state.panel.width = this.workbenchGrid.getViewSize(this.panelPartView); - } - } - - private saveLayoutState(e: IWillSaveStateEvent): void { - - // Zen Mode - if (this.state.zenMode.active) { - this.storageService.store(Storage.ZEN_MODE_ENABLED, true, StorageScope.WORKSPACE); - } else { - this.storageService.remove(Storage.ZEN_MODE_ENABLED, StorageScope.WORKSPACE); - } - - if (e.reason === WillSaveStateReason.SHUTDOWN && this.state.zenMode.active) { - if (!this.configurationService.getValue(Settings.ZEN_MODE_RESTORE)) { - this.toggleZenMode(true); // We will not restore zen mode, need to clear all zen mode state changes - } - } - } - - private getPart(key: Parts): Part { - const part = this.parts.get(key); - if (!part) { - throw new Error('unknown part'); - } - return part; - } - - dispose(): void { - super.dispose(); - - this.disposed = true; - } - - //#endregion } diff --git a/src/vs/workbench/buildfile.js b/src/vs/workbench/buildfile.js index 83c43b0ace9..04e3814fa84 100644 --- a/src/vs/workbench/buildfile.js +++ b/src/vs/workbench/buildfile.js @@ -5,18 +5,20 @@ 'use strict'; function createModuleDescription(name, exclude) { - var result = {}; - var excludes = ['vs/css', 'vs/nls']; + const result = {}; + + let excludes = ['vs/css', 'vs/nls']; result.name = name; if (Array.isArray(exclude) && exclude.length > 0) { excludes = excludes.concat(exclude); } result.exclude = excludes; + return result; } exports.collectModules = function () { - var modules = [ + return [ createModuleDescription('vs/workbench/contrib/output/common/outputLinkComputer', ['vs/base/common/worker/simpleWorker', 'vs/editor/common/services/editorSimpleWorker']), createModuleDescription('vs/workbench/contrib/debug/node/telemetryApp', []), @@ -28,6 +30,4 @@ exports.collectModules = function () { createModuleDescription('vs/workbench/services/extensions/node/extensionHostProcess', []), ]; - - return modules; }; \ No newline at end of file diff --git a/src/vs/workbench/contrib/comments/electron-browser/commentNode.ts b/src/vs/workbench/contrib/comments/electron-browser/commentNode.ts index 0baa989fb19..fc507f47afa 100644 --- a/src/vs/workbench/contrib/comments/electron-browser/commentNode.ts +++ b/src/vs/workbench/contrib/comments/electron-browser/commentNode.ts @@ -48,8 +48,9 @@ export class CommentNode extends Disposable { private _editAction: Action; private _commentEditContainer: HTMLElement; private _commentDetailsContainer: HTMLElement; + private _actionsToolbarContainer: HTMLElement; private _reactionsActionBar?: ActionBar; - private _actionsContainer?: HTMLElement; + private _reactionActionsContainer?: HTMLElement; private _commentEditor: SimpleCommentEditor | null; private _commentEditorDisposables: IDisposable[] = []; private _commentEditorModel: ITextModel; @@ -125,10 +126,19 @@ export class CommentNode extends Disposable { this._isPendingLabel = dom.append(header, dom.$('span.isPending')); - if (this.comment.isDraft) { + if (this.comment.label) { + this._isPendingLabel.innerText = this.comment.label; + } else if (this.comment.isDraft) { this._isPendingLabel.innerText = 'Pending'; + } else { + this._isPendingLabel.innerText = ''; } + this._actionsToolbarContainer = dom.append(header, dom.$('.comment-actions.hidden')); + this.createActionsToolbar(); + } + + private createActionsToolbar() { const actions: Action[] = []; let reactionGroup = this.commentService.getReactionGroup(this.owner); @@ -144,7 +154,7 @@ export class CommentNode extends Disposable { } if (this.comment.canEdit || this.comment.editCommand) { - this._editAction = this.createEditAction(commentDetailsContainer); + this._editAction = this.createEditAction(this._commentDetailsContainer); actions.push(this._editAction); } @@ -154,9 +164,7 @@ export class CommentNode extends Disposable { } if (actions.length) { - const actionsContainer = dom.append(header, dom.$('.comment-actions.hidden')); - - this.toolbar = new ToolBar(actionsContainer, this.contextMenuService, { + this.toolbar = new ToolBar(this._actionsToolbarContainer, this.contextMenuService, { actionItemProvider: action => { if (action.id === ToggleReactionsAction.ID) { return new DropdownMenuActionItem( @@ -177,7 +185,7 @@ export class CommentNode extends Disposable { orientation: ActionsOrientation.HORIZONTAL }); - this.registerActionBarListeners(actionsContainer); + this.registerActionBarListeners(this._actionsToolbarContainer); this.toolbar.setActions(actions, [])(); this._toDispose.push(this.toolbar); } @@ -293,8 +301,8 @@ export class CommentNode extends Disposable { } private createReactionsContainer(commentDetailsContainer: HTMLElement): void { - this._actionsContainer = dom.append(commentDetailsContainer, dom.$('div.comment-reactions')); - this._reactionsActionBar = new ActionBar(this._actionsContainer, { + this._reactionActionsContainer = dom.append(commentDetailsContainer, dom.$('div.comment-reactions')); + this._reactionsActionBar = new ActionBar(this._reactionActionsContainer, { actionItemProvider: action => { if (action.id === ToggleReactionsAction.ID) { return new DropdownMenuActionItem( @@ -573,8 +581,15 @@ export class CommentNode extends Disposable { this._body.appendChild(this._md); } + const shouldUpdateActions = newComment.editCommand !== this.comment.editCommand || newComment.deleteCommand !== this.comment.deleteCommand; this.comment = newComment; + if (shouldUpdateActions) { + dom.clearNode(this._actionsToolbarContainer); + this.createActionsToolbar(); + } + + if (newComment.label) { this._isPendingLabel.innerText = newComment.label; } else if (newComment.isDraft) { @@ -584,8 +599,8 @@ export class CommentNode extends Disposable { } // update comment reactions - if (this._actionsContainer) { - this._actionsContainer.remove(); + if (this._reactionActionsContainer) { + this._reactionActionsContainer.remove(); } if (this._reactionsActionBar) { diff --git a/src/vs/workbench/contrib/debug/electron-browser/breakpointWidget.ts b/src/vs/workbench/contrib/debug/browser/breakpointWidget.ts similarity index 99% rename from src/vs/workbench/contrib/debug/electron-browser/breakpointWidget.ts rename to src/vs/workbench/contrib/debug/browser/breakpointWidget.ts index d7a864094b6..9524567935b 100644 --- a/src/vs/workbench/contrib/debug/electron-browser/breakpointWidget.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointWidget.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!../browser/media/breakpointWidget'; +import 'vs/css!./media/breakpointWidget'; import * as nls from 'vs/nls'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { SelectBox, ISelectOptionItem } from 'vs/base/browser/ui/selectBox/selectBox'; @@ -161,7 +161,7 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi })); this.input.setPosition({ lineNumber: 1, column: this.input.getModel().getLineMaxColumn(1) }); // Due to an electron bug we have to do the timeout, otherwise we do not get focus - setTimeout(() => this.input.focus(), 100); + setTimeout(() => this.input.focus(), 150); } public close(success: boolean): void { diff --git a/src/vs/workbench/contrib/debug/electron-browser/callStackView.ts b/src/vs/workbench/contrib/debug/browser/callStackView.ts similarity index 94% rename from src/vs/workbench/contrib/debug/electron-browser/callStackView.ts rename to src/vs/workbench/contrib/debug/browser/callStackView.ts index a7dca3f1786..134e17c1b2f 100644 --- a/src/vs/workbench/contrib/debug/electron-browser/callStackView.ts +++ b/src/vs/workbench/contrib/debug/browser/callStackView.ts @@ -15,21 +15,18 @@ import { MenuId, IMenu, IMenuService } from 'vs/platform/actions/common/actions' import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { renderViewTree } from 'vs/workbench/contrib/debug/browser/baseDebugView'; import { IAction } from 'vs/base/common/actions'; -import { RestartAction, StopAction, ContinueAction, StepOverAction, StepIntoAction, StepOutAction, PauseAction, RestartFrameAction, TerminateThreadAction } from 'vs/workbench/contrib/debug/browser/debugActions'; -import { CopyStackTraceAction } from 'vs/workbench/contrib/debug/electron-browser/electronDebugActions'; +import { RestartAction, StopAction, ContinueAction, StepOverAction, StepIntoAction, StepOutAction, PauseAction, RestartFrameAction, TerminateThreadAction, CopyStackTraceAction } from 'vs/workbench/contrib/debug/browser/debugActions'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IViewletPanelOptions, ViewletPanel } from 'vs/workbench/browser/parts/views/panelViewlet'; import { ILabelService } from 'vs/platform/label/common/label'; -import { DebugSession } from 'vs/workbench/contrib/debug/electron-browser/debugSession'; import { IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; import { fillInContextMenuActions } from 'vs/platform/actions/browser/menuItemActionItem'; import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { ITreeRenderer, ITreeNode, ITreeContextMenuEvent, IAsyncDataSource } from 'vs/base/browser/ui/tree/tree'; -import { TreeResourceNavigator2, WorkbenchAsyncDataTree, IListService } from 'vs/platform/list/browser/listService'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { TreeResourceNavigator2, WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService'; import { onUnexpectedError } from 'vs/base/common/errors'; import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; import { createMatches, FuzzyScore } from 'vs/base/common/filters'; @@ -48,7 +45,7 @@ export class CallStackView extends ViewletPanel { private ignoreFocusStackFrameEvent: boolean; private callStackItemType: IContextKey; private dataSource: CallStackDataSource; - private tree: WorkbenchAsyncDataTree; + private tree: WorkbenchAsyncDataTree; private contributedContextMenu: IMenu; constructor( @@ -60,9 +57,7 @@ export class CallStackView extends ViewletPanel { @IEditorService private readonly editorService: IEditorService, @IConfigurationService configurationService: IConfigurationService, @IMenuService menuService: IMenuService, - @IContextKeyService private readonly contextKeyService: IContextKeyService, - @IThemeService private readonly themeService: IThemeService, - @IListService private readonly listService: IListService + @IContextKeyService readonly contextKeyService: IContextKeyService, ) { super({ ...(options as IViewletPanelOptions), ariaHeaderLabel: nls.localize('callstackSection', "Call Stack Section") }, keybindingService, contextMenuService, configurationService); this.callStackItemType = CONTEXT_CALLSTACK_ITEM_TYPE.bindTo(contextKeyService); @@ -105,7 +100,7 @@ export class CallStackView extends ViewletPanel { const treeContainer = renderViewTree(container); this.dataSource = new CallStackDataSource(); - this.tree = new WorkbenchAsyncDataTree(treeContainer, new CallStackDelegate(), [ + this.tree = this.instantiationService.createInstance(WorkbenchAsyncDataTree, treeContainer, new CallStackDelegate(), [ new SessionsRenderer(), new ThreadsRenderer(), this.instantiationService.createInstance(StackFramesRenderer), @@ -124,12 +119,12 @@ export class CallStackView extends ViewletPanel { return `showMore ${element[0].getId()}`; } - return element.getId(); + return (element).getId(); } }, keyboardNavigationLabelProvider: { getKeyboardNavigationLabel: e => { - if (e instanceof DebugSession) { + if (isDebugSession(e)) { return e.getLabel(); } if (e instanceof Thread) { @@ -145,7 +140,7 @@ export class CallStackView extends ViewletPanel { return nls.localize('showMoreStackFrames2', "Show More Stack Frames"); } } - }, this.contextKeyService, this.listService, this.themeService, this.configurationService, this.keybindingService); + }) as WorkbenchAsyncDataTree; this.tree.setInput(this.debugService.getModel()).then(undefined, onUnexpectedError); @@ -173,7 +168,7 @@ export class CallStackView extends ViewletPanel { if (element instanceof Thread) { focusStackFrame(undefined, element, element.session); } - if (element instanceof DebugSession) { + if (isDebugSession(element)) { focusStackFrame(undefined, undefined, element); } if (element instanceof ThreadAndSessionIds) { @@ -271,7 +266,7 @@ export class CallStackView extends ViewletPanel { private onContextMenu(e: ITreeContextMenuEvent): void { const actions: IAction[] = []; const element = e.element; - if (element instanceof DebugSession) { + if (isDebugSession(element)) { this.callStackItemType.set('session'); actions.push(this.instantiationService.createInstance(RestartAction, RestartAction.ID, RestartAction.LABEL)); actions.push(new StopAction(StopAction.ID, StopAction.LABEL, this.debugService, this.keybindingService)); @@ -294,7 +289,7 @@ export class CallStackView extends ViewletPanel { if (element.thread.session.capabilities.supportsRestartFrame) { actions.push(new RestartFrameAction(RestartFrameAction.ID, RestartFrameAction.LABEL, this.debugService, this.keybindingService)); } - actions.push(new CopyStackTraceAction(CopyStackTraceAction.ID, CopyStackTraceAction.LABEL)); + actions.push(this.instantiationService.createInstance(CopyStackTraceAction, CopyStackTraceAction.ID, CopyStackTraceAction.LABEL)); } else { this.callStackItemType.reset(); } @@ -548,7 +543,7 @@ class CallStackDelegate implements IListVirtualDelegate { } getTemplateId(element: CallStackItem): string { - if (element instanceof DebugSession) { + if (isDebugSession(element)) { return SessionsRenderer.ID; } if (element instanceof Thread) { @@ -573,6 +568,10 @@ function isDebugModel(obj: any): obj is IDebugModel { return typeof obj.getSessions === 'function'; } +function isDebugSession(obj: any): obj is IDebugSession { + return typeof obj.getAllThreads === 'function'; +} + function isDeemphasized(frame: IStackFrame): boolean { return frame.source.presentationHint === 'deemphasize' || frame.presentationHint === 'deemphasize'; } @@ -581,7 +580,7 @@ class CallStackDataSource implements IAsyncDataSource { @@ -597,7 +596,7 @@ class CallStackDataSource implements IAsyncDataSourcethreads[0]) : Promise.resolve(threads); - } else if (element instanceof DebugSession) { + } else if (isDebugSession(element)) { return Promise.resolve(element.getAllThreads()); } else { return this.getThreadChildren(element); @@ -671,7 +670,7 @@ class CallStackAccessibilityProvider implements IAccessibilityProvider= 30 && code <= 37) || (code >= 90 && code <= 97)) { - styleNames.push('code-foreground-' + code); - } else if (code === 39) { - // Remove all foreground colour codes + } else if (code === 39 || (code >= 30 && code <= 37) || (code >= 90 && code <= 97)) { + // Remove all previous foreground colour codes styleNames = styleNames.filter(style => !style.match(/^code-foreground-\d+$/)); - } else if ((code >= 40 && code <= 47) || (code >= 100 && code <= 107)) { - styleNames.push('code-background-' + code); - } else if (code === 49) { - // Remove all background colour codes + + if (code !== 39) { + styleNames.push('code-foreground-' + code); + } + } else if (code === 49 || (code >= 40 && code <= 47) || (code >= 100 && code <= 107)) { + // Remove all previous background colour codes styleNames = styleNames.filter(style => !style.match(/^code-background-\d+$/)); + + if (code !== 49) { + styleNames.push('code-background-' + code); + } } } diff --git a/src/vs/workbench/contrib/debug/browser/debugActions.ts b/src/vs/workbench/contrib/debug/browser/debugActions.ts index 85d9fba3f6f..c922e954607 100644 --- a/src/vs/workbench/contrib/debug/browser/debugActions.ts +++ b/src/vs/workbench/contrib/debug/browser/debugActions.ts @@ -22,6 +22,8 @@ import { first } from 'vs/base/common/arrays'; import { IHistoryService } from 'vs/workbench/services/history/common/history'; import { memoize } from 'vs/base/common/decorators'; import { AsyncDataTree } from 'vs/base/browser/ui/tree/asyncDataTree'; +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { ITextResourcePropertiesService } from 'vs/editor/common/services/resourceConfiguration'; export abstract class AbstractDebugAction extends Action { @@ -810,3 +812,85 @@ export class ReplCollapseAllAction extends CollapseAction { }); } } + +export class CopyValueAction extends Action { + static readonly ID = 'workbench.debug.viewlet.action.copyValue'; + static LABEL = nls.localize('copyValue', "Copy Value"); + + constructor( + id: string, label: string, private value: any, private context: string, + @IDebugService private readonly debugService: IDebugService, + @IClipboardService private readonly clipboardService: IClipboardService + ) { + super(id, label, 'debug-action copy-value'); + this._enabled = typeof this.value === 'string' || (this.value instanceof Variable && !!this.value.evaluateName); + } + + public run(): Promise { + const stackFrame = this.debugService.getViewModel().focusedStackFrame; + const session = this.debugService.getViewModel().focusedSession; + + if (this.value instanceof Variable && stackFrame && session && this.value.evaluateName) { + return session.evaluate(this.value.evaluateName, stackFrame.frameId, this.context).then(result => { + this.clipboardService.writeText(result.body.result); + }, err => this.clipboardService.writeText(this.value.value)); + } + + this.clipboardService.writeText(this.value); + return Promise.resolve(undefined); + } +} + +export class CopyEvaluatePathAction extends Action { + static readonly ID = 'workbench.debug.viewlet.action.copyEvaluatePath'; + static LABEL = nls.localize('copyAsExpression', "Copy as Expression"); + + constructor( + id: string, label: string, private value: Variable, + @IClipboardService private readonly clipboardService: IClipboardService + ) { + super(id, label); + this._enabled = this.value && !!this.value.evaluateName; + } + + public run(): Promise { + this.clipboardService.writeText(this.value.evaluateName!); + return Promise.resolve(undefined); + } +} + +export class CopyAction extends Action { + static readonly ID = 'workbench.debug.action.copy'; + static LABEL = nls.localize('copy', "Copy"); + + constructor( + id: string, label: string, + @IClipboardService private readonly clipboardService: IClipboardService + ) { + super(id, label); + } + + public run(): Promise { + this.clipboardService.writeText(window.getSelection().toString()); + return Promise.resolve(undefined); + } +} + +export class CopyStackTraceAction extends Action { + static readonly ID = 'workbench.action.debug.copyStackTrace'; + static LABEL = nls.localize('copyStackTrace', "Copy Call Stack"); + + constructor( + id: string, label: string, + @IClipboardService private readonly clipboardService: IClipboardService, + @ITextResourcePropertiesService private readonly textResourcePropertiesService: ITextResourcePropertiesService + ) { + super(id, label); + } + + public run(frame: IStackFrame): Promise { + const eol = this.textResourcePropertiesService.getEOL(frame.source.uri); + this.clipboardService.writeText(frame.thread.getCallStack().map(sf => sf.toString()).join(eol)); + return Promise.resolve(undefined); + } +} \ No newline at end of file diff --git a/src/vs/workbench/contrib/debug/electron-browser/debugEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts similarity index 99% rename from src/vs/workbench/contrib/debug/electron-browser/debugEditorContribution.ts rename to src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts index 0376cb18cdf..c0cd269d9a0 100644 --- a/src/vs/workbench/contrib/debug/electron-browser/debugEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts @@ -28,10 +28,8 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { ICommandService } from 'vs/platform/commands/common/commands'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { DebugHoverWidget } from 'vs/workbench/contrib/debug/electron-browser/debugHover'; import { RemoveBreakpointAction } from 'vs/workbench/contrib/debug/browser/debugActions'; import { IDebugEditorContribution, IDebugService, State, IBreakpoint, EDITOR_CONTRIBUTION_ID, CONTEXT_BREAKPOINT_WIDGET_VISIBLE, IStackFrame, IDebugConfiguration, IExpression, IExceptionInfo, BreakpointWidgetContext } from 'vs/workbench/contrib/debug/common/debug'; -import { BreakpointWidget } from 'vs/workbench/contrib/debug/electron-browser/breakpointWidget'; import { ExceptionWidget } from 'vs/workbench/contrib/debug/browser/exceptionWidget'; import { FloatingClickWidget } from 'vs/workbench/browser/parts/editor/editorWidgets'; import { Position } from 'vs/editor/common/core/position'; @@ -45,6 +43,8 @@ import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { getHover } from 'vs/editor/contrib/hover/getHover'; import { IEditorHoverOptions } from 'vs/editor/common/config/editorOptions'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { BreakpointWidget } from 'vs/workbench/contrib/debug/browser/breakpointWidget'; +import { DebugHoverWidget } from 'vs/workbench/contrib/debug/browser/debugHover'; const HOVER_DELAY = 300; const LAUNCH_JSON_REGEX = /launch\.json$/; diff --git a/src/vs/workbench/contrib/debug/electron-browser/debugHover.ts b/src/vs/workbench/contrib/debug/browser/debugHover.ts similarity index 91% rename from src/vs/workbench/contrib/debug/electron-browser/debugHover.ts rename to src/vs/workbench/contrib/debug/browser/debugHover.ts index f385cc6021e..408e07c27ad 100644 --- a/src/vs/workbench/contrib/debug/electron-browser/debugHover.ts +++ b/src/vs/workbench/contrib/debug/browser/debugHover.ts @@ -17,7 +17,6 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IDebugService, IExpression, IExpressionContainer } from 'vs/workbench/contrib/debug/common/debug'; import { Expression } from 'vs/workbench/contrib/debug/common/debugModel'; import { renderExpressionValue } from 'vs/workbench/contrib/debug/browser/baseDebugView'; -import { VariablesRenderer } from 'vs/workbench/contrib/debug/electron-browser/variablesView'; import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { attachStylerCallback } from 'vs/platform/theme/common/styler'; import { IThemeService } from 'vs/platform/theme/common/themeService'; @@ -27,12 +26,10 @@ import { getExactExpressionStartAndEnd } from 'vs/workbench/contrib/debug/common import { AsyncDataTree } from 'vs/base/browser/ui/tree/asyncDataTree'; import { IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; -import { WorkbenchAsyncDataTree, IListService } from 'vs/platform/list/browser/listService'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService'; import { coalesce } from 'vs/base/common/arrays'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IAsyncDataSource } from 'vs/base/browser/ui/tree/tree'; +import { VariablesRenderer } from 'vs/workbench/contrib/debug/browser/variablesView'; const $ = dom.$; const MAX_TREE_HEIGHT = 324; @@ -61,10 +58,6 @@ export class DebugHoverWidget implements IContentWidget { @IDebugService private readonly debugService: IDebugService, @IInstantiationService private readonly instantiationService: IInstantiationService, @IThemeService private readonly themeService: IThemeService, - @IContextKeyService private readonly contextKeyService: IContextKeyService, - @IListService private readonly listService: IListService, - @IConfigurationService private readonly configurationService: IConfigurationService, - @IKeybindingService private readonly keybindingService: IKeybindingService ) { this.toDispose = []; @@ -81,13 +74,13 @@ export class DebugHoverWidget implements IContentWidget { this.treeContainer.setAttribute('role', 'tree'); this.dataSource = new DebugHoverDataSource(); - this.tree = new WorkbenchAsyncDataTree(this.treeContainer, new DebugHoverDelegate(), [this.instantiationService.createInstance(VariablesRenderer)], + this.tree = this.instantiationService.createInstance(WorkbenchAsyncDataTree, this.treeContainer, new DebugHoverDelegate(), [this.instantiationService.createInstance(VariablesRenderer)], this.dataSource, { ariaLabel: nls.localize('treeAriaLabel', "Debug Hover"), accessibilityProvider: new DebugHoverAccessibilityProvider(), mouseSupport: false, horizontalScrolling: true - }, this.contextKeyService, this.listService, this.themeService, this.configurationService, this.keybindingService); + }) as any as AsyncDataTree; this.valueContainer = $('.value'); this.valueContainer.tabIndex = 0; diff --git a/src/vs/workbench/contrib/debug/browser/exceptionWidget.ts b/src/vs/workbench/contrib/debug/browser/exceptionWidget.ts index 7148fa39200..aca2b4d0c5b 100644 --- a/src/vs/workbench/contrib/debug/browser/exceptionWidget.ts +++ b/src/vs/workbench/contrib/debug/browser/exceptionWidget.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!../browser/media/exceptionWidget'; +import 'vs/css!./media/exceptionWidget'; import * as nls from 'vs/nls'; import * as dom from 'vs/base/browser/dom'; import { ZoneWidget } from 'vs/editor/contrib/zoneWidget/zoneWidget'; diff --git a/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts b/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts index e2de1fa52d1..a706559347a 100644 --- a/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts +++ b/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts @@ -29,11 +29,10 @@ import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { ITreeRenderer, ITreeNode, ITreeFilter, TreeVisibility, TreeFilterResult, IAsyncDataSource } from 'vs/base/browser/ui/tree/tree'; import { IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { WorkbenchAsyncDataTree, IListService, TreeResourceNavigator2 } from 'vs/platform/list/browser/listService'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { DebugContentProvider } from 'vs/workbench/contrib/debug/browser/debugContentProvider'; +import { WorkbenchAsyncDataTree, TreeResourceNavigator2 } from 'vs/platform/list/browser/listService'; import { dispose } from 'vs/base/common/lifecycle'; import { createMatches, FuzzyScore } from 'vs/base/common/filters'; +import { DebugContentProvider } from 'vs/workbench/contrib/debug/common/debugContentProvider'; const SMART = true; @@ -387,12 +386,10 @@ export class LoadedScriptsView extends ViewletPanel { @IInstantiationService private readonly instantiationService: IInstantiationService, @IConfigurationService configurationService: IConfigurationService, @IEditorService private readonly editorService: IEditorService, - @IContextKeyService private readonly contextKeyService: IContextKeyService, + @IContextKeyService readonly contextKeyService: IContextKeyService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @IEnvironmentService private readonly environmentService: IEnvironmentService, @IDebugService private readonly debugService: IDebugService, - @IListService private readonly listService: IListService, - @IThemeService private readonly themeService: IThemeService ) { super({ ...(options as IViewletPanelOptions), ariaHeaderLabel: nls.localize('loadedScriptsSection', "Loaded Scripts Section") }, keybindingService, contextMenuService, configurationService); this.loadedScriptsItemType = CONTEXT_LOADED_SCRIPTS_ITEM_TYPE.bindTo(contextKeyService); @@ -411,22 +408,21 @@ export class LoadedScriptsView extends ViewletPanel { this.treeLabels = this.instantiationService.createInstance(ResourceLabels, { onDidChangeVisibility: this.onDidChangeBodyVisibility } as IResourceLabelsContainer); this.disposables.push(this.treeLabels); - this.tree = new WorkbenchAsyncDataTree(this.treeContainer, new LoadedScriptsDelegate(), + this.tree = this.instantiationService.createInstance(WorkbenchAsyncDataTree, this.treeContainer, new LoadedScriptsDelegate(), [new LoadedScriptsRenderer(this.treeLabels)], new LoadedScriptsDataSource(), { identityProvider: { - getId: element => element.getId() + getId: element => (element).getId() }, keyboardNavigationLabelProvider: { - getKeyboardNavigationLabel: element => element.getLabel() + getKeyboardNavigationLabel: element => (element).getLabel() }, filter: this.filter, accessibilityProvider: new LoadedSciptsAccessibilityProvider(), ariaLabel: nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'loadedScriptsAriaLabel' }, "Debug Loaded Scripts"), - }, - this.contextKeyService, this.listService, this.themeService, this.configurationService, this.keybindingService - ); + } + ) as WorkbenchAsyncDataTree; this.tree.setInput(root); diff --git a/src/vs/workbench/contrib/debug/electron-browser/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts similarity index 96% rename from src/vs/workbench/contrib/debug/electron-browser/repl.ts rename to src/vs/workbench/contrib/debug/browser/repl.ts index 8faf4bd387e..fd002d2279b 100644 --- a/src/vs/workbench/contrib/debug/electron-browser/repl.ts +++ b/src/vs/workbench/contrib/debug/browser/repl.ts @@ -25,7 +25,6 @@ import { IInstantiationService, createDecorator } from 'vs/platform/instantiatio import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { Panel } from 'vs/workbench/browser/panel'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { clipboard } from 'electron'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { memoize } from 'vs/base/common/decorators'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; @@ -47,25 +46,24 @@ import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; import { Variable, Expression, SimpleReplElement, RawObjectReplElement } from 'vs/workbench/contrib/debug/common/debugModel'; import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; -import { VariablesRenderer } from 'vs/workbench/contrib/debug/electron-browser/variablesView'; import { ITreeRenderer, ITreeNode, ITreeContextMenuEvent, IAsyncDataSource } from 'vs/base/browser/ui/tree/tree'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { renderExpressionValue } from 'vs/workbench/contrib/debug/browser/baseDebugView'; import { handleANSIOutput } from 'vs/workbench/contrib/debug/browser/debugANSIHandling'; import { ILabelService } from 'vs/platform/label/common/label'; import { LinkDetector } from 'vs/workbench/contrib/debug/browser/linkDetector'; -import { CopyAction } from 'vs/workbench/contrib/debug/electron-browser/electronDebugActions'; -import { ReplCollapseAllAction } from 'vs/workbench/contrib/debug/browser/debugActions'; +import { ReplCollapseAllAction, CopyAction } from 'vs/workbench/contrib/debug/browser/debugActions'; import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { removeAnsiEscapeCodes } from 'vs/base/common/strings'; -import { WorkbenchAsyncDataTree, IListService } from 'vs/platform/list/browser/listService'; +import { WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ITextResourcePropertiesService } from 'vs/editor/common/services/resourceConfiguration'; import { RunOnceScheduler } from 'vs/base/common/async'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { FuzzyScore, createMatches } from 'vs/base/common/filters'; import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { VariablesRenderer } from 'vs/workbench/contrib/debug/browser/variablesView'; const $ = dom.$; @@ -117,10 +115,9 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati @IContextKeyService private readonly contextKeyService: IContextKeyService, @ICodeEditorService codeEditorService: ICodeEditorService, @IContextMenuService private readonly contextMenuService: IContextMenuService, - @IListService private readonly listService: IListService, @IConfigurationService private readonly configurationService: IConfigurationService, @ITextResourcePropertiesService private readonly textResourcePropertiesService: ITextResourcePropertiesService, - @IKeybindingService private readonly keybindingService: IKeybindingService + @IClipboardService private readonly clipboardService: IClipboardService ) { super(REPL_ID, telemetryService, themeService, storageService); @@ -369,7 +366,7 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati this.createReplInput(this.container); this.replDelegate = new ReplDelegate(this.configurationService); - this.tree = new WorkbenchAsyncDataTree(treeContainer, this.replDelegate, [ + this.tree = this.instantiationService.createInstance(WorkbenchAsyncDataTree, treeContainer, this.replDelegate, [ this.instantiationService.createInstance(VariablesRenderer), this.instantiationService.createInstance(ReplSimpleElementsRenderer), new ReplExpressionsRenderer(), @@ -377,13 +374,13 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati ], new ReplDataSource(), { ariaLabel: nls.localize('replAriaLabel', "Read Eval Print Loop Panel"), accessibilityProvider: new ReplAccessibilityProvider(), - identityProvider: { getId: element => element.getId() }, + identityProvider: { getId: element => (element).getId() }, mouseSupport: false, keyboardNavigationLabelProvider: { getKeyboardNavigationLabel: e => e }, horizontalScrolling: false, setRowLineHeight: false, supportDynamicHeights: true - }, this.contextKeyService, this.listService, this.themeService, this.configurationService, this.keybindingService); + }) as WorkbenchAsyncDataTree; this.toDispose.push(this.tree.onContextMenu(e => this.onContextMenu(e))); // Make sure to select the session if debugging is already active @@ -455,9 +452,9 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati private onContextMenu(e: ITreeContextMenuEvent): void { const actions: IAction[] = []; - actions.push(new CopyAction(CopyAction.ID, CopyAction.LABEL)); + actions.push(new CopyAction(CopyAction.ID, CopyAction.LABEL, this.clipboardService)); actions.push(new Action('workbench.debug.action.copyAll', nls.localize('copyAll', "Copy All"), undefined, true, () => { - clipboard.writeText(this.getVisibleContent()); + this.clipboardService.writeText(this.getVisibleContent()); return Promise.resolve(undefined); })); actions.push(new ReplCollapseAllAction(this.tree, this.replInput)); @@ -823,7 +820,8 @@ class ReplCopyAllAction extends EditorAction { } run(accessor: ServicesAccessor, editor: ICodeEditor): void | Promise { - clipboard.writeText(accessor.get(IPrivateReplService).getVisibleContent()); + const clipboardService = accessor.get(IClipboardService); + clipboardService.writeText(accessor.get(IPrivateReplService).getVisibleContent()); } } diff --git a/src/vs/workbench/contrib/debug/electron-browser/variablesView.ts b/src/vs/workbench/contrib/debug/browser/variablesView.ts similarity index 89% rename from src/vs/workbench/contrib/debug/electron-browser/variablesView.ts rename to src/vs/workbench/contrib/debug/browser/variablesView.ts index de016d8d61e..d3552b9eb43 100644 --- a/src/vs/workbench/contrib/debug/electron-browser/variablesView.ts +++ b/src/vs/workbench/contrib/debug/browser/variablesView.ts @@ -14,8 +14,7 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { renderViewTree, renderVariable, IInputBoxOptions, AbstractExpressionsRenderer, IExpressionTemplateData } from 'vs/workbench/contrib/debug/browser/baseDebugView'; import { IAction } from 'vs/base/common/actions'; -import { SetValueAction, AddToWatchExpressionsAction } from 'vs/workbench/contrib/debug/browser/debugActions'; -import { CopyValueAction, CopyEvaluatePathAction } from 'vs/workbench/contrib/debug/electron-browser/electronDebugActions'; +import { SetValueAction, AddToWatchExpressionsAction, CopyValueAction, CopyEvaluatePathAction } from 'vs/workbench/contrib/debug/browser/debugActions'; import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IViewletPanelOptions, ViewletPanel } from 'vs/workbench/browser/parts/views/panelViewlet'; @@ -24,9 +23,7 @@ import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { ITreeRenderer, ITreeNode, ITreeMouseEvent, ITreeContextMenuEvent, IAsyncDataSource } from 'vs/base/browser/ui/tree/tree'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { Emitter } from 'vs/base/common/event'; -import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { WorkbenchAsyncDataTree, IListService } from 'vs/platform/list/browser/listService'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService'; import { onUnexpectedError } from 'vs/base/common/errors'; import { FuzzyScore, createMatches } from 'vs/base/common/filters'; import { HighlightedLabel, IHighlight } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; @@ -48,9 +45,6 @@ export class VariablesView extends ViewletPanel { @IKeybindingService keybindingService: IKeybindingService, @IConfigurationService configurationService: IConfigurationService, @IInstantiationService private readonly instantiationService: IInstantiationService, - @IContextKeyService private readonly contextKeyService: IContextKeyService, - @IListService private readonly listService: IListService, - @IThemeService private readonly themeService: IThemeService ) { super({ ...(options as IViewletPanelOptions), ariaHeaderLabel: nls.localize('variablesSection', "Variables Section") }, keybindingService, contextMenuService, configurationService); @@ -75,14 +69,14 @@ export class VariablesView extends ViewletPanel { dom.addClass(container, 'debug-variables'); const treeContainer = renderViewTree(container); - this.tree = new WorkbenchAsyncDataTree(treeContainer, new VariablesDelegate(), + this.tree = this.instantiationService.createInstance(WorkbenchAsyncDataTree, treeContainer, new VariablesDelegate(), [this.instantiationService.createInstance(VariablesRenderer), new ScopesRenderer()], new VariablesDataSource(), { ariaLabel: nls.localize('variablesAriaTreeLabel', "Debug Variables"), accessibilityProvider: new VariablesAccessibilityProvider(), - identityProvider: { getId: element => element.getId() }, + identityProvider: { getId: element => (element).getId() }, keyboardNavigationLabelProvider: { getKeyboardNavigationLabel: e => e } - }, this.contextKeyService, this.listService, this.themeService, this.configurationService, this.keybindingService); + }) as WorkbenchAsyncDataTree; this.tree.setInput(this.debugService.getViewModel()).then(null, onUnexpectedError); @@ -140,8 +134,8 @@ export class VariablesView extends ViewletPanel { const actions: IAction[] = []; const variable = element as Variable; actions.push(new SetValueAction(SetValueAction.ID, SetValueAction.LABEL, variable, this.debugService, this.keybindingService)); - actions.push(new CopyValueAction(CopyValueAction.ID, CopyValueAction.LABEL, variable, 'variables', this.debugService)); - actions.push(new CopyEvaluatePathAction(CopyEvaluatePathAction.ID, CopyEvaluatePathAction.LABEL, variable)); + actions.push(this.instantiationService.createInstance(CopyValueAction, CopyValueAction.ID, CopyValueAction.LABEL, variable, 'variables')); + actions.push(this.instantiationService.createInstance(CopyEvaluatePathAction, CopyEvaluatePathAction.ID, CopyEvaluatePathAction.LABEL, variable)); actions.push(new Separator()); actions.push(new AddToWatchExpressionsAction(AddToWatchExpressionsAction.ID, AddToWatchExpressionsAction.LABEL, variable, this.debugService, this.keybindingService)); diff --git a/src/vs/workbench/contrib/debug/electron-browser/watchExpressionsView.ts b/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts similarity index 90% rename from src/vs/workbench/contrib/debug/electron-browser/watchExpressionsView.ts rename to src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts index d4404d8de27..c00aaa96c9f 100644 --- a/src/vs/workbench/contrib/debug/electron-browser/watchExpressionsView.ts +++ b/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts @@ -10,28 +10,25 @@ import { CollapseAction } from 'vs/workbench/browser/viewlet'; import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { IDebugService, IExpression, CONTEXT_WATCH_EXPRESSIONS_FOCUSED } from 'vs/workbench/contrib/debug/common/debug'; import { Expression, Variable } from 'vs/workbench/contrib/debug/common/debugModel'; -import { AddWatchExpressionAction, RemoveAllWatchExpressionsAction, EditWatchExpressionAction, RemoveWatchExpressionAction } from 'vs/workbench/contrib/debug/browser/debugActions'; +import { AddWatchExpressionAction, RemoveAllWatchExpressionsAction, EditWatchExpressionAction, RemoveWatchExpressionAction, CopyValueAction } from 'vs/workbench/contrib/debug/browser/debugActions'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IAction } from 'vs/base/common/actions'; -import { CopyValueAction } from 'vs/workbench/contrib/debug/electron-browser/electronDebugActions'; import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { renderExpressionValue, renderViewTree, IInputBoxOptions, AbstractExpressionsRenderer, IExpressionTemplateData } from 'vs/workbench/contrib/debug/browser/baseDebugView'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IViewletPanelOptions, ViewletPanel } from 'vs/workbench/browser/parts/views/panelViewlet'; import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; -import { VariablesRenderer, variableSetEmitter } from 'vs/workbench/contrib/debug/electron-browser/variablesView'; -import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { WorkbenchAsyncDataTree, IListService } from 'vs/platform/list/browser/listService'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService'; import { IAsyncDataSource, ITreeMouseEvent, ITreeContextMenuEvent, ITreeDragAndDrop, ITreeDragOverReaction } from 'vs/base/browser/ui/tree/tree'; import { IDragAndDropData } from 'vs/base/browser/dnd'; import { onUnexpectedError } from 'vs/base/common/errors'; import { ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView'; import { FuzzyScore } from 'vs/base/common/filters'; import { IHighlight } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; +import { variableSetEmitter, VariablesRenderer } from 'vs/workbench/contrib/debug/browser/variablesView'; const MAX_VALUE_RENDER_LENGTH_IN_VIEWLET = 1024; @@ -48,9 +45,6 @@ export class WatchExpressionsView extends ViewletPanel { @IKeybindingService keybindingService: IKeybindingService, @IInstantiationService private readonly instantiationService: IInstantiationService, @IConfigurationService configurationService: IConfigurationService, - @IContextKeyService private readonly contextKeyService: IContextKeyService, - @IListService private readonly listService: IListService, - @IThemeService private readonly themeService: IThemeService ) { super({ ...(options as IViewletPanelOptions), ariaHeaderLabel: nls.localize('watchExpressionsSection', "Watch Expressions Section") }, keybindingService, contextMenuService, configurationService); @@ -65,14 +59,14 @@ export class WatchExpressionsView extends ViewletPanel { const treeContainer = renderViewTree(container); const expressionsRenderer = this.instantiationService.createInstance(WatchExpressionsRenderer); - this.tree = new WorkbenchAsyncDataTree(treeContainer, new WatchExpressionsDelegate(), [expressionsRenderer, this.instantiationService.createInstance(VariablesRenderer)], + this.tree = this.instantiationService.createInstance(WorkbenchAsyncDataTree, treeContainer, new WatchExpressionsDelegate(), [expressionsRenderer, this.instantiationService.createInstance(VariablesRenderer)], new WatchExpressionsDataSource(), { ariaLabel: nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'watchAriaTreeLabel' }, "Debug Watch Expressions"), accessibilityProvider: new WatchExpressionsAccessibilityProvider(), - identityProvider: { getId: element => element.getId() }, + identityProvider: { getId: element => (element).getId() }, keyboardNavigationLabelProvider: { getKeyboardNavigationLabel: e => e }, dnd: new WatchExpressionsDragAndDrop(this.debugService), - }, this.contextKeyService, this.listService, this.themeService, this.configurationService, this.keybindingService); + }) as WorkbenchAsyncDataTree; this.tree.setInput(this.debugService).then(undefined, onUnexpectedError); CONTEXT_WATCH_EXPRESSIONS_FOCUSED.bindTo(this.tree.contextKeyService); @@ -152,7 +146,7 @@ export class WatchExpressionsView extends ViewletPanel { actions.push(new AddWatchExpressionAction(AddWatchExpressionAction.ID, AddWatchExpressionAction.LABEL, this.debugService, this.keybindingService)); actions.push(new EditWatchExpressionAction(EditWatchExpressionAction.ID, EditWatchExpressionAction.LABEL, this.debugService, this.keybindingService)); if (!expression.hasChildren) { - actions.push(new CopyValueAction(CopyValueAction.ID, CopyValueAction.LABEL, expression.value, 'watch', this.debugService)); + actions.push(this.instantiationService.createInstance(CopyValueAction, CopyValueAction.ID, CopyValueAction.LABEL, expression.value, 'watch', this.debugService)); } actions.push(new Separator()); @@ -163,7 +157,7 @@ export class WatchExpressionsView extends ViewletPanel { if (element instanceof Variable) { const variable = element as Variable; if (!variable.hasChildren) { - actions.push(new CopyValueAction(CopyValueAction.ID, CopyValueAction.LABEL, variable, 'watch', this.debugService)); + actions.push(this.instantiationService.createInstance(CopyValueAction, CopyValueAction.ID, CopyValueAction.LABEL, variable, 'watch', this.debugService)); } actions.push(new Separator()); } diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index 1fd99ba4960..fb7bac29730 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -63,10 +63,8 @@ export const INTERNAL_CONSOLE_OPTIONS_SCHEMA = { // raw export interface IRawModelUpdate { - threadId: number; sessionId: string; - thread?: DebugProtocol.Thread; - callStack?: DebugProtocol.StackFrame[]; + threads: DebugProtocol.Thread[]; stoppedDetails?: IRawStoppedDetails; } @@ -542,7 +540,7 @@ export interface IDebuggerContribution extends IPlatformSpecificAdapterContribut export interface IDebugConfigurationProvider { readonly type: string; - resolveDebugConfiguration?(folderUri: uri | undefined, debugConfiguration: IConfig): Promise; + resolveDebugConfiguration?(folderUri: uri | undefined, debugConfiguration: IConfig): Promise; provideDebugConfigurations?(folderUri: uri | undefined): Promise; debugAdapterExecutable?(folderUri: uri | undefined): Promise; // TODO@AW legacy } @@ -791,7 +789,7 @@ export interface IDebugService { * Returns true if the start debugging was successfull. For compound launches, all configurations have to start successfuly for it to return success. * On errors the startDebugging will throw an error, however some error and cancelations are handled and in that case will simply return false. */ - startDebugging(launch: ILaunch | undefined, configOrName?: IConfig | string, noDebug?: boolean): Promise; + startDebugging(launch: ILaunch | undefined, configOrName?: IConfig | string, noDebug?: boolean, parentSession?: IDebugSession): Promise; /** * Restarts a session or creates a new one if there is no active session. diff --git a/src/vs/workbench/contrib/debug/browser/debugContentProvider.ts b/src/vs/workbench/contrib/debug/common/debugContentProvider.ts similarity index 100% rename from src/vs/workbench/contrib/debug/browser/debugContentProvider.ts rename to src/vs/workbench/contrib/debug/common/debugContentProvider.ts diff --git a/src/vs/workbench/contrib/debug/electron-browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/electron-browser/debug.contribution.ts index a7a09a264dc..5cd18589cf6 100644 --- a/src/vs/workbench/contrib/debug/electron-browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/electron-browser/debug.contribution.ts @@ -17,10 +17,8 @@ import { ShowViewletAction, Extensions as ViewletExtensions, ViewletRegistry, Vi import { TogglePanelAction, Extensions as PanelExtensions, PanelRegistry, PanelDescriptor } from 'vs/workbench/browser/panel'; import { StatusbarItemDescriptor, IStatusbarRegistry, Extensions as StatusExtensions } from 'vs/workbench/browser/parts/statusbar/statusbar'; import { StatusbarAlignment } from 'vs/platform/statusbar/common/statusbar'; -import { VariablesView } from 'vs/workbench/contrib/debug/electron-browser/variablesView'; import { BreakpointsView } from 'vs/workbench/contrib/debug/browser/breakpointsView'; -import { WatchExpressionsView } from 'vs/workbench/contrib/debug/electron-browser/watchExpressionsView'; -import { CallStackView } from 'vs/workbench/contrib/debug/electron-browser/callStackView'; +import { CallStackView } from 'vs/workbench/contrib/debug/browser/callStackView'; import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { IDebugService, VIEWLET_ID, REPL_ID, CONTEXT_IN_DEBUG_MODE, INTERNAL_CONSOLE_OPTIONS_SCHEMA, @@ -35,8 +33,6 @@ import { } from 'vs/workbench/contrib/debug/browser/debugActions'; import { DebugToolbar } from 'vs/workbench/contrib/debug/browser/debugToolbar'; import * as service from 'vs/workbench/contrib/debug/electron-browser/debugService'; -import { DebugContentProvider } from 'vs/workbench/contrib/debug/browser/debugContentProvider'; -import 'vs/workbench/contrib/debug/electron-browser/debugEditorContribution'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { registerCommands, ADD_CONFIGURATION_ID, TOGGLE_INLINE_BREAKPOINT_ID } from 'vs/workbench/contrib/debug/browser/debugCommands'; import { IQuickOpenRegistry, Extensions as QuickOpenExtensions, QuickOpenHandlerDescriptor } from 'vs/workbench/browser/quickopen'; @@ -46,7 +42,6 @@ import { isMacintosh } from 'vs/base/common/platform'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { URI } from 'vs/base/common/uri'; import { DebugViewlet } from 'vs/workbench/contrib/debug/browser/debugViewlet'; -import { Repl, ClearReplAction } from 'vs/workbench/contrib/debug/electron-browser/repl'; import { DebugQuickOpenHandler } from 'vs/workbench/contrib/debug/browser/debugQuickOpen'; import { DebugStatus } from 'vs/workbench/contrib/debug/browser/debugStatus'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; @@ -54,6 +49,10 @@ import { launchSchemaId } from 'vs/workbench/services/configuration/common/confi import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { LoadedScriptsView } from 'vs/workbench/contrib/debug/browser/loadedScriptsView'; import { TOGGLE_LOG_POINT_ID, TOGGLE_CONDITIONAL_BREAKPOINT_ID, TOGGLE_BREAKPOINT_ID } from 'vs/workbench/contrib/debug/browser/debugEditorActions'; +import { WatchExpressionsView } from 'vs/workbench/contrib/debug/browser/watchExpressionsView'; +import { VariablesView } from 'vs/workbench/contrib/debug/browser/variablesView'; +import { ClearReplAction, Repl } from 'vs/workbench/contrib/debug/browser/repl'; +import { DebugContentProvider } from 'vs/workbench/contrib/debug/common/debugContentProvider'; class OpenDebugViewletAction extends ShowViewletAction { public static readonly ID = VIEWLET_ID; diff --git a/src/vs/workbench/contrib/debug/electron-browser/debugConfigurationManager.ts b/src/vs/workbench/contrib/debug/electron-browser/debugConfigurationManager.ts index e6f01c26cdd..2580f4e8aa0 100644 --- a/src/vs/workbench/contrib/debug/electron-browser/debugConfigurationManager.ts +++ b/src/vs/workbench/contrib/debug/electron-browser/debugConfigurationManager.ts @@ -212,9 +212,9 @@ export class ConfigurationManager implements IConfigurationManager { return providers.length > 0; } - resolveConfigurationByProviders(folderUri: uri | undefined, type: string | undefined, debugConfiguration: IConfig): Promise { + resolveConfigurationByProviders(folderUri: uri | undefined, type: string | undefined, debugConfiguration: IConfig): Promise { return this.activateDebuggers('onDebugResolve', type).then(() => { - // pipe the config through the promises sequentially. append at the end the '*' types + // pipe the config through the promises sequentially. Append at the end the '*' types const providers = this.configProviders.filter(p => p.type === type && p.resolveDebugConfiguration) .concat(this.configProviders.filter(p => p.type === '*' && p.resolveDebugConfiguration)); diff --git a/src/vs/workbench/contrib/debug/electron-browser/debugService.ts b/src/vs/workbench/contrib/debug/electron-browser/debugService.ts index 1d95a644fe9..10d5ea1b424 100644 --- a/src/vs/workbench/contrib/debug/electron-browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/electron-browser/debugService.ts @@ -252,7 +252,7 @@ export class DebugService implements IDebugService { * main entry point * properly manages compounds, checks for errors and handles the initializing state. */ - startDebugging(launch: ILaunch | undefined, configOrName?: IConfig | string, noDebug = false, unresolvedConfig?: IConfig, ): Promise { + startDebugging(launch: ILaunch | undefined, configOrName?: IConfig | string, noDebug = false, parentSession?: IDebugSession): Promise { this.startInitializingState(); // make sure to save all files and that the configuration is up to date @@ -315,7 +315,7 @@ export class DebugService implements IDebugService { } } - return this.createSession(launchForName, launchForName!.getConfiguration(name), unresolvedConfig, noDebug); + return this.createSession(launchForName, launchForName!.getConfiguration(name), noDebug); })).then(values => values.every(success => !!success)); // Compound launch is a success only if each configuration launched successfully } @@ -325,7 +325,7 @@ export class DebugService implements IDebugService { return Promise.reject(new Error(message)); } - return this.createSession(launch, config, unresolvedConfig, noDebug); + return this.createSession(launch, config, noDebug); }); })); }).then(success => { @@ -341,7 +341,7 @@ export class DebugService implements IDebugService { /** * gets the debugger for the type, resolves configurations by providers, substitutes variables and runs prelaunch tasks */ - private createSession(launch: ILaunch | undefined, config: IConfig | undefined, unresolvedConfig: IConfig | undefined, noDebug: boolean): Promise { + private createSession(launch: ILaunch | undefined, config: IConfig | undefined, noDebug: boolean): Promise { // We keep the debug type in a separate variable 'type' so that a no-folder config has no attributes. // Storing the type in the config would break extensions that assume that the no-folder case is indicated by an empty config. let type: string | undefined; @@ -351,7 +351,7 @@ export class DebugService implements IDebugService { // a no-folder workspace has no launch.config config = Object.create(null); } - unresolvedConfig = unresolvedConfig || deepClone(config); + const unresolvedConfig = deepClone(config); if (noDebug) { config!.noDebug = true; @@ -591,12 +591,24 @@ export class DebugService implements IDebugService { } } - let substitutionThenable: Promise = Promise.resolve(session.configuration); + let substitutionThenable: Promise = Promise.resolve(session.configuration); if (launch && needsToSubstitute && unresolved) { substitutionThenable = this.configurationManager.resolveConfigurationByProviders(launch.workspace ? launch.workspace.uri : undefined, unresolved.type, unresolved) - .then(resolved => this.substituteVariables(launch, resolved)); + .then(resolved => { + if (resolved) { + // start debugging + return this.substituteVariables(launch, resolved); + } else if (resolved === null) { + // abort debugging silently and open launch.json + return Promise.resolve(null); + } else { + // abort debugging silently + return Promise.resolve(undefined); + } + }); } substitutionThenable.then(resolved => { + if (!resolved) { return c(undefined); } diff --git a/src/vs/workbench/contrib/debug/electron-browser/debugSession.ts b/src/vs/workbench/contrib/debug/electron-browser/debugSession.ts index 393e1e24f86..f4695c0ac7b 100644 --- a/src/vs/workbench/contrib/debug/electron-browser/debugSession.ts +++ b/src/vs/workbench/contrib/debug/electron-browser/debugSession.ts @@ -544,32 +544,34 @@ export class DebugSession implements IDebugSession { } rawUpdate(data: IRawModelUpdate): void { - - if (data.thread && !this.threads.has(data.threadId)) { - // A new thread came in, initialize it. - this.threads.set(data.threadId, new Thread(this, data.thread.name, data.thread.id)); - } else if (data.thread && data.thread.name) { - // Just the thread name got updated #18244 - const thread = this.threads.get(data.threadId); - if (thread) { - thread.name = data.thread.name; + data.threads.forEach(thread => { + if (!this.threads.has(thread.id)) { + // A new thread came in, initialize it. + this.threads.set(thread.id, new Thread(this, thread.name, thread.id)); + } else if (thread.name) { + // Just the thread name got updated #18244 + const oldThread = this.threads.get(thread.id); + if (oldThread) { + oldThread.name = thread.name; + } } - } + }); - if (data.stoppedDetails) { + const stoppedDetails = data.stoppedDetails; + if (stoppedDetails) { // Set the availability of the threads' callstacks depending on // whether the thread is stopped or not - if (data.stoppedDetails.allThreadsStopped) { + if (stoppedDetails.allThreadsStopped) { this.threads.forEach(thread => { - thread.stoppedDetails = thread.threadId === data.threadId ? data.stoppedDetails : { reason: undefined }; + thread.stoppedDetails = thread.threadId === stoppedDetails.threadId ? stoppedDetails : { reason: undefined }; thread.stopped = true; thread.clearCallStack(); }); } else { - const thread = this.threads.get(data.threadId); + const thread = typeof stoppedDetails.threadId === 'number' ? this.threads.get(stoppedDetails.threadId) : undefined; if (thread) { // One thread is stopped, only update that thread. - thread.stoppedDetails = data.stoppedDetails; + thread.stoppedDetails = stoppedDetails; thread.clearCallStack(); thread.stopped = true; } @@ -580,13 +582,10 @@ export class DebugSession implements IDebugSession { private fetchThreads(stoppedDetails?: IRawStoppedDetails): Promise { return this.raw ? this.raw.threads().then(response => { if (response && response.body && response.body.threads) { - response.body.threads.forEach(thread => { - this.model.rawUpdate({ - sessionId: this.getId(), - threadId: thread.id, - thread, - stoppedDetails: stoppedDetails && thread.id === stoppedDetails.threadId ? stoppedDetails : undefined - }); + this.model.rawUpdate({ + sessionId: this.getId(), + threads: response.body.threads, + stoppedDetails }); } }) : Promise.resolve(undefined); diff --git a/src/vs/workbench/contrib/debug/electron-browser/electronDebugActions.ts b/src/vs/workbench/contrib/debug/electron-browser/electronDebugActions.ts deleted file mode 100644 index 6b504bb0733..00000000000 --- a/src/vs/workbench/contrib/debug/electron-browser/electronDebugActions.ts +++ /dev/null @@ -1,72 +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 { Action } from 'vs/base/common/actions'; -import { Variable } from 'vs/workbench/contrib/debug/common/debugModel'; -import { IDebugService, IStackFrame } from 'vs/workbench/contrib/debug/common/debug'; -import { clipboard } from 'electron'; -import { isWindows } from 'vs/base/common/platform'; - -export class CopyValueAction extends Action { - static readonly ID = 'workbench.debug.viewlet.action.copyValue'; - static LABEL = nls.localize('copyValue', "Copy Value"); - - constructor(id: string, label: string, private value: any, private context: string, @IDebugService private readonly debugService: IDebugService) { - super(id, label, 'debug-action copy-value'); - this._enabled = typeof this.value === 'string' || (this.value instanceof Variable && !!this.value.evaluateName); - } - - public run(): Promise { - const stackFrame = this.debugService.getViewModel().focusedStackFrame; - const session = this.debugService.getViewModel().focusedSession; - - if (this.value instanceof Variable && stackFrame && session && this.value.evaluateName) { - return session.evaluate(this.value.evaluateName, stackFrame.frameId, this.context).then(result => { - clipboard.writeText(result.body.result); - }, err => clipboard.writeText(this.value.value)); - } - - clipboard.writeText(this.value); - return Promise.resolve(undefined); - } -} - -export class CopyEvaluatePathAction extends Action { - static readonly ID = 'workbench.debug.viewlet.action.copyEvaluatePath'; - static LABEL = nls.localize('copyAsExpression', "Copy as Expression"); - - constructor(id: string, label: string, private value: Variable) { - super(id, label); - this._enabled = this.value && !!this.value.evaluateName; - } - - public run(): Promise { - clipboard.writeText(this.value.evaluateName!); - return Promise.resolve(undefined); - } -} - -export class CopyAction extends Action { - static readonly ID = 'workbench.debug.action.copy'; - static LABEL = nls.localize('copy', "Copy"); - - public run(): Promise { - clipboard.writeText(window.getSelection().toString()); - return Promise.resolve(undefined); - } -} - -const lineDelimiter = isWindows ? '\r\n' : '\n'; - -export class CopyStackTraceAction extends Action { - static readonly ID = 'workbench.action.debug.copyStackTrace'; - static LABEL = nls.localize('copyStackTrace', "Copy Call Stack"); - - public run(frame: IStackFrame): Promise { - clipboard.writeText(frame.thread.getCallStack().map(sf => sf.toString()).join(lineDelimiter)); - return Promise.resolve(undefined); - } -} diff --git a/src/vs/workbench/contrib/debug/node/debugAdapter.ts b/src/vs/workbench/contrib/debug/node/debugAdapter.ts index 80ff618743e..b98c5d5355e 100644 --- a/src/vs/workbench/contrib/debug/node/debugAdapter.ts +++ b/src/vs/workbench/contrib/debug/node/debugAdapter.ts @@ -14,9 +14,9 @@ import * as objects from 'vs/base/common/objects'; import * as platform from 'vs/base/common/platform'; import { Emitter, Event } from 'vs/base/common/event'; import { ExtensionsChannelId } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import { IOutputService } from 'vs/workbench/contrib/output/common/output'; import { IDebugAdapter, IDebugAdapterExecutable, IDebuggerContribution, IPlatformSpecificAdapterContribution, IDebugAdapterServer } from 'vs/workbench/contrib/debug/common/debug'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; /** * Abstract implementation of the low level API for a debug adapter. diff --git a/src/vs/workbench/contrib/debug/node/debugger.ts b/src/vs/workbench/contrib/debug/node/debugger.ts index 3fed5251e36..c71c681e6f5 100644 --- a/src/vs/workbench/contrib/debug/node/debugger.ts +++ b/src/vs/workbench/contrib/debug/node/debugger.ts @@ -12,7 +12,6 @@ import { TelemetryAppenderClient } from 'vs/platform/telemetry/node/telemetryIpc import { IJSONSchema, IJSONSchemaSnippet } from 'vs/base/common/jsonSchema'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { IConfig, IDebuggerContribution, IDebugAdapterExecutable, INTERNAL_CONSOLE_OPTIONS_SCHEMA, IConfigurationManager, IDebugAdapter, ITerminalSettings, IDebugger, IDebugSession, IAdapterDescriptor, IDebugAdapterServer } from 'vs/workbench/contrib/debug/common/debug'; -import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IOutputService } from 'vs/workbench/contrib/output/common/output'; @@ -28,6 +27,7 @@ import { ITextResourcePropertiesService } from 'vs/editor/common/services/resour import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; import { isDebuggerMainContribution } from 'vs/workbench/contrib/debug/common/debugUtils'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; export class Debugger implements IDebugger { diff --git a/src/vs/workbench/contrib/debug/test/browser/debugANSIHandling.test.ts b/src/vs/workbench/contrib/debug/test/browser/debugANSIHandling.test.ts index 4902d43a463..8263031e078 100644 --- a/src/vs/workbench/contrib/debug/test/browser/debugANSIHandling.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/debugANSIHandling.test.ts @@ -130,16 +130,37 @@ suite('Debug - ANSI Handling', () => { }); } - // Codes do not interfere - assertSingleSequenceElement('\x1b[1;3;4;30;31;32;33;34;35;36;37m', (child) => { - assert.equal(11, child.classList.length); + // Different codes do not interfere + assertSingleSequenceElement('\x1b[1;3;4;30;41m', (child) => { + assert.equal(5, child.classList.length); assert(dom.hasClass(child, 'code-bold')); assert(dom.hasClass(child, 'code-italic')); assert(dom.hasClass(child, 'code-underline')); - for (let i = 30; i <= 37; i++) { - assert(dom.hasClass(child, 'code-foreground-' + i)); - } + assert(dom.hasClass(child, 'code-foreground-30')); + assert(dom.hasClass(child, 'code-background-41')); + }); + + // New foreground color codes remove old codes + assertSingleSequenceElement('\x1b[30;31;32;33;34;35;36;37m', (child) => { + assert.equal(1, child.classList.length); + + assert(dom.hasClass(child, 'code-foreground-37')); + }); + + // New background color codes remove old codes + assertSingleSequenceElement('\x1b[40;41;42;43;44;45;46;47m', (child) => { + assert.equal(1, child.classList.length); + + assert(dom.hasClass(child, 'code-background-47')); + }); + + // New foreground codes don't remove old background codes and vice versa + assertSingleSequenceElement('\x1b[40;31;42;33m', (child) => { + assert.equal(2, child.classList.length); + + assert(dom.hasClass(child, 'code-background-42')); + assert(dom.hasClass(child, 'code-foreground-33')); }); // Duplicate codes do not change output diff --git a/src/vs/workbench/contrib/debug/test/electron-browser/debugModel.test.ts b/src/vs/workbench/contrib/debug/test/electron-browser/debugModel.test.ts index a627407d4f8..4069d26f76a 100644 --- a/src/vs/workbench/contrib/debug/test/electron-browser/debugModel.test.ts +++ b/src/vs/workbench/contrib/debug/test/electron-browser/debugModel.test.ts @@ -115,11 +115,10 @@ suite('Debug - Model', () => { assert.equal(model.getSessions(true).length, 1); model.rawUpdate({ sessionId: session.getId(), - threadId: threadId, - thread: { + threads: [{ id: threadId, name: threadName - } + }] }); assert.equal(session.getThread(threadId)!.name, threadName); @@ -144,26 +143,27 @@ suite('Debug - Model', () => { model.rawUpdate({ sessionId: session.getId(), - threadId: threadId1, - thread: { + threads: [{ id: threadId1, name: threadName1 - } + }] }); model.rawUpdate({ sessionId: session.getId(), - threadId: threadId2, - thread: { + threads: [{ id: threadId2, name: threadName2 - } + }] }); // Stopped event with all threads stopped model.rawUpdate({ sessionId: session.getId(), - threadId: threadId1, + threads: [{ + id: threadId1, + name: threadName1 + }], stoppedDetails: { reason: stoppedReason, threadId: 1, @@ -232,26 +232,27 @@ suite('Debug - Model', () => { // Add the threads model.rawUpdate({ sessionId: session.getId(), - threadId: stoppedThreadId, - thread: { + threads: [{ id: stoppedThreadId, name: stoppedThreadName - } + }] }); model.rawUpdate({ sessionId: session.getId(), - threadId: runningThreadId, - thread: { + threads: [{ id: runningThreadId, name: runningThreadName - } + }] }); // Stopped event with only one thread stopped model.rawUpdate({ sessionId: session.getId(), - threadId: stoppedThreadId, + threads: [{ + id: 1, + name: stoppedThreadName + }], stoppedDetails: { reason: stoppedReason, threadId: 1, diff --git a/src/vs/workbench/contrib/debug/test/node/debugger.test.ts b/src/vs/workbench/contrib/debug/test/node/debugger.test.ts index d13a94effd8..72cfc4d9805 100644 --- a/src/vs/workbench/contrib/debug/test/node/debugger.test.ts +++ b/src/vs/workbench/contrib/debug/test/node/debugger.test.ts @@ -12,8 +12,7 @@ import { TestConfigurationService } from 'vs/platform/configuration/test/common/ import { URI } from 'vs/base/common/uri'; import { ExecutableDebugAdapter } from 'vs/workbench/contrib/debug/node/debugAdapter'; import { TestTextResourcePropertiesService } from 'vs/workbench/test/workbenchTestServices'; -import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; -import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; +import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; suite('Debug - Debugger', () => { 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 8fe108cabdb..73d6f8953f0 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts @@ -47,6 +47,7 @@ import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { ExtensionActivationProgress } from 'vs/workbench/contrib/extensions/electron-browser/extensionsActivationProgress'; import { ExtensionsAutoProfiler } from 'vs/workbench/contrib/extensions/electron-browser/extensionsAutoProfiler'; import { onUnexpectedError } from 'vs/base/common/errors'; +import { ExtensionDependencyChecker } from 'vs/workbench/contrib/extensions/electron-browser/extensionsDependencyChecker'; // Singletons registerSingleton(IExtensionsWorkbenchService, ExtensionsWorkbenchService); @@ -61,6 +62,7 @@ workbenchRegistry.registerWorkbenchContribution(KeymapExtensions, LifecyclePhase workbenchRegistry.registerWorkbenchContribution(ExtensionsViewletViewsContribution, LifecyclePhase.Starting); workbenchRegistry.registerWorkbenchContribution(ExtensionActivationProgress, LifecyclePhase.Eventually); workbenchRegistry.registerWorkbenchContribution(ExtensionsAutoProfiler, LifecyclePhase.Eventually); +workbenchRegistry.registerWorkbenchContribution(ExtensionDependencyChecker, LifecyclePhase.Eventually); Registry.as(OutputExtensions.OutputChannels) .registerChannel({ id: ExtensionsChannelId, label: ExtensionsLabel, log: false }); @@ -238,11 +240,6 @@ Registry.as(ConfigurationExtensions.Configuration) type: 'boolean', description: localize('extensionsCloseExtensionDetailsOnViewChange', "When enabled, editors with extension details will be automatically closed upon navigating away from the Extensions View."), default: false - }, - 'extensions.enableExperimentalAzureSearch': { - type: 'boolean', - description: localize('enableExperimentalAzureSearch', "Enable experimental Azure based search for searching extensions in Marketplace."), - default: true } } }); diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionsActions.ts index f5bb1021c70..390fe796ccd 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensionsActions.ts @@ -17,7 +17,7 @@ import { IExtension, ExtensionState, IExtensionsWorkbenchService, VIEWLET_ID, IE import { ExtensionsConfigurationInitialContent } from 'vs/workbench/contrib/extensions/common/extensionsFileTemplate'; import { IExtensionEnablementService, IExtensionTipsService, EnablementState, ExtensionsLabel, IExtensionRecommendation, IGalleryExtension, IExtensionsConfigContent, IExtensionGalleryService, INSTALL_ERROR_MALICIOUS, INSTALL_ERROR_INCOMPATIBLE, IGalleryExtensionVersion, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; -import { ExtensionType, ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { ExtensionType, ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ShowViewletAction } from 'vs/workbench/browser/viewlet'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; @@ -25,7 +25,7 @@ import { Query } from 'vs/workbench/contrib/extensions/common/extensionQuery'; import { IFileService, IContent } from 'vs/platform/files/common/files'; import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows'; -import { IExtensionService, IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { URI } from 'vs/base/common/uri'; import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionsDependencyChecker.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionsDependencyChecker.ts new file mode 100644 index 00000000000..260e6ffda4b --- /dev/null +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensionsDependencyChecker.ts @@ -0,0 +1,79 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; +import { localize } from 'vs/nls'; +import { values } from 'vs/base/common/map'; +import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; +import { Action } from 'vs/base/common/actions'; +import { IWindowService } from 'vs/platform/windows/common/windows'; +import { Disposable } from 'vs/base/common/lifecycle'; + +export class ExtensionDependencyChecker extends Disposable implements IWorkbenchContribution { + + constructor( + @IExtensionService private readonly extensionService: IExtensionService, + @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, + @INotificationService private readonly notificationService: INotificationService, + @IWindowService private readonly windowService: IWindowService + ) { + super(); + CommandsRegistry.registerCommand('workbench.extensions.installMissingDepenencies', () => this.installMissingDependencies()); + MenuRegistry.appendMenuItem(MenuId.CommandPalette, { + command: { + id: 'workbench.extensions.installMissingDepenencies', + category: localize('extensions', "Extensions"), + title: localize('auto install missing deps', "Install Missing Dependencies") + } + }); + } + + private async getUninstalledMissingDependencies(): Promise { + const allMissingDependencies = await this.getAllMissingDependencies(); + const localExtensions = await this.extensionsWorkbenchService.queryLocal(); + return allMissingDependencies.filter(id => localExtensions.every(l => !areSameExtensions(l.identifier, { id }))); + } + + private async getAllMissingDependencies(): Promise { + const runningExtensions = await this.extensionService.getExtensions(); + const runningExtensionsIds: Set = runningExtensions.reduce((result, r) => { result.add(r.identifier.value.toLowerCase()); return result; }, new Set()); + const missingDependencies: Set = new Set(); + for (const extension of runningExtensions) { + if (extension.extensionDependencies) { + extension.extensionDependencies.forEach(dep => { + if (!runningExtensionsIds.has(dep.toLowerCase())) { + missingDependencies.add(dep); + } + }); + } + } + return values(missingDependencies); + } + + private async installMissingDependencies(): Promise { + const missingDependencies = await this.getUninstalledMissingDependencies(); + if (missingDependencies.length) { + const extensions = (await this.extensionsWorkbenchService.queryGallery({ names: missingDependencies, pageSize: missingDependencies.length })).firstPage; + if (extensions.length) { + await Promise.all(extensions.map(extension => this.extensionsWorkbenchService.install(extension))); + this.notificationService.notify({ + severity: Severity.Info, + message: localize('finished installing missing deps', "Finished installing missing dependencies. Please reload the window now."), + actions: { + primary: [new Action('realod', localize('reload', "Realod Window"), '', true, + () => this.windowService.reloadWindow())] + } + }); + } + } else { + this.notificationService.info(localize('no missing deps', "There are no missing dependencies to install.")); + } + } +} \ No newline at end of file diff --git a/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts b/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts index 01382d9e152..88f4399fc73 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts @@ -15,7 +15,7 @@ import { IInstantiationService, createDecorator } from 'vs/platform/instantiatio import { IExtensionsWorkbenchService, IExtension } from 'vs/workbench/contrib/extensions/common/extensions'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { IExtensionService, IExtensionDescription, IExtensionsStatus, IExtensionHostProfile } from 'vs/workbench/services/extensions/common/extensions'; +import { IExtensionService, IExtensionsStatus, IExtensionHostProfile } from 'vs/workbench/services/extensions/common/extensions'; import { IListVirtualDelegate, IListRenderer } from 'vs/base/browser/ui/list/list'; import { WorkbenchList } from 'vs/platform/list/browser/listService'; import { append, $, addClass, toggleClass, Dimension, clearNode } from 'vs/base/browser/dom'; @@ -42,7 +42,7 @@ import { ILabelService } from 'vs/platform/label/common/label'; import { renderOcticons } from 'vs/base/browser/ui/octiconLabel/octiconLabel'; import { join } from 'vs/base/common/path'; import { onUnexpectedError } from 'vs/base/common/errors'; -import { ExtensionIdentifier, ExtensionType } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, ExtensionType, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; export const IExtensionHostProfileService = createDecorator('extensionHostProfileService'); diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts index 61fa611b160..44fec66ca60 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts @@ -24,7 +24,7 @@ import { Emitter } from 'vs/base/common/event'; import { IPager } from 'vs/base/common/paging'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; -import { IExtensionService, IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { TestContextService, TestWindowService, TestSharedProcessService } from 'vs/workbench/test/workbenchTestServices'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -34,9 +34,9 @@ import { URLService } from 'vs/platform/url/common/urlService'; import { URI } from 'vs/base/common/uri'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { ExtensionManagementServerService } from 'vs/workbench/services/extensions/electron-browser/extensionManagementServerService'; -import { IRemoteAgentService } from 'vs/workbench/services/remote/node/remoteAgentService'; +import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { RemoteAgentService } from 'vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl'; -import { ExtensionIdentifier, IExtensionContributions, ExtensionType } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, IExtensionContributions, ExtensionType, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; suite('ExtensionsActions Test', () => { diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts index a00cc90eebf..45f003ec025 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts @@ -35,7 +35,7 @@ import { URI } from 'vs/base/common/uri'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { SinonStub } from 'sinon'; import { IExperimentService, ExperimentService, ExperimentState, ExperimentActionType } from 'vs/workbench/contrib/experiments/node/experimentService'; -import { IRemoteAgentService } from 'vs/workbench/services/remote/node/remoteAgentService'; +import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { RemoteAgentService } from 'vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl'; import { ExtensionManagementServerService } from 'vs/workbench/services/extensions/electron-browser/extensionManagementServerService'; import { ExtensionIdentifier, ExtensionType } from 'vs/platform/extensions/common/extensions'; diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts index bd310e41d61..e727ec888c7 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts @@ -38,7 +38,7 @@ import { URI } from 'vs/base/common/uri'; import { CancellationToken } from 'vs/base/common/cancellation'; import { ExtensionType } from 'vs/platform/extensions/common/extensions'; import { ExtensionManagementServerService } from 'vs/workbench/services/extensions/electron-browser/extensionManagementServerService'; -import { IRemoteAgentService } from 'vs/workbench/services/remote/node/remoteAgentService'; +import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { RemoteAgentService } from 'vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl'; import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; diff --git a/src/vs/workbench/contrib/files/browser/views/explorerView.ts b/src/vs/workbench/contrib/files/browser/views/explorerView.ts index 2878759fabf..f037b2c733b 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerView.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerView.ts @@ -26,7 +26,7 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { ResourceContextKey } from 'vs/workbench/common/resources'; import { IDecorationsService } from 'vs/workbench/services/decorations/browser/decorations'; -import { WorkbenchAsyncDataTree, IListService, TreeResourceNavigator2 } from 'vs/platform/list/browser/listService'; +import { WorkbenchAsyncDataTree, TreeResourceNavigator2 } from 'vs/platform/list/browser/listService'; import { DelayedDragHandler } from 'vs/base/browser/dnd'; import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { IViewletPanelOptions, ViewletPanel } from 'vs/workbench/browser/parts/views/panelViewlet'; @@ -83,7 +83,6 @@ export class ExplorerView extends ViewletPanel { @IDecorationsService decorationService: IDecorationsService, @ILabelService private readonly labelService: ILabelService, @IThemeService private readonly themeService: IWorkbenchThemeService, - @IListService private readonly listService: IListService, @IMenuService private readonly menuService: IMenuService, @ITelemetryService private readonly telemetryService: ITelemetryService, @IExplorerService private readonly explorerService: IExplorerService, @@ -279,20 +278,21 @@ export class ExplorerView extends ViewletPanel { this.disposables.push(createFileIconThemableTreeContainerScope(container, this.themeService)); - this.tree = new WorkbenchAsyncDataTree(container, new ExplorerDelegate(), [filesRenderer], + this.tree = this.instantiationService.createInstance(WorkbenchAsyncDataTree, container, new ExplorerDelegate(), [filesRenderer], this.instantiationService.createInstance(ExplorerDataSource), { accessibilityProvider: new ExplorerAccessibilityProvider(), ariaLabel: nls.localize('treeAriaLabel', "Files Explorer"), identityProvider: { - getId: stat => stat.resource + getId: stat => (stat).resource }, keyboardNavigationLabelProvider: { getKeyboardNavigationLabel: stat => { - if (this.explorerService.isEditable(stat)) { + const item = stat; + if (this.explorerService.isEditable(item)) { return undefined; } - return stat.name; + return item.name; } }, multipleSelectionSupport: true, @@ -300,7 +300,7 @@ export class ExplorerView extends ViewletPanel { sorter: this.instantiationService.createInstance(FileSorter), dnd: this.instantiationService.createInstance(FileDragAndDrop), autoExpandSingleChildren: true - }, this.contextKeyService, this.listService, this.themeService, this.configurationService, this.keybindingService); + }) as WorkbenchAsyncDataTree; this.disposables.push(this.tree); // Bind context keys @@ -340,7 +340,7 @@ export class ExplorerView extends ViewletPanel { this.disposables.push(this.tree.onKeyDown(e => { const event = new StandardKeyboardEvent(e); const toggleCollapsed = isMacintosh ? (event.keyCode === KeyCode.DownArrow && event.metaKey) : event.keyCode === KeyCode.Enter; - if (toggleCollapsed) { + if (toggleCollapsed && !this.explorerService.isEditable(undefined)) { const focus = this.tree.getFocus(); if (focus.length === 1 && focus[0].isDirectory) { this.tree.toggleCollapsed(focus[0]); diff --git a/src/vs/workbench/contrib/output/browser/outputPanel.ts b/src/vs/workbench/contrib/output/browser/outputPanel.ts index 944b643be3c..793df5e5c8b 100644 --- a/src/vs/workbench/contrib/output/browser/outputPanel.ts +++ b/src/vs/workbench/contrib/output/browser/outputPanel.ts @@ -141,7 +141,7 @@ export class OutputPanel extends AbstractTextResourceEditor { } protected createEditor(parent: HTMLElement): void { - // First create the scoped instantation service and only then construct the editor using the scoped service + // First create the scoped instantiation service and only then construct the editor using the scoped service const scopedContextKeyService = this._register(this.contextKeyService.createScoped(parent)); this.scopedInstantiationService = this.instantiationService.createChild(new ServiceCollection([IContextKeyService, scopedContextKeyService])); super.createEditor(parent); diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts b/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts index 91408f607b3..23a70c0f146 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts @@ -49,11 +49,12 @@ import { SearchWidget, SettingsTarget, SettingsTargetsWidget } from 'vs/workbenc import { CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_SEARCH_FOCUS, IPreferencesSearchService, ISearchProvider, CONTEXT_SETTINGS_JSON_EDITOR } from 'vs/workbench/contrib/preferences/common/preferences'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { IFilterResult, IPreferencesService, ISearchResult, ISetting, ISettingsEditorModel, ISettingsGroup, SettingsEditorOptions } from 'vs/workbench/services/preferences/common/preferences'; +import { IFilterResult, IPreferencesService, ISetting, ISettingsEditorModel, ISettingsGroup, SettingsEditorOptions } from 'vs/workbench/services/preferences/common/preferences'; import { DefaultPreferencesEditorInput, PreferencesEditorInput } from 'vs/workbench/services/preferences/common/preferencesEditorInput'; import { DefaultSettingsEditorModel, SettingsEditorModel } from 'vs/workbench/services/preferences/common/preferencesModels'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IWindowService } from 'vs/platform/windows/common/windows'; +import { withNullAsUndefined, withUndefinedAsNull } from 'vs/base/common/types'; export class PreferencesEditor extends BaseEditor { @@ -72,7 +73,7 @@ export class PreferencesEditor extends BaseEditor { private remoteSearchThrottle: ThrottledDelayer; private _lastReportedFilter: string; - private lastFocusedWidget: SearchWidget | SideBySidePreferencesWidget = null; + private lastFocusedWidget: SearchWidget | SideBySidePreferencesWidget | undefined = undefined; get minimumWidth(): number { return this.sideBySidePreferencesWidget ? this.sideBySidePreferencesWidget.minimumWidth : 0; } get maximumWidth(): number { return this.sideBySidePreferencesWidget ? this.sideBySidePreferencesWidget.maximumWidth : Number.POSITIVE_INFINITY; } @@ -83,8 +84,8 @@ export class PreferencesEditor extends BaseEditor { readonly minimumHeight = 260; - private _onDidCreateWidget = new Emitter<{ width: number; height: number; }>(); - readonly onDidSizeConstraintsChange: Event<{ width: number; height: number; }> = this._onDidCreateWidget.event; + private _onDidCreateWidget = new Emitter<{ width: number; height: number; } | undefined>(); + readonly onDidSizeConstraintsChange: Event<{ width: number; height: number; } | undefined> = this._onDidCreateWidget.event; constructor( @IPreferencesService private readonly preferencesService: IPreferencesService, @@ -169,7 +170,7 @@ export class PreferencesEditor extends BaseEditor { this.sideBySidePreferencesWidget.layout(new DOM.Dimension(dimension.width, dimension.height - headerHeight)); } - getControl(): IEditorControl { + getControl(): IEditorControl | null { return this.sideBySidePreferencesWidget.getControl(); } @@ -209,11 +210,11 @@ export class PreferencesEditor extends BaseEditor { private updateInput(newInput: PreferencesEditorInput, options: EditorOptions, token: CancellationToken): Promise { return this.sideBySidePreferencesWidget.setInput(newInput.details, newInput.master, options, token).then(({ defaultPreferencesRenderer, editablePreferencesRenderer }) => { if (token.isCancellationRequested) { - return undefined; + return; } - this.preferencesRenderers.defaultPreferencesRenderer = defaultPreferencesRenderer; - this.preferencesRenderers.editablePreferencesRenderer = editablePreferencesRenderer; + this.preferencesRenderers.defaultPreferencesRenderer = defaultPreferencesRenderer!; + this.preferencesRenderers.editablePreferencesRenderer = editablePreferencesRenderer!; this.onInputChanged(); }); } @@ -258,7 +259,7 @@ export class PreferencesEditor extends BaseEditor { if (target === ConfigurationTarget.USER) { this.preferencesService.switchSettings(ConfigurationTarget.USER, this.preferencesService.userSettingsResource, true); } else if (target === ConfigurationTarget.WORKSPACE) { - this.preferencesService.switchSettings(ConfigurationTarget.WORKSPACE, this.preferencesService.workspaceSettingsResource, true); + this.preferencesService.switchSettings(ConfigurationTarget.WORKSPACE, this.preferencesService.workspaceSettingsResource!, true); } else if (target instanceof URI) { this.preferencesService.switchSettings(ConfigurationTarget.WORKSPACE_FOLDER, target, true); } @@ -297,7 +298,7 @@ export class PreferencesEditor extends BaseEditor { return result; } - private reportFilteringUsed(filter: string, filterResult: IFilterResult): void { + private reportFilteringUsed(filter: string, filterResult: IFilterResult | undefined): void { if (filter && filter !== this._lastReportedFilter) { const metadata = filterResult && filterResult.metadata; const counts = filterResult && this._countById(filterResult.filteredGroups); @@ -338,11 +339,11 @@ export class PreferencesEditor extends BaseEditor { class SettingsNavigator extends ArrayNavigator { - next(): ISetting { + next(): ISetting | null { return super.next() || super.first(); } - previous(): ISetting { + previous(): ISetting | null { return super.previous() || super.last(); } @@ -365,13 +366,13 @@ class PreferencesRenderersController extends Disposable { private _editablePreferencesRendererDisposables: IDisposable[] = []; private _settingsNavigator: SettingsNavigator; - private _remoteFilterCancelToken: CancellationTokenSource; + private _remoteFilterCancelToken: CancellationTokenSource | undefined; private _prefsModelsForSearch = new Map(); private _currentLocalSearchProvider: ISearchProvider; - private _currentRemoteSearchProvider: ISearchProvider; + private _currentRemoteSearchProvider: ISearchProvider | undefined; private _lastQuery: string; - private _lastFilterResult: IFilterResult; + private _lastFilterResult: IFilterResult | undefined; private readonly _onDidFilterResultsCountChange: Emitter = this._register(new Emitter()); readonly onDidFilterResultsCountChange: Event = this._onDidFilterResultsCountChange.event; @@ -386,7 +387,7 @@ class PreferencesRenderersController extends Disposable { super(); } - get lastFilterResult(): IFilterResult { + get lastFilterResult(): IFilterResult | undefined { return this._lastFilterResult; } @@ -449,20 +450,20 @@ class PreferencesRenderersController extends Disposable { if (this._remoteFilterCancelToken) { this._remoteFilterCancelToken.cancel(); this._remoteFilterCancelToken.dispose(); - this._remoteFilterCancelToken = null; + this._remoteFilterCancelToken = undefined; } this._currentRemoteSearchProvider = (updateCurrentResults && this._currentRemoteSearchProvider) || this.preferencesSearchService.getRemoteSearchProvider(query); this._remoteFilterCancelToken = new CancellationTokenSource(); - return this.filterOrSearchPreferences(query, this._currentRemoteSearchProvider, 'nlpResult', nls.localize('nlpResult', "Natural Language Results"), 1, this._remoteFilterCancelToken.token, updateCurrentResults).then(() => { + return this.filterOrSearchPreferences(query, this._currentRemoteSearchProvider!, 'nlpResult', nls.localize('nlpResult', "Natural Language Results"), 1, this._remoteFilterCancelToken.token, updateCurrentResults).then(() => { if (this._remoteFilterCancelToken) { this._remoteFilterCancelToken.dispose(); - this._remoteFilterCancelToken = null; + this._remoteFilterCancelToken = undefined; } }, err => { if (isPromiseCanceledError(err)) { - return null; + return; } else { onUnexpectedError(err); } @@ -481,12 +482,12 @@ class PreferencesRenderersController extends Disposable { private filterOrSearchPreferences(query: string, searchProvider: ISearchProvider, groupId: string, groupLabel: string, groupOrder: number, token?: CancellationToken, editableContentOnly?: boolean): Promise { this._lastQuery = query; - const filterPs: Promise[] = [this._filterOrSearchPreferences(query, this.editablePreferencesRenderer, searchProvider, groupId, groupLabel, groupOrder, token)]; + const filterPs: Promise[] = [this._filterOrSearchPreferences(query, this.editablePreferencesRenderer, searchProvider, groupId, groupLabel, groupOrder, token)]; if (!editableContentOnly) { filterPs.push( this._filterOrSearchPreferences(query, this.defaultPreferencesRenderer, searchProvider, groupId, groupLabel, groupOrder, token)); filterPs.push( - this.searchAllSettingsTargets(query, searchProvider, groupId, groupLabel, groupOrder, token).then(() => null)); + this.searchAllSettingsTargets(query, searchProvider, groupId, groupLabel, groupOrder, token).then(() => undefined)); } return Promise.all(filterPs).then(results => { @@ -511,21 +512,21 @@ class PreferencesRenderersController extends Disposable { for (const folder of this.workspaceContextService.getWorkspace().folders) { const folderSettingsResource = this.preferencesService.getFolderSettingsResource(folder.uri); - searchPs.push(this.searchSettingsTarget(query, searchProvider, folderSettingsResource, groupId, groupLabel, groupOrder, token)); + searchPs.push(this.searchSettingsTarget(query, searchProvider, withNullAsUndefined(folderSettingsResource), groupId, groupLabel, groupOrder, token)); } return Promise.all(searchPs).then(() => { }); } - private searchSettingsTarget(query: string, provider: ISearchProvider, target: SettingsTarget, groupId: string, groupLabel: string, groupOrder: number, token?: CancellationToken): Promise { + private searchSettingsTarget(query: string, provider: ISearchProvider, target: SettingsTarget | undefined, groupId: string, groupLabel: string, groupOrder: number, token?: CancellationToken): Promise { if (!query) { // Don't open the other settings targets when query is empty this._onDidFilterResultsCountChange.fire({ target, count: 0 }); - return Promise.resolve(undefined); + return Promise.resolve(); } - return this.getPreferencesEditorModel(target).then(model => { + return this.getPreferencesEditorModel(target).then(model => { return model && this._filterOrSearchPreferencesModel('', model, provider, groupId, groupLabel, groupOrder, token); }).then(result => { const count = result ? this._flatten(result.filteredGroups).length : 0; @@ -535,17 +536,17 @@ class PreferencesRenderersController extends Disposable { return Promise.reject(err); } - return null; + return undefined; }); } - private async getPreferencesEditorModel(target: SettingsTarget): Promise { + private async getPreferencesEditorModel(target: SettingsTarget | undefined): Promise { const resource = target === ConfigurationTarget.USER ? this.preferencesService.userSettingsResource : target === ConfigurationTarget.WORKSPACE ? this.preferencesService.workspaceSettingsResource : target; if (!resource) { - return null; + return undefined; } const targetKey = resource.toString(); @@ -555,7 +556,7 @@ class PreferencesRenderersController extends Disposable { this._prefsModelsForSearch.set(targetKey, model); } catch (e) { // Will throw when the settings file doesn't exist. - return null; + return undefined; } } @@ -578,15 +579,15 @@ class PreferencesRenderersController extends Disposable { } const setting = this._settingsNavigator.current(); - const shownInEditableRenderer = this._editablePreferencesRenderer.editPreference(setting); + const shownInEditableRenderer = this._editablePreferencesRenderer.editPreference(setting!); if (!shownInEditableRenderer) { - this.defaultPreferencesRenderer.editPreference(setting); + this.defaultPreferencesRenderer.editPreference(setting!); } } - private _filterOrSearchPreferences(filter: string, preferencesRenderer: IPreferencesRenderer, provider: ISearchProvider, groupId: string, groupLabel: string, groupOrder: number, token?: CancellationToken): Promise { + private _filterOrSearchPreferences(filter: string, preferencesRenderer: IPreferencesRenderer, provider: ISearchProvider, groupId: string, groupLabel: string, groupOrder: number, token?: CancellationToken): Promise { if (!preferencesRenderer) { - return Promise.resolve(null); + return Promise.resolve(undefined); } const model = preferencesRenderer.preferencesModel; @@ -596,10 +597,10 @@ class PreferencesRenderersController extends Disposable { }); } - private _filterOrSearchPreferencesModel(filter: string, model: ISettingsEditorModel, provider: ISearchProvider, groupId: string, groupLabel: string, groupOrder: number, token?: CancellationToken): Promise { + private _filterOrSearchPreferencesModel(filter: string, model: ISettingsEditorModel, provider: ISearchProvider, groupId: string, groupLabel: string, groupOrder: number, token?: CancellationToken): Promise { const searchP = provider ? provider.searchModel(model, token) : Promise.resolve(null); return searchP - .then(null, err => { + .then(null, err => { if (isPromiseCanceledError(err)) { return Promise.reject(err); } else { @@ -615,7 +616,7 @@ class PreferencesRenderersController extends Disposable { this.telemetryService.publicLog('defaultSettings.searchError', { message, filter }); this.logService.info('Setting search error: ' + message); } - return null; + return undefined; } }) .then(searchResult => { @@ -630,7 +631,7 @@ class PreferencesRenderersController extends Disposable { result: searchResult, order: groupOrder }) : - model.updateResultGroup(groupId, null); + model.updateResultGroup(groupId, undefined); if (filterResult) { filterResult.query = filter; @@ -641,7 +642,7 @@ class PreferencesRenderersController extends Disposable { }); } - private consolidateAndUpdate(defaultFilterResult: IFilterResult, editableFilterResult: IFilterResult): void { + private consolidateAndUpdate(defaultFilterResult: IFilterResult | undefined, editableFilterResult: IFilterResult | undefined): void { const defaultPreferencesFilteredGroups = defaultFilterResult ? defaultFilterResult.filteredGroups : this._getAllPreferences(this._defaultPreferencesRenderer); const editablePreferencesFilteredGroups = editableFilterResult ? editableFilterResult.filteredGroups : this._getAllPreferences(this._editablePreferencesRenderer); const consolidatedSettings = this._consolidateSettings(editablePreferencesFilteredGroups, defaultPreferencesFilteredGroups); @@ -670,7 +671,7 @@ class PreferencesRenderersController extends Disposable { return preferencesRenderer ? (preferencesRenderer.preferencesModel).settingsGroups : []; } - private _focusPreference(preference: ISetting, preferencesRenderer: IPreferencesRenderer): void { + private _focusPreference(preference: ISetting | null, preferencesRenderer: IPreferencesRenderer): void { if (preference && preferencesRenderer) { preferencesRenderer.focusPreference(preference); } @@ -718,7 +719,7 @@ class PreferencesRenderersController extends Disposable { this.telemetryService.publicLog('defaultSettingsActions.copySetting', data); } - private _findSetting(filterResult: IFilterResult, key: string): { groupIdx: number, settingIdx: number, overallSettingIdx: number } { + private _findSetting(filterResult: IFilterResult, key: string): { groupIdx: number, settingIdx: number, overallSettingIdx: number } | undefined { let overallSettingIdx = 0; for (let groupIdx = 0; groupIdx < filterResult.filteredGroups.length; groupIdx++) { @@ -733,7 +734,7 @@ class PreferencesRenderersController extends Disposable { } } - return null; + return undefined; } private _consolidateSettings(editableSettingsGroups: ISettingsGroup[], defaultSettingsGroups: ISettingsGroup[]): ISetting[] { @@ -847,17 +848,17 @@ class SideBySidePreferencesWidget extends Widget { setInput(defaultPreferencesEditorInput: DefaultPreferencesEditorInput, editablePreferencesEditorInput: EditorInput, options: EditorOptions, token: CancellationToken): Promise<{ defaultPreferencesRenderer?: IPreferencesRenderer, editablePreferencesRenderer?: IPreferencesRenderer }> { this.getOrCreateEditablePreferencesEditor(editablePreferencesEditorInput); - this.settingsTargetsWidget.settingsTarget = this.getSettingsTarget(editablePreferencesEditorInput.getResource()); + this.settingsTargetsWidget.settingsTarget = this.getSettingsTarget(editablePreferencesEditorInput.getResource()!); return Promise.all([ - this.updateInput(this.defaultPreferencesEditor, defaultPreferencesEditorInput, DefaultSettingsEditorContribution.ID, editablePreferencesEditorInput.getResource(), options, token), - this.updateInput(this.editablePreferencesEditor, editablePreferencesEditorInput, SettingsEditorContribution.ID, defaultPreferencesEditorInput.getResource(), options, token) + this.updateInput(this.defaultPreferencesEditor, defaultPreferencesEditorInput, DefaultSettingsEditorContribution.ID, editablePreferencesEditorInput.getResource()!, options, token), + this.updateInput(this.editablePreferencesEditor, editablePreferencesEditorInput, SettingsEditorContribution.ID, defaultPreferencesEditorInput.getResource()!, options, token) ]) .then(([defaultPreferencesRenderer, editablePreferencesRenderer]) => { if (token.isCancellationRequested) { return {}; } - this.defaultPreferencesHeader.textContent = defaultPreferencesRenderer && this.getDefaultPreferencesHeaderText((defaultPreferencesRenderer.preferencesModel).target); + this.defaultPreferencesHeader.textContent = withUndefinedAsNull(defaultPreferencesRenderer && this.getDefaultPreferencesHeaderText((defaultPreferencesRenderer.preferencesModel).target)); return { defaultPreferencesRenderer, editablePreferencesRenderer }; }); } @@ -889,7 +890,7 @@ class SideBySidePreferencesWidget extends Widget { } } - getControl(): IEditorControl { + getControl(): IEditorControl | null { return this.editablePreferencesEditor ? this.editablePreferencesEditor.getControl() : null; } @@ -919,7 +920,7 @@ class SideBySidePreferencesWidget extends Widget { return this.editablePreferencesEditor; } const descriptor = Registry.as(EditorExtensions.Editors).getEditor(editorInput); - const editor = descriptor.instantiate(this.instantiationService); + const editor = descriptor!.instantiate(this.instantiationService); this.editablePreferencesEditor = editor; this.editablePreferencesEditor.create(this.editablePreferencesEditorContainer); this.editablePreferencesEditor.setVisible(this.isVisible, this.group); @@ -928,14 +929,14 @@ class SideBySidePreferencesWidget extends Widget { return editor; } - private updateInput(editor: BaseEditor, input: EditorInput, editorContributionId: string, associatedPreferencesModelUri: URI, options: EditorOptions, token: CancellationToken): Promise> { + private updateInput(editor: BaseEditor, input: EditorInput, editorContributionId: string, associatedPreferencesModelUri: URI, options: EditorOptions, token: CancellationToken): Promise | undefined> { return editor.setInput(input, options, token) - .then(() => { + .then(() => { if (token.isCancellationRequested) { return undefined; } - return (editor.getControl()).getContribution(editorContributionId).updatePreferencesRenderer(associatedPreferencesModelUri); + return withNullAsUndefined((editor.getControl()).getContribution(editorContributionId).updatePreferencesRenderer(associatedPreferencesModelUri)); }); } @@ -960,11 +961,9 @@ class SideBySidePreferencesWidget extends Widget { private disposeEditors(): void { if (this.defaultPreferencesEditor) { this.defaultPreferencesEditor.dispose(); - this.defaultPreferencesEditor = null; } if (this.editablePreferencesEditor) { this.editablePreferencesEditor.dispose(); - this.editablePreferencesEditor = null; } } @@ -1012,7 +1011,7 @@ export class DefaultPreferencesEditor extends BaseTextEditor { private showReadonlyHint(editor: ICodeEditor): void { const messageController = MessageController.get(editor); if (!messageController.isVisible()) { - messageController.showMessage(nls.localize('defaultEditorReadonly', "Edit in the right hand side editor to override defaults."), editor.getSelection().getPosition()); + messageController.showMessage(nls.localize('defaultEditorReadonly', "Edit in the right hand side editor to override defaults."), editor.getSelection()!.getPosition()); } } @@ -1038,17 +1037,17 @@ export class DefaultPreferencesEditor extends BaseTextEditor { setInput(input: DefaultPreferencesEditorInput, options: EditorOptions, token: CancellationToken): Promise { return super.setInput(input, options, token) - .then(() => this.input.resolve() - .then(editorModel => { + .then(() => this.input!.resolve() + .then(editorModel => { if (token.isCancellationRequested) { return undefined; } - return editorModel.load(); + return editorModel!.load(); }) .then(editorModel => { if (token.isCancellationRequested) { - return undefined; + return; } this.getControl().setModel((editorModel).textEditorModel); @@ -1074,13 +1073,13 @@ export class DefaultPreferencesEditor extends BaseTextEditor { interface ISettingsEditorContribution extends editorCommon.IEditorContribution { - updatePreferencesRenderer(associatedPreferencesModelUri: URI): Promise>; + updatePreferencesRenderer(associatedPreferencesModelUri: URI): Promise | null>; } abstract class AbstractSettingsEditorContribution extends Disposable implements ISettingsEditorContribution { - private preferencesRendererCreationPromise: Promise>; + private preferencesRendererCreationPromise: Promise | null> | null; constructor(protected editor: ICodeEditor, @IInstantiationService protected instantiationService: IInstantiationService, @@ -1113,15 +1112,15 @@ abstract class AbstractSettingsEditorContribution extends Disposable implements } private _hasAssociatedPreferencesModelChanged(associatedPreferencesModelUri: URI): Promise { - return this.preferencesRendererCreationPromise.then(preferencesRenderer => { - return !(preferencesRenderer && preferencesRenderer.getAssociatedPreferencesModel() && preferencesRenderer.getAssociatedPreferencesModel().uri.toString() === associatedPreferencesModelUri.toString()); + return this.preferencesRendererCreationPromise!.then(preferencesRenderer => { + return !(preferencesRenderer && preferencesRenderer.getAssociatedPreferencesModel() && preferencesRenderer.getAssociatedPreferencesModel().uri!.toString() === associatedPreferencesModelUri.toString()); }); } - private _updatePreferencesRenderer(associatedPreferencesModelUri: URI): Promise> { + private _updatePreferencesRenderer(associatedPreferencesModelUri: URI): Promise | null> { return this.preferencesService.createPreferencesEditorModel(associatedPreferencesModelUri) .then(associatedPreferencesEditorModel => { - return this.preferencesRendererCreationPromise.then(preferencesRenderer => { + return this.preferencesRendererCreationPromise!.then(preferencesRenderer => { if (preferencesRenderer) { const associatedPreferencesModel = preferencesRenderer.getAssociatedPreferencesModel(); if (associatedPreferencesModel) { @@ -1155,7 +1154,7 @@ abstract class AbstractSettingsEditorContribution extends Disposable implements super.dispose(); } - protected abstract _createPreferencesRenderer(): Promise>; + protected abstract _createPreferencesRenderer(): Promise | null> | null; abstract getId(): string; } @@ -1167,9 +1166,9 @@ class DefaultSettingsEditorContribution extends AbstractSettingsEditorContributi return DefaultSettingsEditorContribution.ID; } - protected _createPreferencesRenderer(): Promise> { - return this.preferencesService.createPreferencesEditorModel(this.editor.getModel().uri) - .then(editorModel => { + protected _createPreferencesRenderer(): Promise | null> | null { + return this.preferencesService.createPreferencesEditorModel(this.editor.getModel()!.uri) + .then(editorModel => { if (editorModel instanceof DefaultSettingsEditorModel && this.editor.getModel()) { const preferencesRenderer = this.instantiationService.createInstance(DefaultSettingsRenderer, this.editor, editorModel); preferencesRenderer.render(); @@ -1197,10 +1196,10 @@ class SettingsEditorContribution extends AbstractSettingsEditorContribution impl return SettingsEditorContribution.ID; } - protected _createPreferencesRenderer(): Promise> { + protected _createPreferencesRenderer(): Promise | null> | null { if (this.isSettingsModel()) { - return this.preferencesService.createPreferencesEditorModel(this.editor.getModel().uri) - .then(settingsModel => { + return this.preferencesService.createPreferencesEditorModel(this.editor.getModel()!.uri) + .then(settingsModel => { if (settingsModel instanceof SettingsEditorModel && this.editor.getModel()) { switch (settingsModel.configurationTarget) { case ConfigurationTarget.USER: diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts b/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts index 2a41d7d7568..ff56b5797de 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts @@ -15,7 +15,7 @@ import { ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorE import { Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; import * as editorCommon from 'vs/editor/common/editorCommon'; -import { IModelDeltaDecoration, ITextModel, TrackedRangeStickiness } from 'vs/editor/common/model'; +import { IModelDeltaDecoration, TrackedRangeStickiness } from 'vs/editor/common/model'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import * as nls from 'vs/nls'; import { ConfigurationTarget, IConfigurationService, overrideIdentifierFromKey } from 'vs/platform/configuration/common/configuration'; @@ -38,13 +38,13 @@ export interface IPreferencesRenderer extends IDisposable { onFocusPreference: Event; onClearFocusPreference: Event; - onUpdatePreference?: Event<{ key: string, value: any, source: T }>; + onUpdatePreference: Event<{ key: string, value: any, source: T }>; render(): void; updatePreference(key: string, value: any, source: T): void; focusPreference(setting: T): void; clearFocus(setting: T): void; - filterPreferences(filterResult: IFilterResult): void; + filterPreferences(filterResult: IFilterResult | undefined): void; editPreference(setting: T): boolean; } @@ -65,7 +65,7 @@ export class UserSettingsRenderer extends Disposable implements IPreferencesRend private readonly _onUpdatePreference: Emitter<{ key: string, value: any, source: IIndexedSetting }> = new Emitter<{ key: string, value: any, source: IIndexedSetting }>(); readonly onUpdatePreference: Event<{ key: string, value: any, source: IIndexedSetting }> = this._onUpdatePreference.event; - private filterResult: IFilterResult; + private filterResult: IFilterResult | undefined; constructor(protected editor: ICodeEditor, readonly preferencesModel: SettingsEditorModel, @IPreferencesService protected preferencesService: IPreferencesService, @@ -77,7 +77,7 @@ export class UserSettingsRenderer extends Disposable implements IPreferencesRend this.highlightMatchesRenderer = this._register(instantiationService.createInstance(HighlightMatchesRenderer, editor)); this.editSettingActionRenderer = this._register(this.instantiationService.createInstance(EditSettingRenderer, this.editor, this.preferencesModel, this.settingHighlighter)); this._register(this.editSettingActionRenderer.onUpdateSetting(({ key, value, source }) => this._updatePreference(key, value, source))); - this._register(this.editor.getModel().onDidChangeContent(() => this.modelChangeDelayer.trigger(() => this.onModelChanged()))); + this._register(this.editor.getModel()!.onDidChangeContent(() => this.modelChangeDelayer.trigger(() => this.onModelChanged()))); } @@ -126,7 +126,7 @@ export class UserSettingsRenderer extends Disposable implements IPreferencesRend private onSettingUpdated(setting: ISetting) { this.editor.focus(); - setting = this.getSetting(setting); + setting = this.getSetting(setting)!; if (setting) { // TODO:@sandy Selection range should be template range this.editor.setSelection(setting.valueRange); @@ -134,22 +134,22 @@ export class UserSettingsRenderer extends Disposable implements IPreferencesRend } } - private getSetting(setting: ISetting): ISetting { + private getSetting(setting: ISetting): ISetting | undefined { const { key, overrideOf } = setting; if (overrideOf) { const setting = this.getSetting(overrideOf); - for (const override of setting.overrides) { + for (const override of setting!.overrides!) { if (override.key === key) { return override; } } - return null; + return undefined; } return this.preferencesModel.getPreference(key); } - filterPreferences(filterResult: IFilterResult): void { + filterPreferences(filterResult: IFilterResult | undefined): void { this.filterResult = filterResult; this.settingHighlighter.clear(true); this.highlightMatchesRenderer.render(filterResult ? filterResult.matches : []); @@ -171,7 +171,7 @@ export class UserSettingsRenderer extends Disposable implements IPreferencesRend editPreference(setting: ISetting): boolean { const editableSetting = this.getSetting(setting); - return editableSetting && this.editSettingActionRenderer.activateOnSetting(editableSetting); + return !!(editableSetting && this.editSettingActionRenderer.activateOnSetting(editableSetting)); } } @@ -231,7 +231,7 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR private hiddenAreasRenderer: HiddenAreasRenderer; private editSettingActionRenderer: EditSettingRenderer; private bracesHidingRenderer: BracesHidingRenderer; - private filterResult: IFilterResult; + private filterResult: IFilterResult | undefined; private readonly _onUpdatePreference: Emitter<{ key: string, value: any, source: IIndexedSetting }> = new Emitter<{ key: string, value: any, source: IIndexedSetting }>(); readonly onUpdatePreference: Event<{ key: string, value: any, source: IIndexedSetting }> = this._onUpdatePreference.event; @@ -273,28 +273,28 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR this.settingsGroupTitleRenderer.render(this.preferencesModel.settingsGroups); this.editSettingActionRenderer.render(this.preferencesModel.settingsGroups, this._associatedPreferencesModel); this.settingHighlighter.clear(true); - this.bracesHidingRenderer.render(null, this.preferencesModel.settingsGroups); + this.bracesHidingRenderer.render(undefined, this.preferencesModel.settingsGroups); this.settingsGroupTitleRenderer.showGroup(0); this.hiddenAreasRenderer.render(); } - filterPreferences(filterResult: IFilterResult): void { + filterPreferences(filterResult: IFilterResult | undefined): void { this.filterResult = filterResult; if (filterResult) { this.filteredMatchesRenderer.render(filterResult, this.preferencesModel.settingsGroups); - this.settingsGroupTitleRenderer.render(null); + this.settingsGroupTitleRenderer.render(undefined); this.settingsHeaderRenderer.render(filterResult); this.settingHighlighter.clear(true); this.bracesHidingRenderer.render(filterResult, this.preferencesModel.settingsGroups); this.editSettingActionRenderer.render(filterResult.filteredGroups, this._associatedPreferencesModel); } else { this.settingHighlighter.clear(true); - this.filteredMatchesRenderer.render(null, this.preferencesModel.settingsGroups); - this.settingsHeaderRenderer.render(null); + this.filteredMatchesRenderer.render(undefined, this.preferencesModel.settingsGroups); + this.settingsHeaderRenderer.render(undefined); this.settingsGroupTitleRenderer.render(this.preferencesModel.settingsGroups); this.settingsGroupTitleRenderer.showGroup(0); - this.bracesHidingRenderer.render(null, this.preferencesModel.settingsGroups); + this.bracesHidingRenderer.render(undefined, this.preferencesModel.settingsGroups); this.editSettingActionRenderer.render(this.preferencesModel.settingsGroups, this._associatedPreferencesModel); } @@ -311,22 +311,22 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR } } - private getSetting(setting: ISetting): ISetting { + private getSetting(setting: ISetting): ISetting | undefined { const { key, overrideOf } = setting; if (overrideOf) { const setting = this.getSetting(overrideOf); - for (const override of setting.overrides) { + for (const override of setting!.overrides!) { if (override.key === key) { return override; } } - return null; + return undefined; } const settingsGroups = this.filterResult ? this.filterResult.filteredGroups : this.preferencesModel.settingsGroups; return this.getPreference(key, settingsGroups); } - private getPreference(key: string, settingsGroups: ISettingsGroup[]): ISetting { + private getPreference(key: string, settingsGroups: ISettingsGroup[]): ISetting | undefined { for (const group of settingsGroups) { for (const section of group.sections) { for (const setting of section.settings) { @@ -336,7 +336,7 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR } } } - return null; + return undefined; } clearFocus(setting: ISetting): void { @@ -356,14 +356,14 @@ export interface HiddenAreasProvider { } export class BracesHidingRenderer extends Disposable implements HiddenAreasProvider { - private _result: IFilterResult; + private _result: IFilterResult | undefined; private _settingsGroups: ISettingsGroup[]; constructor(private editor: ICodeEditor) { super(); } - render(result: IFilterResult, settingsGroups: ISettingsGroup[]): void { + render(result: IFilterResult | undefined, settingsGroups: ISettingsGroup[]): void { this._result = result; this._settingsGroups = settingsGroups; } @@ -403,7 +403,7 @@ export class BracesHidingRenderer extends Disposable implements HiddenAreasProvi } // Closing square brace - const lineCount = this.editor.getModel().getLineCount(); + const lineCount = this.editor.getModel()!.getLineCount(); hiddenAreas.push({ startLineNumber: lineCount, startColumn: 1, @@ -428,7 +428,7 @@ class DefaultSettingsHeaderRenderer extends Disposable { this.onClick = this.settingsHeaderWidget.onClick; } - render(filterResult: IFilterResult) { + render(filterResult: IFilterResult | undefined) { const hasSettings = !filterResult || filterResult.filteredGroups.length > 0; this.settingsHeaderWidget.toggleMessage(hasSettings); } @@ -458,7 +458,7 @@ export class SettingsGroupTitleRenderer extends Disposable implements HiddenArea return hiddenAreas; } - render(settingsGroups: ISettingsGroup[]) { + render(settingsGroups: ISettingsGroup[] | undefined) { this.disposeWidgets(); if (!settingsGroups) { return; @@ -503,7 +503,7 @@ export class SettingsGroupTitleRenderer extends Disposable implements HiddenArea const index = this.hiddenGroups.indexOf(group); if (collapsed) { const currentPosition = this.editor.getPosition(); - if (group.range.startLineNumber <= currentPosition.lineNumber && group.range.endLineNumber >= currentPosition.lineNumber) { + if (group.range.startLineNumber <= currentPosition!.lineNumber && group.range.endLineNumber >= currentPosition!.lineNumber) { this.editor.setPosition({ lineNumber: group.range.startLineNumber - 1, column: 1 }); } this.hiddenGroups.push(group); @@ -555,19 +555,18 @@ export class FilteredMatchesRenderer extends Disposable implements HiddenAreasPr super(); } - render(result: IFilterResult, allSettingsGroups: ISettingsGroup[]): void { - const model = this.editor.getModel(); + render(result: IFilterResult | undefined, allSettingsGroups: ISettingsGroup[]): void { this.hiddenAreas = []; if (result) { - this.hiddenAreas = this.computeHiddenRanges(result.filteredGroups, result.allGroups, model); - this.decorationIds = this.editor.deltaDecorations(this.decorationIds, result.matches.map(match => this.createDecoration(match, model))); + this.hiddenAreas = this.computeHiddenRanges(result.filteredGroups, result.allGroups); + this.decorationIds = this.editor.deltaDecorations(this.decorationIds, result.matches.map(match => this.createDecoration(match))); } else { - this.hiddenAreas = this.computeHiddenRanges(null, allSettingsGroups, model); + this.hiddenAreas = this.computeHiddenRanges(undefined, allSettingsGroups); this.decorationIds = this.editor.deltaDecorations(this.decorationIds, []); } } - private createDecoration(range: IRange, model: ITextModel): IModelDeltaDecoration { + private createDecoration(range: IRange): IModelDeltaDecoration { return { range, options: FilteredMatchesRenderer._FIND_MATCH @@ -579,7 +578,7 @@ export class FilteredMatchesRenderer extends Disposable implements HiddenAreasPr className: 'findMatch' }); - private computeHiddenRanges(filteredGroups: ISettingsGroup[], allSettingsGroups: ISettingsGroup[], model: ITextModel): IRange[] { + private computeHiddenRanges(filteredGroups: ISettingsGroup[] | undefined, allSettingsGroups: ISettingsGroup[]): IRange[] { // Hide the contents of hidden groups const notMatchesRanges: IRange[] = []; if (filteredGroups) { @@ -612,8 +611,7 @@ export class HighlightMatchesRenderer extends Disposable { } render(matches: IRange[]): void { - const model = this.editor.getModel(); - this.decorationIds = this.editor.deltaDecorations(this.decorationIds, matches.map(match => this.createDecoration(match, model))); + this.decorationIds = this.editor.deltaDecorations(this.decorationIds, matches.map(match => this.createDecoration(match))); } private static readonly _FIND_MATCH = ModelDecorationOptions.register({ @@ -621,7 +619,7 @@ export class HighlightMatchesRenderer extends Disposable { className: 'findMatch' }); - private createDecoration(range: IRange, model: ITextModel): IModelDeltaDecoration { + private createDecoration(range: IRange): IModelDeltaDecoration { return { range, options: HighlightMatchesRenderer._FIND_MATCH @@ -676,7 +674,7 @@ class EditSettingRenderer extends Disposable { this.settingsGroups = settingsGroups; this.associatedPreferencesModel = associatedPreferencesModel; - const settings = this.getSettings(this.editor.getPosition().lineNumber); + const settings = this.getSettings(this.editor.getPosition()!.lineNumber); if (settings.length) { this.showEditPreferencesWidget(this.editPreferenceWidgetForCursorPosition, settings); } @@ -713,9 +711,9 @@ class EditSettingRenderer extends Disposable { this.toggleEditPreferencesForMouseMoveDelayer.trigger(() => this.toggleEditPreferenceWidgetForMouseMove(mouseMoveEvent)); } - private getEditPreferenceWidgetUnderMouse(mouseMoveEvent: IEditorMouseEvent): EditPreferenceWidget { + private getEditPreferenceWidgetUnderMouse(mouseMoveEvent: IEditorMouseEvent): EditPreferenceWidget | undefined { if (mouseMoveEvent.target.type === MouseTargetType.GUTTER_GLYPH_MARGIN) { - const line = mouseMoveEvent.target.position.lineNumber; + const line = mouseMoveEvent.target.position!.lineNumber; if (this.editPreferenceWidgetForMouseMove.getLine() === line && this.editPreferenceWidgetForMouseMove.isVisible()) { return this.editPreferenceWidgetForMouseMove; } @@ -723,7 +721,7 @@ class EditSettingRenderer extends Disposable { return this.editPreferenceWidgetForCursorPosition; } } - return null; + return undefined; } private toggleEditPreferenceWidgetForMouseMove(mouseMoveEvent: IEditorMouseEvent): void { @@ -797,9 +795,9 @@ class EditSettingRenderer extends Disposable { break; } if (lineNumber >= setting.range.startLineNumber && lineNumber <= setting.range.endLineNumber) { - if (!this.isDefaultSettings() && setting.overrides.length) { + if (!this.isDefaultSettings() && setting.overrides!.length) { // Only one level because override settings cannot have override settings - for (const overrideSetting of setting.overrides) { + for (const overrideSetting of setting.overrides!) { if (lineNumber >= overrideSetting.range.startLineNumber && lineNumber <= overrideSetting.range.endLineNumber) { settings.push({ ...overrideSetting, index, groupId: group.id }); } @@ -850,9 +848,9 @@ class EditSettingRenderer extends Disposable { private toAbsoluteCoords(position: Position): { x: number, y: number } { const positionCoords = this.editor.getScrolledVisiblePosition(position); - const editorCoords = getDomNodePagePosition(this.editor.getDomNode()); - const x = editorCoords.left + positionCoords.left; - const y = editorCoords.top + positionCoords.top + positionCoords.height; + const editorCoords = getDomNodePagePosition(this.editor.getDomNode()!); + const x = editorCoords.left + positionCoords!.left; + const y = editorCoords.top + positionCoords!.top + positionCoords!.height; return { x, y: y + 10 }; } @@ -930,7 +928,7 @@ class SettingHighlighter extends Disposable { const highlighter = fix ? this.fixedHighlighter : this.volatileHighlighter; highlighter.highlightRange({ range: setting.valueRange, - resource: this.editor.getModel().uri + resource: this.editor.getModel()!.uri }, this.editor); this.editor.revealLineInCenterIfOutsideViewport(setting.valueRange.startLineNumber, editorCommon.ScrollType.Smooth); @@ -956,7 +954,7 @@ class WorkspaceConfigurationRenderer extends Disposable { @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService ) { super(); - this._register(this.editor.getModel().onDidChangeContent(() => this.renderingDelayer.trigger(() => this.render(this.associatedSettingsEditorModel)))); + this._register(this.editor.getModel()!.onDidChangeContent(() => this.renderingDelayer.trigger(() => this.render(this.associatedSettingsEditorModel)))); } render(associatedSettingsEditorModel: IPreferencesEditorModel): void { @@ -978,7 +976,7 @@ class WorkspaceConfigurationRenderer extends Disposable { } } } - this.decorationIds = this.editor.deltaDecorations(this.decorationIds, ranges.map(range => this.createDecoration(range, this.editor.getModel()))); + this.decorationIds = this.editor.deltaDecorations(this.decorationIds, ranges.map(range => this.createDecoration(range))); } } @@ -987,7 +985,7 @@ class WorkspaceConfigurationRenderer extends Disposable { inlineClassName: 'dim-configuration' }); - private createDecoration(range: IRange, model: ITextModel): IModelDeltaDecoration { + private createDecoration(range: IRange): IModelDeltaDecoration { return { range, options: WorkspaceConfigurationRenderer._DIM_CONFIGURATION_ 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 5e3c4a3ff08..8ba4cd2094d 100644 --- a/src/vs/workbench/contrib/preferences/electron-browser/preferences.contribution.ts +++ b/src/vs/workbench/contrib/preferences/electron-browser/preferences.contribution.ts @@ -424,7 +424,7 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon dark: URI.parse(require.toUrl(`vs/workbench/contrib/preferences/electron-browser/media/preferences-editor-inverse.svg`)) } }, - when: ContextKeyExpr.and(ResourceContextKey.Resource.isEqualTo(this.preferencesService.workspaceSettingsResource.toString()), WorkbenchStateContext.isEqualTo('workspace')), + when: ContextKeyExpr.and(ResourceContextKey.Resource.isEqualTo(this.preferencesService.workspaceSettingsResource!.toString()), WorkbenchStateContext.isEqualTo('workspace')), group: 'navigation', order: 1 }); @@ -452,7 +452,7 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon dark: URI.parse(require.toUrl(`vs/workbench/contrib/preferences/electron-browser/media/preferences-editor-inverse.svg`)) } }, - when: ContextKeyExpr.and(ResourceContextKey.Resource.isEqualTo(this.preferencesService.getFolderSettingsResource(folder.uri).toString())), + when: ContextKeyExpr.and(ResourceContextKey.Resource.isEqualTo(this.preferencesService.getFolderSettingsResource(folder.uri)!.toString())), group: 'navigation', order: 1 }); diff --git a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts index f8a35ebfc2d..db9568a6eb4 100644 --- a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts +++ b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts @@ -77,9 +77,8 @@ Registry.as(ConfigurationExtensions.Configuration).regis }, 'scm.providers.visible': { type: 'number', - description: localize('providersVisible', "Controls how many providers are visible in the Source Control Provider section."), - default: 10, - minimum: 1 + description: localize('providersVisible', "Controls how many providers are visible in the Source Control Provider section. Set to `0` to be able to manually resize the view."), + default: 10 }, 'scm.diffDecorations': { type: 'string', diff --git a/src/vs/workbench/contrib/scm/browser/scmViewlet.ts b/src/vs/workbench/contrib/scm/browser/scmViewlet.ts index cdaf5b2b48f..40234b4505c 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewlet.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewlet.ts @@ -236,7 +236,10 @@ export class MainPanel extends ViewletPanel { const renderer = this.instantiationService.createInstance(ProviderRenderer); const identityProvider = { getId: r => r.provider.id }; - this.list = this.instantiationService.createInstance(WorkbenchList, container, delegate, [renderer], { identityProvider }) as WorkbenchList; + this.list = this.instantiationService.createInstance(WorkbenchList, container, delegate, [renderer], { + identityProvider, + horizontalScrolling: false + }) as WorkbenchList; renderer.onDidRenderElement(e => this.list.updateWidth(this.viewModel.repositories.indexOf(e)), null, this.disposables); this.list.onSelectionChange(this.onListSelectionChange, this, this.disposables); @@ -273,12 +276,12 @@ export class MainPanel extends ViewletPanel { } private updateBodySize(): void { - const visibleCount = Math.max(this.configurationService.getValue('scm.providers.visible'), 1); + const visibleCount = this.configurationService.getValue('scm.providers.visible'); const empty = this.list.length === 0; const size = Math.min(this.viewModel.repositories.length, visibleCount) * 22; - this.minimumBodySize = size; - this.maximumBodySize = empty ? Number.POSITIVE_INFINITY : size; + this.minimumBodySize = visibleCount === 0 ? 22 : size; + this.maximumBodySize = visibleCount === 0 ? Number.POSITIVE_INFINITY : empty ? Number.POSITIVE_INFINITY : size; } private onListContextMenu(e: IListContextMenuEvent): void { @@ -314,7 +317,9 @@ export class MainPanel extends ViewletPanel { private onListSelectionChange(e: IListEvent): void { if (e.elements.length > 0 && e.browserEvent) { + const scrollTop = this.list.scrollTop; this.viewModel.setVisibleRepositories(e.elements); + this.list.scrollTop = scrollTop; } } @@ -834,7 +839,8 @@ export class RepositoryPanel extends ViewletPanel { this.list = this.instantiationService.createInstance(WorkbenchList, this.listContainer, delegate, renderers, { identityProvider: scmResourceIdentityProvider, - keyboardNavigationLabelProvider: scmKeyboardNavigationLabelProvider + keyboardNavigationLabelProvider: scmKeyboardNavigationLabelProvider, + horizontalScrolling: false }) as WorkbenchList; Event.chain(this.list.onDidOpen) @@ -1059,13 +1065,24 @@ export class SCMViewlet extends ViewContainerViewlet implements IViewModel { const toSetInvisible = visibleViewDescriptors .filter(d => d instanceof RepositoryViewDescriptor && repositories.indexOf(d.repository) === -1); - for (const viewDescriptor of toSetVisible) { - this.viewsModel.setVisible(viewDescriptor.id, true); - } + let size: number | undefined; + const oneToOne = toSetVisible.length === 1 && toSetInvisible.length === 1; for (const viewDescriptor of toSetInvisible) { + if (oneToOne) { + const panel = this.panels.filter(panel => panel.id === viewDescriptor.id)[0]; + + if (panel) { + size = this.getPanelSize(panel); + } + } + this.viewsModel.setVisible(viewDescriptor.id, false); } + + for (const viewDescriptor of toSetVisible) { + this.viewsModel.setVisible(viewDescriptor.id, true, size); + } } constructor( diff --git a/src/vs/workbench/contrib/search/browser/search.contribution.ts b/src/vs/workbench/contrib/search/browser/search.contribution.ts index 423aedf576a..0cb679b5e84 100644 --- a/src/vs/workbench/contrib/search/browser/search.contribution.ts +++ b/src/vs/workbench/contrib/search/browser/search.contribution.ts @@ -364,9 +364,10 @@ const searchInFolderCommand: ICommandHandler = (accessor, resource?: URI) => { const viewletService = accessor.get(IViewletService); const panelService = accessor.get(IPanelService); const fileService = accessor.get(IFileService); + const configurationService = accessor.get(IConfigurationService); const resources = getMultiSelectedResources(resource, listService, accessor.get(IEditorService)); - return openSearchView(viewletService, panelService, true).then(searchView => { + return openSearchView(viewletService, panelService, configurationService, true).then(searchView => { if (resources && resources.length) { return fileService.resolveFiles(resources.map(resource => ({ resource }))).then(results => { const folders: URI[] = []; @@ -412,7 +413,7 @@ const FIND_IN_WORKSPACE_ID = 'filesExplorer.findInWorkspace'; CommandsRegistry.registerCommand({ id: FIND_IN_WORKSPACE_ID, handler: (accessor) => { - return openSearchView(accessor.get(IViewletService), accessor.get(IPanelService), true).then(searchView => { + return openSearchView(accessor.get(IViewletService), accessor.get(IPanelService), accessor.get(IConfigurationService), true).then(searchView => { searchView.searchInFolders(null); }); } diff --git a/src/vs/workbench/contrib/search/browser/searchActions.ts b/src/vs/workbench/contrib/search/browser/searchActions.ts index 8af67c0f2d1..553335b52e7 100644 --- a/src/vs/workbench/contrib/search/browser/searchActions.ts +++ b/src/vs/workbench/contrib/search/browser/searchActions.ts @@ -52,12 +52,11 @@ export function appendKeyBindingLabel(label: string, inputKeyBinding: number | R } } -export function openSearchView(viewletService: IViewletService, panelService: IPanelService, focus?: boolean): Promise { - if (viewletService.getViewlets().filter(v => v.id === VIEWLET_ID).length) { - return viewletService.openViewlet(VIEWLET_ID, focus).then(viewlet => (viewlet as SearchViewlet).getSearchView()); +export function openSearchView(viewletService: IViewletService, panelService: IPanelService, configurationService: IConfigurationService, focus?: boolean): Promise { + if (configurationService.getValue().search.location === 'panel') { + return Promise.resolve((panelService.openPanel(PANEL_ID, focus) as SearchPanel).getSearchView()); } - - return Promise.resolve((panelService.openPanel(PANEL_ID, focus) as SearchPanel).getSearchView()); + return viewletService.openViewlet(VIEWLET_ID, focus).then(viewlet => (viewlet as SearchViewlet).getSearchView()); } export function getSearchView(viewletService: IViewletService, panelService: IPanelService): SearchView | null { @@ -142,14 +141,14 @@ export class FocusPreviousInputAction extends Action { export abstract class FindOrReplaceInFilesAction extends Action { - constructor(id: string, label: string, protected viewletService: IViewletService, protected panelService: IPanelService, + constructor(id: string, label: string, protected viewletService: IViewletService, protected panelService: IPanelService, protected configurationService: IConfigurationService, private expandSearchReplaceWidget: boolean ) { super(id, label); } run(): Promise { - return openSearchView(this.viewletService, this.panelService, false).then(openedView => { + return openSearchView(this.viewletService, this.panelService, this.configurationService, false).then(openedView => { const searchAndReplaceWidget = openedView.searchAndReplaceWidget; searchAndReplaceWidget.toggleReplace(this.expandSearchReplaceWidget); @@ -165,9 +164,10 @@ export class FindInFilesAction extends FindOrReplaceInFilesAction { constructor(id: string, label: string, @IViewletService viewletService: IViewletService, - @IPanelService panelService: IPanelService + @IPanelService panelService: IPanelService, + @IConfigurationService configurationService: IConfigurationService ) { - super(id, label, viewletService, panelService, /*expandSearchReplaceWidget=*/false); + super(id, label, viewletService, panelService, configurationService, /*expandSearchReplaceWidget=*/false); } } @@ -178,9 +178,10 @@ export class OpenSearchViewletAction extends FindOrReplaceInFilesAction { constructor(id: string, label: string, @IViewletService viewletService: IViewletService, @IPanelService panelService: IPanelService, - @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService + @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService, + @IConfigurationService configurationService: IConfigurationService ) { - super(id, label, viewletService, panelService, /*expandSearchReplaceWidget=*/false); + super(id, label, viewletService, panelService, configurationService, /*expandSearchReplaceWidget=*/false); } run(): Promise { @@ -208,9 +209,10 @@ export class ReplaceInFilesAction extends FindOrReplaceInFilesAction { constructor(id: string, label: string, @IViewletService viewletService: IViewletService, - @IPanelService panelService: IPanelService + @IPanelService panelService: IPanelService, + @IConfigurationService configurationService: IConfigurationService ) { - super(id, label, viewletService, panelService, /*expandSearchReplaceWidget=*/true); + super(id, label, viewletService, panelService, configurationService, /*expandSearchReplaceWidget=*/true); } } @@ -384,13 +386,14 @@ export class FocusNextSearchResultAction extends Action { constructor(id: string, label: string, @IViewletService private readonly viewletService: IViewletService, - @IPanelService private readonly panelService: IPanelService + @IPanelService private readonly panelService: IPanelService, + @IConfigurationService private readonly configurationService: IConfigurationService ) { super(id, label); } run(): Promise { - return openSearchView(this.viewletService, this.panelService).then(searchView => { + return openSearchView(this.viewletService, this.panelService, this.configurationService).then(searchView => { searchView.selectNextMatch(); }); } @@ -402,13 +405,14 @@ export class FocusPreviousSearchResultAction extends Action { constructor(id: string, label: string, @IViewletService private readonly viewletService: IViewletService, - @IPanelService private readonly panelService: IPanelService + @IPanelService private readonly panelService: IPanelService, + @IConfigurationService private readonly configurationService: IConfigurationService ) { super(id, label); } run(): Promise { - return openSearchView(this.viewletService, this.panelService).then(searchView => { + return openSearchView(this.viewletService, this.panelService, this.configurationService).then(searchView => { searchView.selectPreviousMatch(); }); } @@ -765,7 +769,8 @@ export const clearHistoryCommand: ICommandHandler = accessor => { export const focusSearchListCommand: ICommandHandler = accessor => { const viewletService = accessor.get(IViewletService); const panelService = accessor.get(IPanelService); - openSearchView(viewletService, panelService).then(searchView => { + const configurationService = accessor.get(IConfigurationService); + openSearchView(viewletService, panelService, configurationService).then(searchView => { searchView.moveFocusToResults(); }); }; diff --git a/src/vs/workbench/contrib/snippets/browser/snippetsFile.ts b/src/vs/workbench/contrib/snippets/browser/snippetsFile.ts index 7ae06dbb3ef..dc3d82ca9fd 100644 --- a/src/vs/workbench/contrib/snippets/browser/snippetsFile.ts +++ b/src/vs/workbench/contrib/snippets/browser/snippetsFile.ts @@ -5,7 +5,6 @@ import { parse as jsonParse } from 'vs/base/common/json'; import { forEach } from 'vs/base/common/collections'; -import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import { localize } from 'vs/nls'; import { extname, basename } from 'vs/base/common/path'; import { SnippetParser, Variable, Placeholder, Text } from 'vs/editor/contrib/snippet/snippetParser'; @@ -13,6 +12,7 @@ import { KnownSnippetVariableNames } from 'vs/editor/contrib/snippet/snippetVari import { isFalsyOrWhitespace } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; import { IFileService } from 'vs/platform/files/common/files'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; export class Snippet { diff --git a/src/vs/workbench/contrib/stats/node/workspaceStats.ts b/src/vs/workbench/contrib/stats/node/workspaceStats.ts index ea4cc981bee..91d99952833 100644 --- a/src/vs/workbench/contrib/stats/node/workspaceStats.ts +++ b/src/vs/workbench/contrib/stats/node/workspaceStats.ts @@ -20,7 +20,6 @@ import { hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspa import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { joinPath } from 'vs/base/common/resources'; -import { collectWorkspaceStats, WorkspaceStats as WorkspaceStatsType } from 'vs/base/node/stats'; const SshProtocolMatcher = /^([^@:]+@)?([^:]+):/; const SshUrlMatcher = /^([^@:]+@)?([^:]+):(.+)$/; @@ -226,15 +225,10 @@ export class WorkspaceStats implements IWorkbenchContribution { private report(): void { - // Workspace Tags + // Workspace Stats this.resolveWorkspaceTags(this.windowService.getConfiguration(), rootFiles => this.handleWorkspaceFiles(rootFiles)) .then(tags => this.reportWorkspaceTags(tags), error => onUnexpectedError(error)); - // Workspace file types, config files, and launch configs - this.getWorkspaceMetadata().then(stats => { - this.reportWorkspaceMetadata(stats); - }); - // Cloud Stats this.reportCloudStats(); @@ -741,39 +735,4 @@ export class WorkspaceStats implements IWorkbenchContribution { this.telemetryService.publicLog('resolveProxy.stats', { type }); }).then(undefined, onUnexpectedError); } - - /* __GDPR__ - "workspace.metadata" : { - "fileTypes" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "configTypes" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "launchConfigs" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } - } - */ - private reportWorkspaceMetadata(stats: WorkspaceStatsType[]): void { - for (let stat of stats) { // one event for each root folder in the workspace - this.telemetryService.publicLog('workspace.metadata', { - 'fileTypes': stat.fileTypes, - 'configTypes': stat.configFiles, - 'launchConfigs': stat.launchConfigFiles - }); - } - } - - private getWorkspaceMetadata(): Promise { - const workspaceStatPromises: Promise[] = []; - const workspace = this.contextService.getWorkspace(); - workspace.folders.forEach(folder => { - const folderUri = URI.revive(folder.uri); - if (folderUri.scheme === 'file') { - const folder = folderUri.fsPath; - workspaceStatPromises.push(collectWorkspaceStats(folder, ['node_modules', '.git']).then(async stats => { - return stats; - })); - } - }); - - return Promise.all(workspaceStatPromises).then((stats) => { - return stats; - }); - } } diff --git a/src/vs/workbench/contrib/tasks/common/tasks.ts b/src/vs/workbench/contrib/tasks/common/tasks.ts index c3b29e5449b..6a6601cb17a 100644 --- a/src/vs/workbench/contrib/tasks/common/tasks.ts +++ b/src/vs/workbench/contrib/tasks/common/tasks.ts @@ -8,10 +8,10 @@ import { IJSONSchemaMap } from 'vs/base/common/jsonSchema'; import * as Objects from 'vs/base/common/objects'; import { UriComponents } from 'vs/base/common/uri'; -import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import { ProblemMatcher } from 'vs/workbench/contrib/tasks/common/problemMatcher'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; export const TASK_RUNNING_STATE = new RawContextKey('taskRunning', false); @@ -597,27 +597,28 @@ export class CustomTask extends CommonTask { return this._source.customizes; } else { let type: string; - if (this.command !== undefined) { - switch (this.command.runtime) { - case RuntimeType.Shell: - type = 'shell'; - break; + const commandRuntime = this.command ? this.command.runtime : undefined; + switch (commandRuntime) { + case RuntimeType.Shell: + type = 'shell'; + break; - case RuntimeType.Process: - type = 'process'; - break; + case RuntimeType.Process: + type = 'process'; + break; - case RuntimeType.CustomExecution: - type = 'customExecution'; - break; + case RuntimeType.CustomExecution: + type = 'customExecution'; + break; - default: - throw new Error('Unexpected task runtime'); - break; - } - } else { - type = '$composite'; + case undefined: + type = '$composite'; + break; + + default: + throw new Error('Unexpected task runtime'); } + let result: KeyedTaskIdentifier = { type, _key: this._id, diff --git a/src/vs/workbench/contrib/tasks/electron-browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/electron-browser/terminalTaskSystem.ts index f18977cb6e6..3302279df3a 100644 --- a/src/vs/workbench/contrib/tasks/electron-browser/terminalTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/electron-browser/terminalTaskSystem.ts @@ -925,7 +925,7 @@ export class TerminalTaskSystem implements ITaskSystem { // (or, if the task has no group, a terminal used by a task without group). for (const taskId of this.idleTaskTerminals.keys()) { const idleTerminalId = this.idleTaskTerminals.get(taskId)!; - if (idleTerminalId && this.terminals[idleTerminalId].group === group) { + if (idleTerminalId && this.terminals[idleTerminalId] && this.terminals[idleTerminalId].group === group) { terminalId = this.idleTaskTerminals.remove(taskId); break; } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 78b84ba1935..6b744c7b073 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -430,7 +430,7 @@ export class TerminalInstance implements ITerminalInstance { this._processManager.onProcessData(data => this._onProcessData(data)); this._xterm.on('data', data => this._processManager!.write(data)); // TODO: How does the cwd work on detached processes? - this._linkHandler = this._instantiationService.createInstance(TerminalLinkHandler, this._xterm, platform.platform); + this._linkHandler = this._instantiationService.createInstance(TerminalLinkHandler, this._xterm, platform.platform, this._processManager); this.processReady.then(async () => { this._linkHandler.processCwd = await this._processManager!.getInitialCwd(); }); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalLinkHandler.ts b/src/vs/workbench/contrib/terminal/browser/terminalLinkHandler.ts index 0217380a328..933e4eaf526 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalLinkHandler.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalLinkHandler.ts @@ -4,18 +4,19 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import * as path from 'vs/base/common/path'; import * as platform from 'vs/base/common/platform'; 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'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ITerminalService } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalService, ITerminalProcessManager } 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'; +import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; +import { posix, win32 } from 'vs/base/common/path'; const pathPrefix = '(\\.\\.?|\\~)'; const pathSeparatorClause = '\\/'; @@ -57,12 +58,16 @@ const LOCAL_LINK_PRIORITY = -2; export type XtermLinkMatcherHandler = (event: MouseEvent, uri: string) => boolean | void; export type XtermLinkMatcherValidationCallback = (uri: string, callback: (isValid: boolean) => void) => void; +interface IPath { + join(...paths: string[]): string; + normalize(path: string): string; +} + export class TerminalLinkHandler { private _hoverDisposables: IDisposable[] = []; private _mouseMoveDisposable: IDisposable; private _widgetManager: TerminalWidgetManager; private _processCwd: string; - private _localLinkPattern: RegExp; private _gitDiffPreImagePattern: RegExp; private _gitDiffPostImagePattern: RegExp; private readonly _tooltipCallback: (event: MouseEvent, uri: string) => boolean | void; @@ -70,15 +75,13 @@ export class TerminalLinkHandler { constructor( private _xterm: any, private _platform: platform.Platform, + private readonly _processManager: ITerminalProcessManager, @IOpenerService private readonly _openerService: IOpenerService, @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 - this._localLinkPattern = new RegExp(`${baseLocalLinkClause}(${lineAndColumnClause})`); // Matches '--- a/src/file1', capturing 'src/file1' in group 1 this._gitDiffPreImagePattern = /^--- a\/(\S*)/; // Matches '+++ b/src/file1', capturing 'src/file1' in group 1 @@ -183,7 +186,9 @@ export class TerminalLinkHandler { } protected get _localLinkRegex(): RegExp { - return this._localLinkPattern; + const baseLocalLinkClause = this._processManager.os === platform.OperatingSystem.Windows ? winLocalLinkClause : unixLocalLinkClause; + // Append line and column number regex + return new RegExp(`${baseLocalLinkClause}(${lineAndColumnClause})`); } protected get _gitDiffPreImageRegex(): RegExp { @@ -199,19 +204,12 @@ export class TerminalLinkHandler { if (!resolvedLink) { return Promise.resolve(null); } - const normalizedPath = path.normalize(path.resolve(resolvedLink)); - const normalizedUrl = this.extractLinkUrl(normalizedPath); - if (!normalizedUrl) { - return Promise.resolve(null); - } - const resource = URI.file(normalizedUrl); const lineColumnInfo: LineColumnInfo = this.extractLineColumnInfo(link); const selection: ITextEditorSelection = { startLineNumber: lineColumnInfo.lineNumber, startColumn: lineColumnInfo.columnNumber }; - - return this._editorService.openEditor({ resource, options: { pinned: true, selection } }); + return this._editorService.openEditor({ resource: resolvedLink, options: { pinned: true, selection } }); }); } @@ -247,37 +245,44 @@ export class TerminalLinkHandler { return nls.localize('terminalLinkHandler.followLinkCtrl', 'Ctrl + click to follow link'); } - protected _preprocessPath(link: string): string | null { - if (this._platform === platform.Platform.Windows) { - // Resolve ~ -> %HOMEDRIVE%\%HOMEPATH% - if (link.charAt(0) === '~') { - if (!process.env.HOMEDRIVE || !process.env.HOMEPATH) { - return null; - } - link = `${process.env.HOMEDRIVE}\\${process.env.HOMEPATH + link.substring(1)}`; - } + private get osPath(): IPath { + if (this._processManager.os === platform.OperatingSystem.Windows) { + return win32; + } + return posix; + } - // Resolve relative paths (.\a, ..\a, ~\a, a\b) - if (!link.match('^' + winDrivePrefix)) { + protected _preprocessPath(link: string): string | null { + if (link.charAt(0) === '~') { + // Resolve ~ -> userHome + if (!this._processManager.userHome) { + return null; + } + link = this.osPath.join(this._processManager.userHome, link.substring(1)); + } else if (link.charAt(0) !== '/' && link.charAt(0) !== '~') { + // Resolve workspace path . | .. | -> /. | /.. | / + if (this._processManager.os === platform.OperatingSystem.Windows) { + if (!link.match('^' + winDrivePrefix)) { + if (!this._processCwd) { + // Abort if no workspace is open + return null; + } + link = this.osPath.join(this._processCwd, link); + } + } else { if (!this._processCwd) { // Abort if no workspace is open return null; } - link = path.join(this._processCwd, link); + link = this.osPath.join(this._processCwd, link); } } - // Resolve workspace path . | .. | -> /. | /.. | / - else if (link.charAt(0) !== '/' && link.charAt(0) !== '~') { - if (!this._processCwd) { - // Abort if no workspace is open - return null; - } - link = path.join(this._processCwd, link); - } + link = this.osPath.normalize(link); + return link; } - private _resolvePath(link: string): PromiseLike { + private _resolvePath(link: string): PromiseLike { const preprocessedLink = this._preprocessPath(link); if (!preprocessedLink) { return Promise.resolve(null); @@ -288,13 +293,31 @@ export class TerminalLinkHandler { return Promise.resolve(null); } - // Ensure the file exists on disk, so an editor can be opened after clicking it - return this._fileService.resolveFile(URI.file(linkUrl)).then(stat => { - if (stat.isDirectory) { - return null; + try { + let uri: URI; + if (this._processManager.remoteAuthority) { + uri = URI.from({ + scheme: REMOTE_HOST_SCHEME, + authority: this._processManager.remoteAuthority, + path: linkUrl + }); + } else { + uri = URI.file(linkUrl); } - return preprocessedLink; - }); + + return this._fileService.resolveFile(uri).then(stat => { + if (stat.isDirectory) { + return null; + } + return uri; + }).catch(() => { + // Does not exist + return null; + }); + } catch { + // Errors in parsing the path + return Promise.resolve(null); + } } /** diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts index 432d886e99f..a721e961983 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts @@ -22,6 +22,7 @@ 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'; +import { IRemoteEnvironmentService } from 'vs/workbench/services/remote/common/remoteEnvironmentService'; /** The amount of time to consider terminal errors to be related to the launch */ const LAUNCHING_DURATION = 500; @@ -38,6 +39,9 @@ export class TerminalProcessManager implements ITerminalProcessManager { public processState: ProcessState = ProcessState.UNINITIALIZED; public ptyProcessReady: Promise; public shellProcessId: number; + public remoteAuthority: string | undefined; + public os: platform.OperatingSystem | undefined; + public userHome: string | undefined; private _process: ITerminalChildProcess | null = null; private _preLaunchInputQueue: string[] = []; @@ -64,7 +68,8 @@ export class TerminalProcessManager implements ITerminalProcessManager { @IConfigurationService private readonly _workspaceConfigurationService: IConfigurationService, @IEnvironmentService private readonly _environmentService: IEnvironmentService, @IProductService private readonly _productService: IProductService, - @ITerminalInstanceService private readonly _terminalInstanceService: ITerminalInstanceService + @ITerminalInstanceService private readonly _terminalInstanceService: ITerminalInstanceService, + @IRemoteEnvironmentService private readonly _remoteEnvironmentService: IRemoteEnvironmentService ) { this.ptyProcessReady = new Promise(c => { this.onProcessReady(() => { @@ -96,17 +101,29 @@ export class TerminalProcessManager implements ITerminalProcessManager { cols: number, rows: number ): void { - let launchRemotely = false; const forceExtHostProcess = (this._configHelper.config as any).extHostProcess; - if (shellLaunchConfig.cwd && typeof shellLaunchConfig.cwd === 'object') { - launchRemotely = !!getRemoteAuthority(shellLaunchConfig.cwd); + this.remoteAuthority = getRemoteAuthority(shellLaunchConfig.cwd); } else { - launchRemotely = !!this._windowService.getConfiguration().remoteAuthority; + this.remoteAuthority = this._windowService.getConfiguration().remoteAuthority; } + const hasRemoteAuthority = !!this.remoteAuthority; + let launchRemotely = hasRemoteAuthority || forceExtHostProcess; - if (launchRemotely || forceExtHostProcess) { - const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(forceExtHostProcess ? undefined : REMOTE_HOST_SCHEME); + this.userHome = this._environmentService.userHome; + this.os = platform.OS; + if (launchRemotely) { + if (hasRemoteAuthority) { + this._remoteEnvironmentService.remoteEnvironment.then(env => { + if (!env) { + return; + } + this.userHome = env.userHome.path; + this.os = env.os; + }); + } + + const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(hasRemoteAuthority ? REMOTE_HOST_SCHEME : undefined); this._process = this._instantiationService.createInstance(TerminalProcessExtHostProxy, this._terminalId, shellLaunchConfig, activeWorkspaceRootUri, cols, rows); } else { if (!shellLaunchConfig.executable) { diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index b718dba571e..c66e1da4003 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -637,6 +637,9 @@ export interface ITerminalProcessManager extends IDisposable { readonly processState: ProcessState; readonly ptyProcessReady: Promise; readonly shellProcessId: number; + readonly remoteAuthority: string | undefined; + readonly os: platform.OperatingSystem | undefined; + readonly userHome: string | undefined; readonly onProcessReady: Event; readonly onProcessData: Event; diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts index d9d128e5f92..47acc177be8 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts @@ -24,7 +24,7 @@ export class TerminalInstanceService implements ITerminalInstanceService { public _serviceBrand: any; constructor( - @IInstantiationService private readonly _instantiationService: IInstantiationService, + @IInstantiationService private readonly _instantiationService: IInstantiationService ) { } 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 5b708d68123..bfb91e02998 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 @@ -4,11 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { Platform } from 'vs/base/common/platform'; +import { Platform, OperatingSystem } from 'vs/base/common/platform'; 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'; class TestTerminalLinkHandler extends TerminalLinkHandler { public get localLinkRegex(): RegExp { @@ -39,7 +37,10 @@ interface LinkFormatInfo { suite('Workbench - TerminalLinkHandler', () => { suite('localLinkRegex', () => { test('Windows', () => { - const terminalLinkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Windows, null!, null!, null!, null!, null!); + const terminalLinkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Windows, { + os: OperatingSystem.Windows, + userHome: '' + } as any, 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 +112,10 @@ suite('Workbench - TerminalLinkHandler', () => { }); test('Linux', () => { - const terminalLinkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Linux, null!, null!, null!, null!, null!); + const terminalLinkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Linux, { + os: OperatingSystem.Linux, + userHome: '' + } as any, 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,58 +179,64 @@ suite('Workbench - TerminalLinkHandler', () => { suite('preprocessPath', () => { test('Windows', () => { - const linkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Windows, null!, null!, null!, null!, null!); + const linkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Windows, { + os: OperatingSystem.Windows, + userHome: 'C:\\Users\\Me' + } as any, null!, null!, null!, null!, null!); linkHandler.processCwd = 'C:\\base'; - let stub = sinon.stub(path, 'join', function (arg1: string, arg2: string) { - return arg1 + '\\' + arg2; - }); - assert.equal(linkHandler.preprocessPath('./src/file1'), 'C:\\base\\./src/file1'); + assert.equal(linkHandler.preprocessPath('./src/file1'), 'C:\\base\\src\\file1'); assert.equal(linkHandler.preprocessPath('src\\file2'), 'C:\\base\\src\\file2'); - assert.equal(linkHandler.preprocessPath('C:\\absolute\\path\\file3'), 'C:\\absolute\\path\\file3'); - - stub.restore(); + assert.equal(linkHandler.preprocessPath('~/src/file3'), 'C:\\Users\\Me\\src\\file3'); + assert.equal(linkHandler.preprocessPath('~\\src\\file4'), 'C:\\Users\\Me\\src\\file4'); + assert.equal(linkHandler.preprocessPath('C:\\absolute\\path\\file5'), 'C:\\absolute\\path\\file5'); }); test('Windows - spaces', () => { - const linkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Windows, null!, null!, null!, null!, null!); + const linkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Windows, { + os: OperatingSystem.Windows, + userHome: 'C:\\Users\\M e' + } as any, null!, null!, null!, null!, null!); linkHandler.processCwd = 'C:\\base dir'; - let stub = sinon.stub(path, 'join', function (arg1: string, arg2: string) { - return arg1 + '\\' + arg2; - }); - assert.equal(linkHandler.preprocessPath('./src/file1'), 'C:\\base dir\\./src/file1'); + assert.equal(linkHandler.preprocessPath('./src/file1'), 'C:\\base dir\\src\\file1'); assert.equal(linkHandler.preprocessPath('src\\file2'), 'C:\\base dir\\src\\file2'); - assert.equal(linkHandler.preprocessPath('C:\\absolute\\path\\file3'), 'C:\\absolute\\path\\file3'); - - stub.restore(); + assert.equal(linkHandler.preprocessPath('~/src/file3'), 'C:\\Users\\M e\\src\\file3'); + assert.equal(linkHandler.preprocessPath('~\\src\\file4'), 'C:\\Users\\M e\\src\\file4'); + assert.equal(linkHandler.preprocessPath('C:\\abso lute\\path\\file5'), 'C:\\abso lute\\path\\file5'); }); test('Linux', () => { - const linkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Linux, null!, null!, null!, null!, null!); + const linkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Linux, { + os: OperatingSystem.Linux, + userHome: '/home/me' + } as any, null!, null!, null!, null!, null!); linkHandler.processCwd = '/base'; - let stub = sinon.stub(path, 'join', function (arg1: string, arg2: string) { - return arg1 + '/' + arg2; - }); - - assert.equal(linkHandler.preprocessPath('./src/file1'), '/base/./src/file1'); + assert.equal(linkHandler.preprocessPath('./src/file1'), '/base/src/file1'); assert.equal(linkHandler.preprocessPath('src/file2'), '/base/src/file2'); - assert.equal(linkHandler.preprocessPath('/absolute/path/file3'), '/absolute/path/file3'); - stub.restore(); + assert.equal(linkHandler.preprocessPath('~/src/file3'), '/home/me/src/file3'); + assert.equal(linkHandler.preprocessPath('/absolute/path/file4'), '/absolute/path/file4'); }); test('No Workspace', () => { - const linkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Linux, null!, null!, null!, null!, null!); + const linkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Linux, { + os: OperatingSystem.Linux, + userHome: '/home/me' + } as any, null!, null!, null!, null!, null!); assert.equal(linkHandler.preprocessPath('./src/file1'), null); assert.equal(linkHandler.preprocessPath('src/file2'), null); - assert.equal(linkHandler.preprocessPath('/absolute/path/file3'), '/absolute/path/file3'); + assert.equal(linkHandler.preprocessPath('~/src/file3'), '/home/me/src/file3'); + assert.equal(linkHandler.preprocessPath('/absolute/path/file4'), '/absolute/path/file4'); }); }); 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!, null!); + const linkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Linux, { + os: OperatingSystem.Linux, + userHome: '' + } as any, null!, null!, null!, null!, null!); function assertAreGoodMatches(matches: RegExpMatchArray | null) { if (matches) { diff --git a/src/vs/workbench/contrib/webview/electron-browser/webview-pre.js b/src/vs/workbench/contrib/webview/electron-browser/webview-pre.js index a239f09f03e..74fc798bc9b 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webview-pre.js +++ b/src/vs/workbench/contrib/webview/electron-browser/webview-pre.js @@ -64,9 +64,10 @@ }; /** + * @param {HTMLDocument} document * @param {HTMLElement} body */ - const applyStyles = (body) => { + const applyStyles = (document, body) => { if (!body) { return; } @@ -76,7 +77,7 @@ if (initData.styles) { for (const variable of Object.keys(initData.styles)) { - body.style.setProperty(`--${variable}`, initData.styles[variable]); + document.documentElement.style.setProperty(`--${variable}`, initData.styles[variable]); } } }; @@ -175,7 +176,7 @@ return; } - applyStyles(target.contentDocument.body); + applyStyles(target.contentDocument, target.contentDocument.body); }); // propagate focus @@ -249,10 +250,10 @@ // apply default styles const defaultStyles = newDocument.createElement('style'); defaultStyles.id = '_defaultStyles'; - defaultStyles.innerHTML = getDefaultCss(initData.styles); + defaultStyles.innerHTML = defaultCssRules; newDocument.head.prepend(defaultStyles); - applyStyles(newDocument.body); + applyStyles(newDocument, newDocument.body); const frame = getActiveFrame(); const wasFirstLoad = firstLoad; @@ -321,7 +322,7 @@ document.body.removeChild(oldActiveFrame); } // Styles may have changed since we created the element. Make sure we re-style - applyStyles(newFrame.contentDocument.body); + applyStyles(newFrame.contentDocument, newFrame.contentDocument.body); newFrame.setAttribute('id', 'active-frame'); newFrame.style.visibility = 'visible'; newFrame.contentWindow.focus(); @@ -396,19 +397,6 @@ ipcRenderer.sendToHost('webview-ready', process.pid); }); - /** - * @param {{ [variable: string]: string }} styles - */ - function getDefaultCss(styles) { - const vars = Object.keys(styles || {}).map(variable => { - return `--${variable}: ${styles[variable].replace(/[^#"',. a-z0-9\-()]/gi, '')};`; - }); - return ` - :root { ${vars.join('\n')} } - ${defaultCssRules} - `; - } - const defaultCssRules = ` body { background-color: var(--vscode-editor-background); diff --git a/src/vs/workbench/services/configuration/node/configurationEditingService.ts b/src/vs/workbench/services/configuration/common/configurationEditingService.ts similarity index 99% rename from src/vs/workbench/services/configuration/node/configurationEditingService.ts rename to src/vs/workbench/services/configuration/common/configurationEditingService.ts index d559ff78819..58d6eedd5e8 100644 --- a/src/vs/workbench/services/configuration/node/configurationEditingService.ts +++ b/src/vs/workbench/services/configuration/common/configurationEditingService.ts @@ -6,7 +6,6 @@ import * as nls from 'vs/nls'; import { URI } from 'vs/base/common/uri'; import * as json from 'vs/base/common/json'; -import * as encoding from 'vs/base/node/encoding'; import * as strings from 'vs/base/common/strings'; import { setProperty } from 'vs/base/common/jsonEdit'; import { Queue } from 'vs/base/common/async'; @@ -367,7 +366,7 @@ export class ConfigurationEditingService { private async resolveModelReference(resource: URI): Promise> { const exists = await this.fileService.existsFile(resource); if (!exists) { - await this.fileService.updateContent(resource, '{}', { encoding: encoding.UTF8 }); + await this.fileService.updateContent(resource, '{}', { encoding: 'utf8' }); } return this.textModelResolverService.createModelReference(resource); } diff --git a/src/vs/workbench/services/configuration/node/jsonEditingService.ts b/src/vs/workbench/services/configuration/common/jsonEditingService.ts similarity index 98% rename from src/vs/workbench/services/configuration/node/jsonEditingService.ts rename to src/vs/workbench/services/configuration/common/jsonEditingService.ts index 5c08f612786..31e52d94bb4 100644 --- a/src/vs/workbench/services/configuration/node/jsonEditingService.ts +++ b/src/vs/workbench/services/configuration/common/jsonEditingService.ts @@ -6,7 +6,6 @@ import * as nls from 'vs/nls'; import { URI } from 'vs/base/common/uri'; import * as json from 'vs/base/common/json'; -import * as encoding from 'vs/base/node/encoding'; import * as strings from 'vs/base/common/strings'; import { setProperty } from 'vs/base/common/jsonEdit'; import { Queue } from 'vs/base/common/async'; @@ -87,7 +86,7 @@ export class JSONEditingService implements IJSONEditingService { private async resolveModelReference(resource: URI): Promise> { const exists = await this.fileService.existsFile(resource); if (!exists) { - await this.fileService.updateContent(resource, '{}', { encoding: encoding.UTF8 }); + await this.fileService.updateContent(resource, '{}', { encoding: 'utf8' }); } return this.textModelResolverService.createModelReference(resource); } diff --git a/src/vs/workbench/services/configuration/node/configuration.ts b/src/vs/workbench/services/configuration/node/configuration.ts index b10f358735a..bf333330d4f 100644 --- a/src/vs/workbench/services/configuration/node/configuration.ts +++ b/src/vs/workbench/services/configuration/node/configuration.ts @@ -18,7 +18,7 @@ import { WorkspaceConfigurationModelParser, FolderSettingsModelParser, Standalon import { FOLDER_SETTINGS_PATH, TASKS_CONFIGURATION_KEY, FOLDER_SETTINGS_NAME, LAUNCH_CONFIGURATION_KEY } from 'vs/workbench/services/configuration/common/configuration'; import { IStoredWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces'; import * as extfs from 'vs/base/node/extfs'; -import { JSONEditingService } from 'vs/workbench/services/configuration/node/jsonEditingService'; +import { JSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditingService'; import { WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; import { extname, join } from 'vs/base/common/path'; diff --git a/src/vs/workbench/services/configuration/node/configurationService.ts b/src/vs/workbench/services/configuration/node/configurationService.ts index d7949b7b5b8..40f6b5da14d 100644 --- a/src/vs/workbench/services/configuration/node/configurationService.ts +++ b/src/vs/workbench/services/configuration/node/configurationService.ts @@ -27,9 +27,9 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { ICommandService } from 'vs/platform/commands/common/commands'; import product from 'vs/platform/product/node/product'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { ConfigurationEditingService } from 'vs/workbench/services/configuration/node/configurationEditingService'; +import { ConfigurationEditingService } from 'vs/workbench/services/configuration/common/configurationEditingService'; import { WorkspaceConfiguration, FolderConfiguration } from 'vs/workbench/services/configuration/node/configuration'; -import { JSONEditingService } from 'vs/workbench/services/configuration/node/jsonEditingService'; +import { JSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditingService'; import { UserConfiguration } from 'vs/platform/configuration/node/configuration'; import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema'; import { localize } from 'vs/nls'; diff --git a/src/vs/workbench/services/configuration/test/electron-browser/configurationEditingService.test.ts b/src/vs/workbench/services/configuration/test/electron-browser/configurationEditingService.test.ts index 970b00ae16b..a4c4ca785f2 100644 --- a/src/vs/workbench/services/configuration/test/electron-browser/configurationEditingService.test.ts +++ b/src/vs/workbench/services/configuration/test/electron-browser/configurationEditingService.test.ts @@ -21,7 +21,7 @@ import * as uuid from 'vs/base/common/uuid'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; import { WorkspaceService } from 'vs/workbench/services/configuration/node/configurationService'; import { FileService } from 'vs/workbench/services/files/node/fileService'; -import { ConfigurationEditingService, ConfigurationEditingError, ConfigurationEditingErrorCode } from 'vs/workbench/services/configuration/node/configurationEditingService'; +import { ConfigurationEditingService, ConfigurationEditingError, ConfigurationEditingErrorCode } from 'vs/workbench/services/configuration/common/configurationEditingService'; import { IFileService } from 'vs/platform/files/common/files'; import { WORKSPACE_STANDALONE_CONFIGURATIONS } from 'vs/workbench/services/configuration/common/configuration'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; 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 9dd899919b0..ce6076488b1 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 @@ -18,7 +18,7 @@ import * as uuid from 'vs/base/common/uuid'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; import { WorkspaceService } from 'vs/workbench/services/configuration/node/configurationService'; import { ISingleFolderWorkspaceInitializationPayload } from 'vs/platform/workspaces/common/workspaces'; -import { ConfigurationEditingErrorCode } from 'vs/workbench/services/configuration/node/configurationEditingService'; +import { ConfigurationEditingErrorCode } from 'vs/workbench/services/configuration/common/configurationEditingService'; import { IFileService, FileChangesEvent, FileChangeType } from 'vs/platform/files/common/files'; import { IWorkspaceContextService, WorkbenchState, IWorkspaceFoldersChangeEvent } from 'vs/platform/workspace/common/workspace'; import { ConfigurationTarget, IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; @@ -30,7 +30,7 @@ import { ITextFileService } from 'vs/workbench/services/textfile/common/textfile 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 { JSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditingService'; import { createHash } from 'crypto'; import { Emitter, Event } from 'vs/base/common/event'; import { Schemas } from 'vs/base/common/network'; diff --git a/src/vs/workbench/services/configurationResolver/common/variableResolver.ts b/src/vs/workbench/services/configurationResolver/common/variableResolver.ts index 4201c733ddc..c87f1af38fe 100644 --- a/src/vs/workbench/services/configurationResolver/common/variableResolver.ts +++ b/src/vs/workbench/services/configurationResolver/common/variableResolver.ts @@ -22,7 +22,7 @@ export interface IVariableResolveContext { getExecPath(): string | undefined; getFilePath(): string | undefined; getSelectedText(): string | undefined; - getLineNumber(): string; + getLineNumber(): string | undefined; } export class AbstractVariableResolverService implements IConfigurationResolverService { @@ -90,187 +90,181 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe throw new Error('resolveWithInteraction not implemented.'); } - private recursiveResolve(folderUri: uri, value: any, commandValueMapping?: IStringDictionary, resolvedVariables?: Map): any { + private recursiveResolve(folderUri: uri | undefined, value: any, commandValueMapping?: IStringDictionary, resolvedVariables?: Map): any { if (types.isString(value)) { - const resolved = this.resolveString(folderUri, value, commandValueMapping); - if (resolvedVariables) { - resolvedVariables.set(resolved.variableName, resolved.resolvedValue); - } - return resolved.replaced; + return this.resolveString(folderUri, value, commandValueMapping, resolvedVariables); } else if (types.isArray(value)) { return value.map(s => this.recursiveResolve(folderUri, s, commandValueMapping, resolvedVariables)); } else if (types.isObject(value)) { let result: IStringDictionary | string[]> = Object.create(null); Object.keys(value).forEach(key => { - const resolvedKey = this.resolveString(folderUri, key, commandValueMapping); - if (resolvedVariables) { - resolvedVariables.set(resolvedKey.variableName, resolvedKey.resolvedValue); - } - result[resolvedKey.replaced] = this.recursiveResolve(folderUri, value[key], commandValueMapping, resolvedVariables); + const replaced = this.resolveString(folderUri, key, commandValueMapping, resolvedVariables); + result[replaced] = this.recursiveResolve(folderUri, value[key], commandValueMapping, resolvedVariables); }); return result; } return value; } - private resolveString(folderUri: uri, value: string, commandValueMapping: IStringDictionary): { replaced: string, variableName: string, resolvedValue: string } { + private resolveString(folderUri: uri | undefined, value: string, commandValueMapping: IStringDictionary | undefined, resolvedVariables?: Map): string { - const filePath = this._context.getFilePath(); - let variableName: string | undefined; - let resolvedValue: string | undefined; + // loop through all variables occurrences in 'value' const replaced = value.replace(AbstractVariableResolverService.VARIABLE_REGEXP, (match: string, variable: string) => { - variableName = variable; - let argument: string | undefined; - const parts = variable.split(':'); - if (parts && parts.length > 1) { - variable = parts[0]; - argument = parts[1]; + let resolvedValue = this.evaluateSingleVariable(match, variable, folderUri, commandValueMapping); + + if (resolvedVariables) { + resolvedVariables.set(variable, resolvedValue); } - switch (variable) { - - case 'env': - if (argument) { - if (isWindows) { - argument = argument.toLowerCase(); - } - const env = this._envVariables[argument]; - if (types.isString(env)) { - return resolvedValue = env; - } - // For `env` we should do the same as a normal shell does - evaluates missing envs to an empty string #46436 - return resolvedValue = ''; - } - throw new Error(localize('missingEnvVarName', "'{0}' can not be resolved because no environment variable name is given.", match)); - - case 'config': - if (argument) { - const config = this._context.getConfigurationValue(folderUri, argument); - if (types.isUndefinedOrNull(config)) { - throw new Error(localize('configNotFound', "'{0}' can not be resolved because setting '{1}' not found.", match, argument)); - } - if (types.isObject(config)) { - throw new Error(localize('configNoString', "'{0}' can not be resolved because '{1}' is a structured value.", match, argument)); - } - return resolvedValue = config; - } - throw new Error(localize('missingConfigName', "'{0}' can not be resolved because no settings name is given.", match)); - - case 'command': - return resolvedValue = this.resolveFromMap(match, argument, commandValueMapping, 'command'); - case 'input': - return resolvedValue = this.resolveFromMap(match, argument, commandValueMapping, 'input'); - - default: { - - // common error handling for all variables that require an open folder and accept a folder name argument - switch (variable) { - case 'workspaceRoot': - case 'workspaceFolder': - case 'workspaceRootFolderName': - case 'workspaceFolderBasename': - case 'relativeFile': - if (argument) { - const folder = this._context.getFolderUri(argument); - if (folder) { - folderUri = folder; - } else { - throw new Error(localize('canNotFindFolder', "'{0}' can not be resolved. No such folder '{1}'.", match, argument)); - } - } - if (!folderUri) { - if (this._context.getWorkspaceFolderCount() > 1) { - throw new Error(localize('canNotResolveWorkspaceFolderMultiRoot', "'{0}' can not be resolved in a multi folder workspace. Scope this variable using ':' and a workspace folder name.", match)); - } - throw new Error(localize('canNotResolveWorkspaceFolder', "'{0}' can not be resolved. Please open a folder.", match)); - } - break; - default: - break; - } - - // common error handling for all variables that require an open file - switch (variable) { - case 'file': - case 'relativeFile': - case 'fileDirname': - case 'fileExtname': - case 'fileBasename': - case 'fileBasenameNoExtension': - if (!filePath) { - throw new Error(localize('canNotResolveFile', "'{0}' can not be resolved. Please open an editor.", match)); - } - break; - default: - break; - } - - switch (variable) { - case 'workspaceRoot': - case 'workspaceFolder': - return resolvedValue = normalizeDriveLetter(folderUri.fsPath); - - case 'cwd': - return resolvedValue = (folderUri ? normalizeDriveLetter(folderUri.fsPath) : process.cwd()); - - case 'workspaceRootFolderName': - case 'workspaceFolderBasename': - return resolvedValue = paths.basename(folderUri.fsPath); - - case 'lineNumber': - const lineNumber = this._context.getLineNumber(); - if (lineNumber) { - return resolvedValue = lineNumber; - } - throw new Error(localize('canNotResolveLineNumber', "'{0}' can not be resolved. Make sure to have a line selected in the active editor.", match)); - - case 'selectedText': - const selectedText = this._context.getSelectedText(); - if (selectedText) { - return resolvedValue = selectedText; - } - throw new Error(localize('canNotResolveSelectedText', "'{0}' can not be resolved. Make sure to have some text selected in the active editor.", match)); - - case 'file': - return resolvedValue = filePath; - - case 'relativeFile': - if (folderUri) { - return resolvedValue = paths.normalize(paths.relative(folderUri.fsPath, filePath)); - } - return resolvedValue = filePath; - - case 'fileDirname': - return resolvedValue = paths.dirname(filePath); - - case 'fileExtname': - return resolvedValue = paths.extname(filePath); - - case 'fileBasename': - return resolvedValue = paths.basename(filePath); - - case 'fileBasenameNoExtension': - const basename = paths.basename(filePath); - return resolvedValue = (basename.slice(0, basename.length - paths.extname(basename).length)); - - case 'execPath': - const ep = this._context.getExecPath(); - if (ep) { - return resolvedValue = ep; - } - return resolvedValue = match; - - default: - return resolvedValue = match; - } - } - } + return resolvedValue; }); - return { replaced, variableName, resolvedValue }; + + return replaced; } - private resolveFromMap(match: string, argument: string, commandValueMapping: IStringDictionary, prefix: string): string { + private evaluateSingleVariable(match: string, variable: string, folderUri: uri | undefined, commandValueMapping: IStringDictionary | undefined): string { + + // try to separate variable arguments from variable name + let argument: string | undefined; + const parts = variable.split(':'); + if (parts.length > 1) { + variable = parts[0]; + argument = parts[1]; + } + + // common error handling for all variables that require an open editor + const getFilePath = (): string => { + + const filePath = this._context.getFilePath(); + if (filePath) { + return filePath; + } + throw new Error(localize('canNotResolveFile', "'{0}' can not be resolved. Please open an editor.", match)); + }; + + // common error handling for all variables that require an open folder and accept a folder name argument + const getFolderUri = (withArg = true): uri => { + + if (withArg && argument) { + const folder = this._context.getFolderUri(argument); + if (folder) { + return folder; + } + throw new Error(localize('canNotFindFolder', "'{0}' can not be resolved. No such folder '{1}'.", match, argument)); + } + + if (folderUri) { + return folderUri; + } + + if (this._context.getWorkspaceFolderCount() > 1) { + throw new Error(localize('canNotResolveWorkspaceFolderMultiRoot', "'{0}' can not be resolved in a multi folder workspace. Scope this variable using ':' and a workspace folder name.", match)); + } + throw new Error(localize('canNotResolveWorkspaceFolder', "'{0}' can not be resolved. Please open a folder.", match)); + }; + + + switch (variable) { + + case 'env': + if (argument) { + if (isWindows) { + argument = argument.toLowerCase(); + } + const env = this._envVariables[argument]; + if (types.isString(env)) { + return env; + } + // For `env` we should do the same as a normal shell does - evaluates missing envs to an empty string #46436 + return ''; + } + throw new Error(localize('missingEnvVarName', "'{0}' can not be resolved because no environment variable name is given.", match)); + + case 'config': + if (argument) { + const config = this._context.getConfigurationValue(getFolderUri(false), argument); + if (types.isUndefinedOrNull(config)) { + throw new Error(localize('configNotFound', "'{0}' can not be resolved because setting '{1}' not found.", match, argument)); + } + if (types.isObject(config)) { + throw new Error(localize('configNoString', "'{0}' can not be resolved because '{1}' is a structured value.", match, argument)); + } + return config; + } + throw new Error(localize('missingConfigName', "'{0}' can not be resolved because no settings name is given.", match)); + + case 'command': + return this.resolveFromMap(match, argument, commandValueMapping, 'command'); + + case 'input': + return this.resolveFromMap(match, argument, commandValueMapping, 'input'); + + default: { + + switch (variable) { + case 'workspaceRoot': + case 'workspaceFolder': + return normalizeDriveLetter(getFolderUri().fsPath); + + case 'cwd': + return (folderUri ? normalizeDriveLetter(getFolderUri().fsPath) : process.cwd()); + + case 'workspaceRootFolderName': + case 'workspaceFolderBasename': + return paths.basename(getFolderUri().fsPath); + + case 'lineNumber': + const lineNumber = this._context.getLineNumber(); + if (lineNumber) { + return lineNumber; + } + throw new Error(localize('canNotResolveLineNumber', "'{0}' can not be resolved. Make sure to have a line selected in the active editor.", match)); + + case 'selectedText': + const selectedText = this._context.getSelectedText(); + if (selectedText) { + return selectedText; + } + throw new Error(localize('canNotResolveSelectedText', "'{0}' can not be resolved. Make sure to have some text selected in the active editor.", match)); + + case 'file': + return getFilePath(); + + case 'relativeFile': + if (folderUri) { + return paths.normalize(paths.relative(getFolderUri().fsPath, getFilePath())); + } + return getFilePath(); + + case 'fileDirname': + return paths.dirname(getFilePath()); + + case 'fileExtname': + return paths.extname(getFilePath()); + + case 'fileBasename': + return paths.basename(getFilePath()); + + case 'fileBasenameNoExtension': + const basename = paths.basename(getFilePath()); + return (basename.slice(0, basename.length - paths.extname(basename).length)); + + case 'execPath': + const ep = this._context.getExecPath(); + if (ep) { + return ep; + } + return match; + + default: + return match; + } + } + } + } + + private resolveFromMap(match: string, argument: string | undefined, commandValueMapping: IStringDictionary | undefined, prefix: string): string { if (argument && commandValueMapping) { const v = commandValueMapping[prefix + ':' + argument]; if (typeof v === 'string') { diff --git a/src/vs/workbench/services/dialogs/browser/fileDialogService.ts b/src/vs/workbench/services/dialogs/browser/fileDialogService.ts new file mode 100644 index 00000000000..3e9da451222 --- /dev/null +++ b/src/vs/workbench/services/dialogs/browser/fileDialogService.ts @@ -0,0 +1,256 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { IWindowService, INativeOpenDialogOptions, OpenDialogOptions, IURIToOpen, FileFilter } from 'vs/platform/windows/common/windows'; +import { IPickAndOpenOptions, ISaveDialogOptions, IOpenDialogOptions, IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; +import { IHistoryService } from 'vs/workbench/services/history/common/history'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { URI } from 'vs/base/common/uri'; +import { Schemas } from 'vs/base/common/network'; +import * as resources from 'vs/base/common/resources'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { RemoteFileDialog } from 'vs/workbench/services/dialogs/browser/remoteFileDialog'; +import { WORKSPACE_EXTENSION } from 'vs/platform/workspaces/common/workspaces'; +import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; + +export class FileDialogService implements IFileDialogService { + + _serviceBrand: any; + + constructor( + @IWindowService private readonly windowService: IWindowService, + @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, + @IHistoryService private readonly historyService: IHistoryService, + @IEnvironmentService private readonly environmentService: IEnvironmentService, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @IConfigurationService private readonly configurationService: IConfigurationService + ) { } + + defaultFilePath(schemeFilter = this.getSchemeFilterForWindow()): URI | undefined { + + // Check for last active file first... + let candidate = this.historyService.getLastActiveFile(schemeFilter); + + // ...then for last active file root + if (!candidate) { + candidate = this.historyService.getLastActiveWorkspaceRoot(schemeFilter); + } + + return candidate && resources.dirname(candidate) || undefined; + } + + defaultFolderPath(schemeFilter = this.getSchemeFilterForWindow()): URI | undefined { + + // Check for last active file root first... + let candidate = this.historyService.getLastActiveWorkspaceRoot(schemeFilter); + + // ...then for last active file + if (!candidate) { + candidate = this.historyService.getLastActiveFile(schemeFilter); + } + + return candidate && resources.dirname(candidate) || undefined; + } + + defaultWorkspacePath(schemeFilter = this.getSchemeFilterForWindow()): URI | undefined { + + // Check for current workspace config file first... + if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) { + const configuration = this.contextService.getWorkspace().configuration; + if (configuration && !isUntitledWorkspace(configuration, this.environmentService)) { + return resources.dirname(configuration) || undefined; + } + } + + // ...then fallback to default folder path + return this.defaultFolderPath(schemeFilter); + } + + private toNativeOpenDialogOptions(options: IPickAndOpenOptions): INativeOpenDialogOptions { + return { + forceNewWindow: options.forceNewWindow, + telemetryExtraData: options.telemetryExtraData, + dialogOptions: { + defaultPath: options.defaultUri && options.defaultUri.fsPath + } + }; + } + + private shouldUseSimplified(schema: string): boolean { + return (schema !== Schemas.file) || (this.configurationService.getValue('workbench.dialogs.useSimplified') === 'true'); + } + + private ensureFileSchema(schema: string): string[] { + return schema !== Schemas.file ? [schema, Schemas.file] : [schema]; + } + + pickFileFolderAndOpen(options: IPickAndOpenOptions): Promise { + const schema = this.getFileSystemSchema(options); + + if (!options.defaultUri) { + options.defaultUri = this.defaultFilePath(schema); + } + + if (this.shouldUseSimplified(schema)) { + const title = nls.localize('openFileOrFolder.title', 'Open File Or Folder'); + const availableFileSystems = this.ensureFileSchema(schema); // always allow file as well + return this.pickRemoteResourceAndOpen({ canSelectFiles: true, canSelectFolders: true, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems }, !!options.forceNewWindow, true); + } + + return this.windowService.pickFileFolderAndOpen(this.toNativeOpenDialogOptions(options)); + } + + pickFileAndOpen(options: IPickAndOpenOptions): Promise { + const schema = this.getFileSystemSchema(options); + + if (!options.defaultUri) { + options.defaultUri = this.defaultFilePath(schema); + } + + if (this.shouldUseSimplified(schema)) { + const title = nls.localize('openFile.title', 'Open File'); + const availableFileSystems = this.ensureFileSchema(schema); // always allow file as well + return this.pickRemoteResourceAndOpen({ canSelectFiles: true, canSelectFolders: false, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems }, !!options.forceNewWindow, true); + } + + return this.windowService.pickFileAndOpen(this.toNativeOpenDialogOptions(options)); + } + + pickFolderAndOpen(options: IPickAndOpenOptions): Promise { + const schema = this.getFileSystemSchema(options); + + if (!options.defaultUri) { + options.defaultUri = this.defaultFolderPath(schema); + } + + if (this.shouldUseSimplified(schema)) { + const title = nls.localize('openFolder.title', 'Open Folder'); + const availableFileSystems = this.ensureFileSchema(schema); // always allow file as well + return this.pickRemoteResourceAndOpen({ canSelectFiles: false, canSelectFolders: true, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems }, !!options.forceNewWindow, false); + } + + return this.windowService.pickFolderAndOpen(this.toNativeOpenDialogOptions(options)); + } + + pickWorkspaceAndOpen(options: IPickAndOpenOptions): Promise { + const schema = this.getFileSystemSchema(options); + + if (!options.defaultUri) { + options.defaultUri = this.defaultWorkspacePath(schema); + } + + if (this.shouldUseSimplified(schema)) { + const title = nls.localize('openWorkspace.title', 'Open Workspace'); + const filters: FileFilter[] = [{ name: nls.localize('filterName.workspace', 'Workspace'), extensions: [WORKSPACE_EXTENSION] }]; + const availableFileSystems = this.ensureFileSchema(schema); // always allow file as well + return this.pickRemoteResourceAndOpen({ canSelectFiles: true, canSelectFolders: false, canSelectMany: false, defaultUri: options.defaultUri, title, filters, availableFileSystems }, !!options.forceNewWindow, false); + } + + return this.windowService.pickWorkspaceAndOpen(this.toNativeOpenDialogOptions(options)); + } + + private toNativeSaveDialogOptions(options: ISaveDialogOptions): Electron.SaveDialogOptions { + return { + defaultPath: options.defaultUri && options.defaultUri.fsPath, + buttonLabel: options.saveLabel, + filters: options.filters, + title: options.title + }; + } + + showSaveDialog(options: ISaveDialogOptions): Promise { + const schema = this.getFileSystemSchema(options); + if (this.shouldUseSimplified(schema)) { + if (!options.availableFileSystems) { + options.availableFileSystems = [schema]; // by default only allow saving in the own file system + } + return this.saveRemoteResource(options); + } + + return this.windowService.showSaveDialog(this.toNativeSaveDialogOptions(options)).then(result => { + if (result) { + return URI.file(result); + } + + return undefined; + }); + } + + showOpenDialog(options: IOpenDialogOptions): Promise { + const schema = this.getFileSystemSchema(options); + if (schema !== Schemas.file) { + if (!options.availableFileSystems) { + options.availableFileSystems = [schema]; // by default only allow loading in the own file system + } + return this.pickRemoteResource(options).then(urisToOpen => { + return urisToOpen && urisToOpen.map(uto => uto.uri); + }); + } + + const defaultUri = options.defaultUri; + + const newOptions: OpenDialogOptions = { + title: options.title, + defaultPath: defaultUri && defaultUri.fsPath, + buttonLabel: options.openLabel, + filters: options.filters, + properties: [] + }; + + newOptions.properties!.push('createDirectory'); + + if (options.canSelectFiles) { + newOptions.properties!.push('openFile'); + } + + if (options.canSelectFolders) { + newOptions.properties!.push('openDirectory'); + } + + if (options.canSelectMany) { + newOptions.properties!.push('multiSelections'); + } + + return this.windowService.showOpenDialog(newOptions).then(result => result ? result.map(URI.file) : undefined); + } + + private pickRemoteResourceAndOpen(options: IOpenDialogOptions, forceNewWindow: boolean, forceOpenWorkspaceAsFile: boolean) { + return this.pickRemoteResource(options).then(urisToOpen => { + if (urisToOpen) { + return this.windowService.openWindow(urisToOpen, { forceNewWindow, forceOpenWorkspaceAsFile }); + } + return undefined; + }); + } + + private pickRemoteResource(options: IOpenDialogOptions): Promise { + const remoteFileDialog = this.instantiationService.createInstance(RemoteFileDialog); + return remoteFileDialog.showOpenDialog(options); + } + + private saveRemoteResource(options: ISaveDialogOptions): Promise { + const remoteFileDialog = this.instantiationService.createInstance(RemoteFileDialog); + return remoteFileDialog.showSaveDialog(options); + } + + private getSchemeFilterForWindow() { + return !this.windowService.getConfiguration().remoteAuthority ? Schemas.file : REMOTE_HOST_SCHEME; + } + + private getFileSystemSchema(options: { availableFileSystems?: string[], defaultUri?: URI }): string { + return options.availableFileSystems && options.availableFileSystems[0] || options.defaultUri && options.defaultUri.scheme || this.getSchemeFilterForWindow(); + } + +} + +function isUntitledWorkspace(path: URI, environmentService: IEnvironmentService): boolean { + return resources.isEqualOrParent(path, environmentService.untitledWorkspacesHome); +} + +registerSingleton(IFileDialogService, FileDialogService, true); \ No newline at end of file diff --git a/src/vs/workbench/services/dialogs/electron-browser/media/dark/accept.svg b/src/vs/workbench/services/dialogs/browser/media/dark/accept.svg similarity index 100% rename from src/vs/workbench/services/dialogs/electron-browser/media/dark/accept.svg rename to src/vs/workbench/services/dialogs/browser/media/dark/accept.svg diff --git a/src/vs/workbench/services/dialogs/electron-browser/media/dark/folder.svg b/src/vs/workbench/services/dialogs/browser/media/dark/folder.svg similarity index 100% rename from src/vs/workbench/services/dialogs/electron-browser/media/dark/folder.svg rename to src/vs/workbench/services/dialogs/browser/media/dark/folder.svg diff --git a/src/vs/workbench/services/dialogs/electron-browser/media/light/accept.svg b/src/vs/workbench/services/dialogs/browser/media/light/accept.svg similarity index 100% rename from src/vs/workbench/services/dialogs/electron-browser/media/light/accept.svg rename to src/vs/workbench/services/dialogs/browser/media/light/accept.svg diff --git a/src/vs/workbench/services/dialogs/electron-browser/media/light/folder.svg b/src/vs/workbench/services/dialogs/browser/media/light/folder.svg similarity index 100% rename from src/vs/workbench/services/dialogs/electron-browser/media/light/folder.svg rename to src/vs/workbench/services/dialogs/browser/media/light/folder.svg diff --git a/src/vs/workbench/services/dialogs/electron-browser/remoteFileDialog.ts b/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts similarity index 91% rename from src/vs/workbench/services/dialogs/electron-browser/remoteFileDialog.ts rename to src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts index 090eb7388a9..9b05a2d3e76 100644 --- a/src/vs/workbench/services/dialogs/electron-browser/remoteFileDialog.ts +++ b/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts @@ -6,7 +6,6 @@ import * as nls from 'vs/nls'; import * as resources from 'vs/base/common/resources'; import * as objects from 'vs/base/common/objects'; -import { RemoteFileService } from 'vs/workbench/services/files/node/remoteFileService'; import { IFileService, IFileStat, FileKind } from 'vs/platform/files/common/files'; import { IQuickInputService, IQuickPickItem, IQuickPick, IQuickInputButton } from 'vs/platform/quickinput/common/quickInput'; import { URI } from 'vs/base/common/uri'; @@ -21,6 +20,8 @@ import { IModelService } from 'vs/editor/common/services/modelService'; import { IModeService } from 'vs/editor/common/services/modeService'; import { getIconClasses } from 'vs/editor/common/services/getIconClasses'; import { Schemas } from 'vs/base/common/network'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IRemoteEnvironmentService } from 'vs/workbench/services/remote/common/remoteEnvironmentService'; interface FileQuickPickItem extends IQuickPickItem { uri: URI; @@ -48,7 +49,7 @@ export class RemoteFileDialog { private shouldOverwriteFile: boolean = false; constructor( - @IFileService private readonly remoteFileService: RemoteFileService, + @IFileService private readonly fileService: IFileService, @IQuickInputService private readonly quickInputService: IQuickInputService, @IWindowService private readonly windowService: IWindowService, @ILabelService private readonly labelService: ILabelService, @@ -57,13 +58,16 @@ export class RemoteFileDialog { @IFileDialogService private readonly fileDialogService: IFileDialogService, @IModelService private readonly modelService: IModelService, @IModeService private readonly modeService: IModeService, + @IEnvironmentService private readonly environmentService: IEnvironmentService, + @IRemoteEnvironmentService private readonly remoteEnvironmentService: IRemoteEnvironmentService, + ) { this.remoteAuthority = this.windowService.getConfiguration().remoteAuthority; } public async showOpenDialog(options: IOpenDialogOptions = {}): Promise { this.scheme = this.getScheme(options.defaultUri, options.availableFileSystems); - const newOptions = this.getOptions(options); + const newOptions = await this.getOptions(options); if (!newOptions) { return Promise.resolve(undefined); } @@ -77,7 +81,7 @@ export class RemoteFileDialog { return this.pickResource().then(async fileFolderUri => { if (fileFolderUri) { - const stat = await this.remoteFileService.resolveFile(fileFolderUri); + const stat = await this.fileService.resolveFile(fileFolderUri); return [{ uri: fileFolderUri, typeHint: stat.isDirectory ? 'folder' : 'file' }]; } @@ -85,10 +89,10 @@ export class RemoteFileDialog { }); } - public showSaveDialog(options: ISaveDialogOptions): Promise { + public async showSaveDialog(options: ISaveDialogOptions): Promise { this.scheme = this.getScheme(options.defaultUri, options.availableFileSystems); this.requiresTrailing = true; - const newOptions = this.getOptions(options); + const newOptions = await this.getOptions(options); if (!newOptions) { return Promise.resolve(undefined); } @@ -104,9 +108,17 @@ export class RemoteFileDialog { }); } - private getOptions(options: ISaveDialogOptions | IOpenDialogOptions): IOpenDialogOptions | undefined { - const defaultUri = options.defaultUri ? options.defaultUri : URI.from({ scheme: this.scheme, authority: this.remoteAuthority, path: '/' }); - if ((this.scheme !== Schemas.file) && !this.remoteFileService.canHandleResource(defaultUri)) { + private async getOptions(options: ISaveDialogOptions | IOpenDialogOptions): Promise { + let defaultUri = options.defaultUri; + if (!defaultUri) { + const env = await this.remoteEnvironmentService.remoteEnvironment; + if (env) { + defaultUri = env.userHome; + } else { + defaultUri = URI.from({ scheme: this.scheme, path: this.environmentService.userHome }); + } + } + if ((this.scheme !== Schemas.file) && !this.fileService.canHandleResource(defaultUri)) { this.notificationService.info(nls.localize('remoteFileDialog.notConnectedToRemote', 'File system provider for {0} is not available.', defaultUri.toString())); return undefined; } @@ -141,7 +153,7 @@ export class RemoteFileDialog { let ext: string = resources.extname(homedir); if (this.options.defaultUri) { try { - stat = await this.remoteFileService.resolveFile(this.options.defaultUri); + stat = await this.fileService.resolveFile(this.options.defaultUri); } catch (e) { // The file or folder doesn't exist } @@ -236,6 +248,8 @@ export class RemoteFileDialog { this.updateItems(homedir, trailing); if (trailing) { this.filePickBox.valueSelection = [this.filePickBox.value.length - trailing.length, this.filePickBox.value.length - ext.length]; + } else { + this.filePickBox.valueSelection = [this.filePickBox.value.length, this.filePickBox.value.length]; } this.userValue = this.filePickBox.value; }); @@ -267,8 +281,8 @@ export class RemoteFileDialog { let stat: IFileStat | undefined; let statDirname: IFileStat | undefined; try { - statDirname = await this.remoteFileService.resolveFile(inputUriDirname); - stat = await this.remoteFileService.resolveFile(inputUri); + statDirname = await this.fileService.resolveFile(inputUriDirname); + stat = await this.fileService.resolveFile(inputUri); } catch (e) { // do nothing } @@ -310,7 +324,7 @@ export class RemoteFileDialog { 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); + stat = await this.fileService.resolveFile(valueUri); } catch (e) { // do nothing } @@ -321,7 +335,7 @@ export class RemoteFileDialog { if (!resources.isEqual(this.currentFolder, inputUriDirname, true)) { let statWithoutTrailing: IFileStat | undefined; try { - statWithoutTrailing = await this.remoteFileService.resolveFile(inputUriDirname); + statWithoutTrailing = await this.fileService.resolveFile(inputUriDirname); } catch (e) { // do nothing } @@ -358,8 +372,8 @@ export class RemoteFileDialog { let stat: IFileStat | undefined; let statDirname: IFileStat | undefined; try { - statDirname = await this.remoteFileService.resolveFile(resources.dirname(uri)); - stat = await this.remoteFileService.resolveFile(uri); + statDirname = await this.fileService.resolveFile(resources.dirname(uri)); + stat = await this.fileService.resolveFile(uri); } catch (e) { // do nothing } @@ -480,7 +494,7 @@ export class RemoteFileDialog { const backDir = this.createBackItem(currentFolder); try { - const fileNames = await this.remoteFileService.readFolder(currentFolder); + const fileNames = await this.fileService.readFolder(currentFolder); const items = await Promise.all(fileNames.map(fileName => this.createItem(fileName, currentFolder))); for (let item of items) { if (item) { @@ -528,7 +542,7 @@ export class RemoteFileDialog { private async createItem(filename: string, parent: URI): Promise { let fullPath = resources.joinPath(parent, filename); try { - const stat = await this.remoteFileService.resolveFile(fullPath); + const stat = await this.fileService.resolveFile(fullPath); if (stat.isDirectory) { filename = this.basenameWithTrailingSlash(fullPath); return { label: filename, uri: fullPath, isFolder: true, iconClasses: getIconClasses(this.modelService, this.modeService, fullPath || undefined, FileKind.FOLDER) }; @@ -543,8 +557,8 @@ export class RemoteFileDialog { private getDialogIcons(name: string): { light: URI, dark: URI } { return { - dark: URI.parse(require.toUrl(`vs/workbench/services/dialogs/electron-browser/media/dark/${name}.svg`)), - light: URI.parse(require.toUrl(`vs/workbench/services/dialogs/electron-browser/media/light/${name}.svg`)) + dark: URI.parse(require.toUrl(`vs/workbench/services/dialogs/browser/media/dark/${name}.svg`)), + light: URI.parse(require.toUrl(`vs/workbench/services/dialogs/browser/media/light/${name}.svg`)) }; } } \ No newline at end of file diff --git a/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts b/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts index 71c66bf9209..1eb57c6c736 100644 --- a/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts +++ b/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts @@ -7,21 +7,10 @@ import * as nls from 'vs/nls'; import product from 'vs/platform/product/node/product'; import Severity from 'vs/base/common/severity'; import { isLinux, isWindows } from 'vs/base/common/platform'; -import { IWindowService, INativeOpenDialogOptions, OpenDialogOptions, IURIToOpen, FileFilter } from 'vs/platform/windows/common/windows'; +import { IWindowService } from 'vs/platform/windows/common/windows'; import { mnemonicButtonLabel } from 'vs/base/common/labels'; -import { IDialogService, IConfirmation, IConfirmationResult, IDialogOptions, IPickAndOpenOptions, ISaveDialogOptions, IOpenDialogOptions, IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { IDialogService, IConfirmation, IConfirmationResult, IDialogOptions } from 'vs/platform/dialogs/common/dialogs'; import { ILogService } from 'vs/platform/log/common/log'; -import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; -import { IHistoryService } from 'vs/workbench/services/history/common/history'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { URI } from 'vs/base/common/uri'; -import { Schemas } from 'vs/base/common/network'; -import * as resources from 'vs/base/common/resources'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { RemoteFileDialog } from 'vs/workbench/services/dialogs/electron-browser/remoteFileDialog'; -import { WORKSPACE_EXTENSION } from 'vs/platform/workspaces/common/workspaces'; -import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; import { DialogChannel } from 'vs/platform/dialogs/node/dialogIpc'; @@ -162,239 +151,4 @@ export class DialogService implements IDialogService { } } -export class FileDialogService implements IFileDialogService { - - _serviceBrand: any; - - constructor( - @IWindowService private readonly windowService: IWindowService, - @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, - @IHistoryService private readonly historyService: IHistoryService, - @IEnvironmentService private readonly environmentService: IEnvironmentService, - @IInstantiationService private readonly instantiationService: IInstantiationService, - @IConfigurationService private readonly configurationService: IConfigurationService - ) { } - - defaultFilePath(schemeFilter = this.getSchemeFilterForWindow()): URI | undefined { - - // Check for last active file first... - let candidate = this.historyService.getLastActiveFile(schemeFilter); - - // ...then for last active file root - if (!candidate) { - candidate = this.historyService.getLastActiveWorkspaceRoot(schemeFilter); - } - - return candidate && resources.dirname(candidate) || undefined; - } - - defaultFolderPath(schemeFilter = this.getSchemeFilterForWindow()): URI | undefined { - - // Check for last active file root first... - let candidate = this.historyService.getLastActiveWorkspaceRoot(schemeFilter); - - // ...then for last active file - if (!candidate) { - candidate = this.historyService.getLastActiveFile(schemeFilter); - } - - return candidate && resources.dirname(candidate) || undefined; - } - - defaultWorkspacePath(schemeFilter = this.getSchemeFilterForWindow()): URI | undefined { - - // Check for current workspace config file first... - if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) { - const configuration = this.contextService.getWorkspace().configuration; - if (configuration && !isUntitledWorkspace(configuration, this.environmentService)) { - return resources.dirname(configuration) || undefined; - } - } - - // ...then fallback to default folder path - return this.defaultFolderPath(schemeFilter); - } - - private toNativeOpenDialogOptions(options: IPickAndOpenOptions): INativeOpenDialogOptions { - return { - forceNewWindow: options.forceNewWindow, - telemetryExtraData: options.telemetryExtraData, - dialogOptions: { - defaultPath: options.defaultUri && options.defaultUri.fsPath - } - }; - } - - private shouldUseSimplified(schema: string): boolean { - return (schema !== Schemas.file) || (this.configurationService.getValue('workbench.dialogs.useSimplified') === 'true'); - } - - private ensureFileSchema(schema: string): string[] { - return schema !== Schemas.file ? [schema, Schemas.file] : [schema]; - } - - pickFileFolderAndOpen(options: IPickAndOpenOptions): Promise { - const schema = this.getFileSystemSchema(options); - - if (!options.defaultUri) { - options.defaultUri = this.defaultFilePath(schema); - } - - if (this.shouldUseSimplified(schema)) { - const title = nls.localize('openFileOrFolder.title', 'Open File Or Folder'); - const availableFileSystems = this.ensureFileSchema(schema); // always allow file as well - return this.pickRemoteResourceAndOpen({ canSelectFiles: true, canSelectFolders: true, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems }, !!options.forceNewWindow, true); - } - - return this.windowService.pickFileFolderAndOpen(this.toNativeOpenDialogOptions(options)); - } - - pickFileAndOpen(options: IPickAndOpenOptions): Promise { - const schema = this.getFileSystemSchema(options); - - if (!options.defaultUri) { - options.defaultUri = this.defaultFilePath(schema); - } - - if (this.shouldUseSimplified(schema)) { - const title = nls.localize('openFile.title', 'Open File'); - const availableFileSystems = this.ensureFileSchema(schema); // always allow file as well - return this.pickRemoteResourceAndOpen({ canSelectFiles: true, canSelectFolders: false, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems }, !!options.forceNewWindow, true); - } - - return this.windowService.pickFileAndOpen(this.toNativeOpenDialogOptions(options)); - } - - pickFolderAndOpen(options: IPickAndOpenOptions): Promise { - const schema = this.getFileSystemSchema(options); - - if (!options.defaultUri) { - options.defaultUri = this.defaultFolderPath(schema); - } - - if (this.shouldUseSimplified(schema)) { - const title = nls.localize('openFolder.title', 'Open Folder'); - const availableFileSystems = this.ensureFileSchema(schema); // always allow file as well - return this.pickRemoteResourceAndOpen({ canSelectFiles: false, canSelectFolders: true, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems }, !!options.forceNewWindow, false); - } - - return this.windowService.pickFolderAndOpen(this.toNativeOpenDialogOptions(options)); - } - - pickWorkspaceAndOpen(options: IPickAndOpenOptions): Promise { - const schema = this.getFileSystemSchema(options); - - if (!options.defaultUri) { - options.defaultUri = this.defaultWorkspacePath(schema); - } - - if (this.shouldUseSimplified(schema)) { - const title = nls.localize('openWorkspace.title', 'Open Workspace'); - const filters: FileFilter[] = [{ name: nls.localize('filterName.workspace', 'Workspace'), extensions: [WORKSPACE_EXTENSION] }]; - const availableFileSystems = this.ensureFileSchema(schema); // always allow file as well - return this.pickRemoteResourceAndOpen({ canSelectFiles: true, canSelectFolders: false, canSelectMany: false, defaultUri: options.defaultUri, title, filters, availableFileSystems }, !!options.forceNewWindow, false); - } - - return this.windowService.pickWorkspaceAndOpen(this.toNativeOpenDialogOptions(options)); - } - - private toNativeSaveDialogOptions(options: ISaveDialogOptions): Electron.SaveDialogOptions { - return { - defaultPath: options.defaultUri && options.defaultUri.fsPath, - buttonLabel: options.saveLabel, - filters: options.filters, - title: options.title - }; - } - - showSaveDialog(options: ISaveDialogOptions): Promise { - const schema = this.getFileSystemSchema(options); - if (this.shouldUseSimplified(schema)) { - if (!options.availableFileSystems) { - options.availableFileSystems = [schema]; // by default only allow saving in the own file system - } - return this.saveRemoteResource(options); - } - - return this.windowService.showSaveDialog(this.toNativeSaveDialogOptions(options)).then(result => { - if (result) { - return URI.file(result); - } - - return undefined; - }); - } - - showOpenDialog(options: IOpenDialogOptions): Promise { - const schema = this.getFileSystemSchema(options); - if (schema !== Schemas.file) { - if (!options.availableFileSystems) { - options.availableFileSystems = [schema]; // by default only allow loading in the own file system - } - return this.pickRemoteResource(options).then(urisToOpen => { - return urisToOpen && urisToOpen.map(uto => uto.uri); - }); - } - - const defaultUri = options.defaultUri; - - const newOptions: OpenDialogOptions = { - title: options.title, - defaultPath: defaultUri && defaultUri.fsPath, - buttonLabel: options.openLabel, - filters: options.filters, - properties: [] - }; - - newOptions.properties!.push('createDirectory'); - - if (options.canSelectFiles) { - newOptions.properties!.push('openFile'); - } - - if (options.canSelectFolders) { - newOptions.properties!.push('openDirectory'); - } - - if (options.canSelectMany) { - newOptions.properties!.push('multiSelections'); - } - - return this.windowService.showOpenDialog(newOptions).then(result => result ? result.map(URI.file) : undefined); - } - - private pickRemoteResourceAndOpen(options: IOpenDialogOptions, forceNewWindow: boolean, forceOpenWorkspaceAsFile: boolean) { - return this.pickRemoteResource(options).then(urisToOpen => { - if (urisToOpen) { - return this.windowService.openWindow(urisToOpen, { forceNewWindow, forceOpenWorkspaceAsFile }); - } - return undefined; - }); - } - - private pickRemoteResource(options: IOpenDialogOptions): Promise { - const remoteFileDialog = this.instantiationService.createInstance(RemoteFileDialog); - return remoteFileDialog.showOpenDialog(options); - } - - private saveRemoteResource(options: ISaveDialogOptions): Promise { - const remoteFileDialog = this.instantiationService.createInstance(RemoteFileDialog); - return remoteFileDialog.showSaveDialog(options); - } - - private getSchemeFilterForWindow() { - return !this.windowService.getConfiguration().remoteAuthority ? Schemas.file : REMOTE_HOST_SCHEME; - } - - private getFileSystemSchema(options: { availableFileSystems?: string[], defaultUri?: URI }): string { - return options.availableFileSystems && options.availableFileSystems[0] || options.defaultUri && options.defaultUri.scheme || this.getSchemeFilterForWindow(); - } - -} - -function isUntitledWorkspace(path: URI, environmentService: IEnvironmentService): boolean { - return resources.isEqualOrParent(path, environmentService.untitledWorkspacesHome); -} - -registerSingleton(IFileDialogService, FileDialogService, true); registerSingleton(IDialogService, DialogService, true); \ No newline at end of file diff --git a/src/vs/workbench/services/extensionManagement/node/multiExtensionManagement.ts b/src/vs/workbench/services/extensionManagement/node/multiExtensionManagement.ts index 69061ed76c7..9059f1826c6 100644 --- a/src/vs/workbench/services/extensionManagement/node/multiExtensionManagement.ts +++ b/src/vs/workbench/services/extensionManagement/node/multiExtensionManagement.ts @@ -14,7 +14,7 @@ import { URI } from 'vs/base/common/uri'; import { Disposable } from 'vs/base/common/lifecycle'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { IRemoteAgentService } from 'vs/workbench/services/remote/node/remoteAgentService'; +import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { getManifest } from 'vs/platform/extensionManagement/node/extensionManagementUtil'; import { ILogService } from 'vs/platform/log/common/log'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; diff --git a/src/vs/workbench/services/extensions/common/extensions.ts b/src/vs/workbench/services/extensions/common/extensions.ts index e06bea02144..04a24e602ce 100644 --- a/src/vs/workbench/services/extensions/common/extensions.ts +++ b/src/vs/workbench/services/extensions/common/extensions.ts @@ -8,18 +8,9 @@ import Severity from 'vs/base/common/severity'; import { URI } from 'vs/base/common/uri'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IExtensionPoint } from 'vs/workbench/services/extensions/common/extensionsRegistry'; -import { ExtensionIdentifier, IExtensionManifest, IExtension, ExtensionType } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, IExtension, ExtensionType, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; -export interface IExtensionDescription extends IExtensionManifest { - readonly identifier: ExtensionIdentifier; - readonly uuid?: string; - readonly isBuiltin: boolean; - readonly isUnderDevelopment: boolean; - readonly extensionLocation: URI; - enableProposedApi?: boolean; -} - export const nullExtensionDescription = Object.freeze({ identifier: new ExtensionIdentifier('nullExtensionDescription'), name: 'Null Extension Description', @@ -46,6 +37,11 @@ export interface IExtensionsStatus { runtimeErrors: Error[]; } +export type ExtensionActivationError = string | MissingDependencyError; +export class MissingDependencyError { + constructor(readonly dependency: string) { } +} + /** * e.g. * ``` diff --git a/src/vs/workbench/services/extensions/common/extensionsRegistry.ts b/src/vs/workbench/services/extensions/common/extensionsRegistry.ts index 97276a5e8b1..aa3e0aeedec 100644 --- a/src/vs/workbench/services/extensions/common/extensionsRegistry.ts +++ b/src/vs/workbench/services/extensions/common/extensionsRegistry.ts @@ -10,8 +10,8 @@ import Severity from 'vs/base/common/severity'; import { EXTENSION_IDENTIFIER_PATTERN } from 'vs/platform/extensionManagement/common/extensionManagement'; import { Extensions, IJSONContributionRegistry } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import { Registry } from 'vs/platform/registry/common/platform'; -import { IExtensionDescription, IMessage } from 'vs/workbench/services/extensions/common/extensions'; -import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { IMessage } from 'vs/workbench/services/extensions/common/extensions'; +import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; const hasOwnProperty = Object.hasOwnProperty; const schemaRegistry = Registry.as(Extensions.JSONContribution); diff --git a/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts b/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts index 059291079fe..500eae61411 100644 --- a/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts +++ b/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts @@ -16,12 +16,11 @@ import { URI } from 'vs/base/common/uri'; import * as pfs from 'vs/base/node/pfs'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { BUILTIN_MANIFEST_CACHE_FILE, MANIFEST_CACHE_FOLDER, USER_MANIFEST_CACHE_FILE, ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { BUILTIN_MANIFEST_CACHE_FILE, MANIFEST_CACHE_FOLDER, USER_MANIFEST_CACHE_FILE, ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import pkg from 'vs/platform/product/node/package'; import product from 'vs/platform/product/node/product'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { IWindowService } from 'vs/platform/windows/common/windows'; -import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import { ExtensionScanner, ExtensionScannerInput, IExtensionReference, IExtensionResolver, ILog, IRelaxedExtensionDescription, Translations } from 'vs/workbench/services/extensions/node/extensionPoints'; interface IExtensionCacheData { diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts index dc7e4284ddc..7e8e7d72c9d 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts @@ -34,13 +34,13 @@ import { IWindowService, IWindowsService } from 'vs/platform/windows/common/wind import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IInitData } from 'vs/workbench/api/node/extHost.protocol'; import { MessageType, createMessageOfType, isMessageOfType } from 'vs/workbench/services/extensions/node/extensionHostProtocol'; -import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import { withNullAsUndefined } from 'vs/base/common/types'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; export interface IExtensionHostStarter { - readonly onCrashed: Event<[number, string]>; + readonly onCrashed: Event<[number, string | null]>; start(): Promise | null; - getInspectPort(): number; + getInspectPort(): number | undefined; dispose(): void; } diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts index aa60ce0f1f2..8e2df7528eb 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts @@ -12,13 +12,13 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ExtHostCustomersRegistry } from 'vs/workbench/api/electron-browser/extHostCustomers'; import { ExtHostContext, ExtHostExtensionServiceShape, IExtHostContext, MainContext } from 'vs/workbench/api/node/extHost.protocol'; -import { ProfileSession, IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; +import { ProfileSession } from 'vs/workbench/services/extensions/common/extensions'; import { IExtensionHostStarter } from 'vs/workbench/services/extensions/electron-browser/extensionHost'; import { ExtensionHostProfiler } from 'vs/workbench/services/extensions/electron-browser/extensionHostProfiler'; import { ProxyIdentifier } from 'vs/workbench/services/extensions/node/proxyIdentifier'; import { IRPCProtocolLogger, RPCProtocol, RequestInitiator, ResponsiveState } from 'vs/workbench/services/extensions/node/rpcProtocol'; import { ResolvedAuthority } from 'vs/platform/remote/common/remoteAuthorityResolver'; -import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import * as nls from 'vs/nls'; import { Action } from 'vs/base/common/actions'; import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; @@ -36,7 +36,7 @@ const NO_OP_VOID_PROMISE = Promise.resolve(undefined); export class ExtensionHostProcessManager extends Disposable { - public readonly onDidCrash: Event<[number, string]>; + public readonly onDidCrash: Event<[number, string | null]>; private readonly _onDidChangeResponsiveState: Emitter = this._register(new Emitter()); public readonly onDidChangeResponsiveState: Event = this._onDidChangeResponsiveState.event; diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts index 16ebfa07804..9e12b862aec 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts @@ -6,7 +6,8 @@ import { Profile, ProfileNode } from 'v8-inspect-profiler'; import { TernarySearchTree } from 'vs/base/common/map'; import { realpathSync } from 'vs/base/node/extfs'; -import { IExtensionDescription, IExtensionHostProfile, IExtensionService, ProfileSegmentId, ProfileSession } from 'vs/workbench/services/extensions/common/extensions'; +import { IExtensionHostProfile, IExtensionService, ProfileSegmentId, ProfileSession } from 'vs/workbench/services/extensions/common/extensions'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; export class ExtensionHostProfiler { diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionManagementServerService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionManagementServerService.ts index 8b1011377b7..e03130527b3 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionManagementServerService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionManagementServerService.ts @@ -8,9 +8,9 @@ import { Schemas } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; import { IExtensionManagementServer, IExtensionManagementServerService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ExtensionManagementChannelClient } from 'vs/platform/extensionManagement/node/extensionManagementIpc'; -import { IRemoteAgentService } from 'vs/workbench/services/remote/node/remoteAgentService'; +import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; -import { IChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index 3f2be8776f7..f8bbeae531c 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -22,14 +22,14 @@ import product from 'vs/platform/product/node/product'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows'; -import { ActivationTimes, ExtensionPointContribution, IExtensionDescription, IExtensionService, IExtensionsStatus, IMessage, ProfileSession, IWillActivateEvent, IResponsiveStateChangeEvent, toExtension } from 'vs/workbench/services/extensions/common/extensions'; +import { ActivationTimes, ExtensionPointContribution, IExtensionService, IExtensionsStatus, IMessage, ProfileSession, IWillActivateEvent, IResponsiveStateChangeEvent, toExtension } from 'vs/workbench/services/extensions/common/extensions'; import { ExtensionMessageCollector, ExtensionPoint, ExtensionsRegistry, IExtensionPoint, IExtensionPointUser, schema } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { ExtensionHostProcessWorker } from 'vs/workbench/services/extensions/electron-browser/extensionHost'; import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry'; import { ResponsiveState } from 'vs/workbench/services/extensions/node/rpcProtocol'; import { CachedExtensionScanner, Logger } from 'vs/workbench/services/extensions/electron-browser/cachedExtensionScanner'; import { ExtensionHostProcessManager } from 'vs/workbench/services/extensions/electron-browser/extensionHostProcessManager'; -import { ExtensionIdentifier, IExtension, ExtensionType } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, IExtension, ExtensionType, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { Schemas } from 'vs/base/common/network'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; @@ -436,7 +436,7 @@ export class ExtensionService extends Disposable implements IExtensionService { this._extensionHostProcessManagers.push(extHostProcessManager); } - private _onExtensionHostCrashed(code: number, signal: string): void { + private _onExtensionHostCrashed(code: number, signal: string | null): void { console.error('Extension host terminated unexpectedly. Code: ', code, ' Signal: ', signal); this._stopExtensionHostProcess(); diff --git a/src/vs/workbench/services/extensions/node/extensionDescriptionRegistry.ts b/src/vs/workbench/services/extensions/node/extensionDescriptionRegistry.ts index 972c560d6b9..0c3523f555e 100644 --- a/src/vs/workbench/services/extensions/node/extensionDescriptionRegistry.ts +++ b/src/vs/workbench/services/extensions/node/extensionDescriptionRegistry.ts @@ -3,8 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; -import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { Emitter } from 'vs/base/common/event'; export class DeltaExtensionsResult { diff --git a/src/vs/workbench/services/extensions/node/extensionHostMain.ts b/src/vs/workbench/services/extensions/node/extensionHostMain.ts index 976f614a3a6..b584e8652f8 100644 --- a/src/vs/workbench/services/extensions/node/extensionHostMain.ts +++ b/src/vs/workbench/services/extensions/node/extensionHostMain.ts @@ -15,8 +15,8 @@ import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService'; import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService'; import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace'; -import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import { RPCProtocol } from 'vs/workbench/services/extensions/node/rpcProtocol'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; // we don't (yet) throw when extensions parse // uris that have no scheme diff --git a/src/vs/workbench/services/extensions/node/extensionPoints.ts b/src/vs/workbench/services/extensions/node/extensionPoints.ts index e2b2719a810..25e50d43f98 100644 --- a/src/vs/workbench/services/extensions/node/extensionPoints.ts +++ b/src/vs/workbench/services/extensions/node/extensionPoints.ts @@ -14,8 +14,7 @@ import { URI } from 'vs/base/common/uri'; import * as pfs from 'vs/base/node/pfs'; import { getGalleryExtensionId, groupByExtension } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { isValidExtensionVersion } from 'vs/platform/extensions/node/extensionValidator'; -import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; -import { ExtensionIdentifier, ExtensionIdentifierWithVersion } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, ExtensionIdentifierWithVersion, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; const MANIFEST_FILE = 'package.json'; @@ -447,7 +446,7 @@ export class ExtensionScannerInput { constructor( public readonly ourVersion: string, - public readonly commit: string | undefined, + public readonly commit: string | null | undefined, public readonly locale: string | undefined, public readonly devMode: boolean, public readonly absoluteFolderPath: string, diff --git a/src/vs/workbench/services/files/node/fileService.ts b/src/vs/workbench/services/files/node/fileService.ts index 162b4ae657c..60094945b5e 100644 --- a/src/vs/workbench/services/files/node/fileService.ts +++ b/src/vs/workbench/services/files/node/fileService.ts @@ -9,7 +9,7 @@ import * as os from 'os'; import * as crypto from 'crypto'; import * as assert from 'assert'; import { isParent, FileOperation, FileOperationEvent, IContent, IFileService, IResolveFileOptions, IResolveFileResult, IResolveContentOptions, IFileStat, IStreamContent, FileOperationError, FileOperationResult, IUpdateContentOptions, FileChangeType, FileChangesEvent, ICreateFileOptions, IContentData, ITextSnapshot, IFilesConfiguration, IFileSystemProviderRegistrationEvent, IFileSystemProvider } from 'vs/platform/files/common/files'; -import { MAX_FILE_SIZE, MAX_HEAP_SIZE } from 'vs/platform/files/node/files'; +import { MAX_FILE_SIZE, MAX_HEAP_SIZE } from 'vs/platform/files/node/fileConstants'; import { isEqualOrParent } from 'vs/base/common/extpath'; import { ResourceMap } from 'vs/base/common/map'; import * as arrays from 'vs/base/common/arrays'; diff --git a/src/vs/workbench/services/files/node/watcher/nsfw/watcherIpc.ts b/src/vs/workbench/services/files/node/watcher/nsfw/watcherIpc.ts index 691db8f251f..7283b31ccc1 100644 --- a/src/vs/workbench/services/files/node/watcher/nsfw/watcherIpc.ts +++ b/src/vs/workbench/services/files/node/watcher/nsfw/watcherIpc.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; import { IWatcherRequest, IWatcherService, IWatcherOptions, IWatchError } from './watcher'; import { Event } from 'vs/base/common/event'; import { IRawFileChange } from 'vs/workbench/services/files/node/watcher/common'; diff --git a/src/vs/workbench/services/files/node/watcher/unix/watcherIpc.ts b/src/vs/workbench/services/files/node/watcher/unix/watcherIpc.ts index 691db8f251f..7283b31ccc1 100644 --- a/src/vs/workbench/services/files/node/watcher/unix/watcherIpc.ts +++ b/src/vs/workbench/services/files/node/watcher/unix/watcherIpc.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; import { IWatcherRequest, IWatcherService, IWatcherOptions, IWatchError } from './watcher'; import { Event } from 'vs/base/common/event'; import { IRawFileChange } from 'vs/workbench/services/files/node/watcher/common'; diff --git a/src/vs/workbench/services/preferences/common/preferences.ts b/src/vs/workbench/services/preferences/common/preferences.ts index 06b0a00d28e..c5f7f6bd07f 100644 --- a/src/vs/workbench/services/preferences/common/preferences.ts +++ b/src/vs/workbench/services/preferences/common/preferences.ts @@ -132,7 +132,7 @@ export interface IFilterMetadata { export interface IPreferencesEditorModel { uri?: URI; - getPreference(key: string): T | null; + getPreference(key: string): T | undefined; dispose(): void; } @@ -144,7 +144,7 @@ export interface ISettingsEditorModel extends IPreferencesEditorModel settingsGroups: ISettingsGroup[]; filterSettings(filter: string, groupFilter: IGroupFilter, settingMatcher: ISettingMatcher): ISettingMatch[]; findValueMatches(filter: string, setting: ISetting): IRange[]; - updateResultGroup(id: string, resultGroup: ISearchResultGroup): IFilterResult | null; + updateResultGroup(id: string, resultGroup: ISearchResultGroup | undefined): IFilterResult | undefined; } export interface ISettingsEditorOptions extends IEditorOptions { diff --git a/src/vs/workbench/services/preferences/common/preferencesModels.ts b/src/vs/workbench/services/preferences/common/preferencesModels.ts index 8a32f4fcd08..54c31c93455 100644 --- a/src/vs/workbench/services/preferences/common/preferencesModels.ts +++ b/src/vs/workbench/services/preferences/common/preferencesModels.ts @@ -31,7 +31,7 @@ export abstract class AbstractSettingsModel extends EditorModel { protected _currentResultGroups = new Map(); - updateResultGroup(id: string, resultGroup: ISearchResultGroup): IFilterResult | null { + updateResultGroup(id: string, resultGroup: ISearchResultGroup | undefined): IFilterResult | undefined { if (resultGroup) { this._currentResultGroups.set(id, resultGroup); } else { @@ -80,7 +80,7 @@ export abstract class AbstractSettingsModel extends EditorModel { return filterMatches.sort((a, b) => b.score - a.score); } - getPreference(key: string): ISetting | null { + getPreference(key: string): ISetting | undefined { for (const group of this.settingsGroups) { for (const section of group.sections) { for (const setting of section.settings) { @@ -90,7 +90,8 @@ export abstract class AbstractSettingsModel extends EditorModel { } } } - return null; + + return undefined; } protected collectMetadata(groups: ISearchResultGroup[]): IStringDictionary { @@ -115,12 +116,12 @@ export abstract class AbstractSettingsModel extends EditorModel { abstract findValueMatches(filter: string, setting: ISetting): IRange[]; - protected abstract update(): IFilterResult | null; + protected abstract update(): IFilterResult | undefined; } export class SettingsEditorModel extends AbstractSettingsModel implements ISettingsEditorModel { - private _settingsGroups: ISettingsGroup[] | null; + private _settingsGroups: ISettingsGroup[] | undefined; protected settingsModel: ITextModel; private readonly _onDidChangeGroups: Emitter = this._register(new Emitter()); @@ -131,7 +132,7 @@ export class SettingsEditorModel extends AbstractSettingsModel implements ISetti this.settingsModel = reference.object.textEditorModel!; this._register(this.onDispose(() => reference.dispose())); this._register(this.settingsModel.onDidChangeContent(() => { - this._settingsGroups = null; + this._settingsGroups = undefined; this._onDidChangeGroups.fire(); })); } @@ -167,10 +168,10 @@ export class SettingsEditorModel extends AbstractSettingsModel implements ISetti this._settingsGroups = parse(this.settingsModel, (property: string, previousParents: string[]): boolean => this.isSettingsProperty(property, previousParents)); } - protected update(): IFilterResult | null { + protected update(): IFilterResult | undefined { const resultGroups = map.values(this._currentResultGroups); if (!resultGroups.length) { - return null; + return undefined; } // Transform resultGroups into IFilterResult - ISetting ranges are already correct here @@ -724,9 +725,9 @@ export class DefaultSettingsEditorModel extends AbstractSettingsModel implements return this.settingsGroups.slice(1); } - protected update(): IFilterResult | null { + protected update(): IFilterResult | undefined { if (this._model.isDisposed()) { - return null; + return undefined; } // Grab current result groups, only render non-empty groups @@ -746,7 +747,7 @@ export class DefaultSettingsEditorModel extends AbstractSettingsModel implements matches, metadata } : - null; + undefined; } /** @@ -848,7 +849,7 @@ export class DefaultSettingsEditorModel extends AbstractSettingsModel implements return []; } - getPreference(key: string): ISetting | null { + getPreference(key: string): ISetting | undefined { for (const group of this.settingsGroups) { for (const section of group.sections) { for (const setting of section.settings) { @@ -858,7 +859,7 @@ export class DefaultSettingsEditorModel extends AbstractSettingsModel implements } } } - return null; + return undefined; } private getGroup(resultGroup: ISearchResultGroup): ISettingsGroup { diff --git a/src/vs/workbench/services/remote/node/remoteAgentService.ts b/src/vs/workbench/services/remote/common/remoteAgentService.ts similarity index 60% rename from src/vs/workbench/services/remote/node/remoteAgentService.ts rename to src/vs/workbench/services/remote/common/remoteAgentService.ts index 703bee3f1d1..a56d0164944 100644 --- a/src/vs/workbench/services/remote/node/remoteAgentService.ts +++ b/src/vs/workbench/services/remote/common/remoteAgentService.ts @@ -3,31 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { OperatingSystem } from 'vs/base/common/platform'; -import { URI } from 'vs/base/common/uri'; -import { IChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; -import { RemoteAgentConnectionContext } from 'vs/platform/remote/node/remoteAgentConnection'; +import { IRemoteAgentEnvironment, RemoteAgentConnectionContext } from 'vs/platform/remote/common/remoteAgentEnvironment'; +import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; export const RemoteExtensionLogFileName = 'remoteagent'; export const IRemoteAgentService = createDecorator('remoteAgentService'); -export interface IRemoteAgentEnvironment { - pid: number; - appRoot: URI; - appSettingsHome: URI; - logsPath: URI; - extensionsPath: URI; - extensionHostLogsPath: URI; - globalStorageHome: URI; - userHome: URI; - extensions: IExtensionDescription[]; - os: OperatingSystem; - syncExtensions: boolean; -} - export interface IRemoteAgentService { _serviceBrand: any; diff --git a/src/vs/workbench/services/remote/common/remoteEnvironmentService.ts b/src/vs/workbench/services/remote/common/remoteEnvironmentService.ts new file mode 100644 index 00000000000..d18662d1d01 --- /dev/null +++ b/src/vs/workbench/services/remote/common/remoteEnvironmentService.ts @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; +import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; + +export interface IRemoteEnvironmentService { + _serviceBrand: any; + remoteEnvironment: Promise; +} + +export const IRemoteEnvironmentService = createDecorator('remoteEnvironmentService'); + +export class RemoteEnvironmentService implements IRemoteEnvironmentService { + _serviceBrand: any; + + constructor( + @IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService, + ) { } + + get remoteEnvironment(): Promise { + const connection = this.remoteAgentService.getConnection(); + if (connection) { + return connection.getEnvironment(); + } + return Promise.resolve(null); + } +} + +registerSingleton(IRemoteEnvironmentService, RemoteEnvironmentService, true); \ No newline at end of file diff --git a/src/vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl.ts b/src/vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl.ts index d2c58a65c2a..96054541715 100644 --- a/src/vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl.ts +++ b/src/vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl.ts @@ -5,14 +5,14 @@ import { localize } from 'vs/nls'; import { Disposable } from 'vs/base/common/lifecycle'; -import { IChannel, getDelayedChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc'; +import { getDelayedChannel } from 'vs/base/parts/ipc/node/ipc'; import { Client } from 'vs/base/parts/ipc/node/ipc.net'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { connectRemoteAgentManagement, RemoteAgentConnectionContext } from 'vs/platform/remote/node/remoteAgentConnection'; +import { connectRemoteAgentManagement } from 'vs/platform/remote/node/remoteAgentConnection'; import { IWindowService } from 'vs/platform/windows/common/windows'; import { RemoteExtensionEnvironmentChannelClient } from 'vs/workbench/services/remote/node/remoteAgentEnvironmentChannel'; -import { IRemoteAgentConnection, IRemoteAgentEnvironment, IRemoteAgentService } from 'vs/workbench/services/remote/node/remoteAgentService'; +import { IRemoteAgentConnection, IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; @@ -21,6 +21,8 @@ import { DownloadServiceChannel } from 'vs/platform/download/node/downloadIpc'; import { LogLevelSetterChannel } from 'vs/platform/log/node/logIpc'; import { ILogService } from 'vs/platform/log/common/log'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IRemoteAgentEnvironment, RemoteAgentConnectionContext } from 'vs/platform/remote/common/remoteAgentEnvironment'; +import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; export class RemoteAgentService implements IRemoteAgentService { diff --git a/src/vs/workbench/services/remote/node/remoteAgentEnvironmentChannel.ts b/src/vs/workbench/services/remote/node/remoteAgentEnvironmentChannel.ts index 83a9d2f21ad..f81978a3393 100644 --- a/src/vs/workbench/services/remote/node/remoteAgentEnvironmentChannel.ts +++ b/src/vs/workbench/services/remote/node/remoteAgentEnvironmentChannel.ts @@ -5,9 +5,9 @@ import * as platform from 'vs/base/common/platform'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { IChannel } from 'vs/base/parts/ipc/node/ipc'; -import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; -import { IRemoteAgentEnvironment } from 'vs/workbench/services/remote/node/remoteAgentService'; +import { IChannel } from 'vs/base/parts/ipc/common/ipc'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; export interface IGetEnvironmentDataArguments { language: string; diff --git a/src/vs/workbench/services/search/node/rawSearchService.ts b/src/vs/workbench/services/search/node/rawSearchService.ts index 26a01c678ea..7c6bf16feb8 100644 --- a/src/vs/workbench/services/search/node/rawSearchService.ts +++ b/src/vs/workbench/services/search/node/rawSearchService.ts @@ -16,7 +16,7 @@ import { StopWatch } from 'vs/base/common/stopwatch'; import * as strings from 'vs/base/common/strings'; import { URI, UriComponents } from 'vs/base/common/uri'; import { compareItemsByScore, IItemAccessor, prepareQuery, ScorerCache } from 'vs/base/parts/quickopen/common/quickOpenScorer'; -import { MAX_FILE_SIZE } from 'vs/platform/files/node/files'; +import { MAX_FILE_SIZE } from 'vs/platform/files/node/fileConstants'; import { ICachedSearchStats, IFileQuery, IFileSearchStats, IFolderQuery, IProgress, IRawFileQuery, IRawQuery, IRawTextQuery, ITextQuery, IFileSearchProgressItem, IRawFileMatch, IRawSearchService, ISearchEngine, ISearchEngineSuccess, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, ISerializedSearchSuccess } from 'vs/workbench/services/search/common/search'; import { Engine as FileSearchEngine } from 'vs/workbench/services/search/node/fileSearch'; import { TextSearchEngineAdapter } from 'vs/workbench/services/search/node/textSearchAdapter'; diff --git a/src/vs/workbench/services/search/node/searchIpc.ts b/src/vs/workbench/services/search/node/searchIpc.ts index 6c22c60cb37..c9e6f859ac2 100644 --- a/src/vs/workbench/services/search/node/searchIpc.ts +++ b/src/vs/workbench/services/search/node/searchIpc.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Event } from 'vs/base/common/event'; -import { IChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; import { IRawFileQuery, IRawTextQuery, IRawSearchService, ISerializedSearchComplete, ISerializedSearchProgressItem } from 'vs/workbench/services/search/common/search'; export class SearchChannel implements IServerChannel { diff --git a/src/vs/workbench/services/textMate/electron-browser/textMateService.ts b/src/vs/workbench/services/textMate/electron-browser/textMateService.ts index de5333bf4a8..0fd4d880811 100644 --- a/src/vs/workbench/services/textMate/electron-browser/textMateService.ts +++ b/src/vs/workbench/services/textMate/electron-browser/textMateService.ts @@ -25,7 +25,6 @@ import { ITextMateService } from 'vs/workbench/services/textMate/common/textMate import { ITokenColorizationRule, IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { IEmbeddedLanguagesMap as IEmbeddedLanguagesMap2, IGrammar, ITokenTypeMap, Registry, StackElement, StandardTokenType } from 'vscode-textmate'; import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { ConfigurationService } from 'vs/platform/configuration/node/configurationService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; @@ -162,7 +161,7 @@ export class TextMateService extends Disposable implements ITextMateService { @IFileService private readonly _fileService: IFileService, @INotificationService private readonly _notificationService: INotificationService, @ILogService private readonly _logService: ILogService, - @IConfigurationService private readonly _configurationService: ConfigurationService + @IConfigurationService private readonly _configurationService: IConfigurationService ) { super(); this._styleElement = dom.createStyleSheet(); diff --git a/src/vs/workbench/services/textfile/node/textResourcePropertiesService.ts b/src/vs/workbench/services/textfile/node/textResourcePropertiesService.ts index 04f67a7f113..3739f5a3736 100644 --- a/src/vs/workbench/services/textfile/node/textResourcePropertiesService.ts +++ b/src/vs/workbench/services/textfile/node/textResourcePropertiesService.ts @@ -7,11 +7,12 @@ import { URI } from 'vs/base/common/uri'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ITextResourcePropertiesService } from 'vs/editor/common/services/resourceConfiguration'; import { OperatingSystem, OS } from 'vs/base/common/platform'; -import { IRemoteAgentService, IRemoteAgentEnvironment } from 'vs/workbench/services/remote/node/remoteAgentService'; +import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { Schemas } from 'vs/base/common/network'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IWindowService } from 'vs/platform/windows/common/windows'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; export class TextResourcePropertiesService implements ITextResourcePropertiesService { diff --git a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts index a253f443813..f1e8e10654e 100644 --- a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts @@ -13,7 +13,6 @@ import { Registry } from 'vs/platform/registry/common/platform'; import * as errors from 'vs/base/common/errors'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions, IConfigurationPropertySchema, IConfigurationNode } from 'vs/platform/configuration/common/configurationRegistry'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ColorThemeData } from './colorThemeData'; import { ITheme, Extensions as ThemingExtensions, IThemingRegistry } from 'vs/platform/theme/common/themeService'; import { Event, Emitter } from 'vs/base/common/event'; @@ -71,8 +70,6 @@ export interface IColorCustomizations { export class WorkbenchThemeService implements IWorkbenchThemeService { _serviceBrand: any; - private fileService: IFileService; - private colorThemeStore: ColorThemeStore; private currentColorTheme: ColorThemeData; private container: HTMLElement; @@ -85,7 +82,6 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { private watchedIconThemeLocation: URI | undefined; private themingParticipantChangeListener: IDisposable; - private _configurationWriter: ConfigurationWriter; private get colorCustomizations(): IColorCustomizations { return this.configurationService.getValue(CUSTOM_WORKBENCH_COLORS_SETTING) || {}; @@ -101,8 +97,8 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { @IConfigurationService private readonly configurationService: IConfigurationService, @ITelemetryService private readonly telemetryService: ITelemetryService, @IWindowService private readonly windowService: IWindowService, - @IInstantiationService private readonly instantiationService: IInstantiationService, - @IEnvironmentService private readonly environmentService: IEnvironmentService + @IEnvironmentService private readonly environmentService: IEnvironmentService, + @IFileService private readonly fileService: IFileService ) { this.container = document.body; @@ -207,10 +203,6 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { } } }); - } - - acquireFileService(fileService: IFileService): void { - this.fileService = fileService; this.fileService.onFileChanges(async e => { if (this.watchedColorThemeLocation && this.currentColorTheme && e.contains(this.watchedColorThemeLocation, FileChangeType.UPDATED)) { @@ -429,7 +421,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { private writeColorThemeConfiguration(settingsTarget: ConfigurationTarget | undefined | 'auto'): Promise { if (!types.isUndefinedOrNull(settingsTarget)) { - return this.configurationWriter.writeConfiguration(COLOR_THEME_SETTING, this.currentColorTheme.settingsId, settingsTarget).then(_ => this.currentColorTheme); + return this.writeConfiguration(COLOR_THEME_SETTING, this.currentColorTheme.settingsId, settingsTarget).then(_ => this.currentColorTheme); } return Promise.resolve(this.currentColorTheme); } @@ -538,17 +530,38 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { private writeFileIconConfiguration(settingsTarget: ConfigurationTarget | undefined | 'auto'): Promise { if (!types.isUndefinedOrNull(settingsTarget)) { - return this.configurationWriter.writeConfiguration(ICON_THEME_SETTING, this.currentIconTheme.settingsId, settingsTarget).then(_ => this.currentIconTheme); + return this.writeConfiguration(ICON_THEME_SETTING, this.currentIconTheme.settingsId, settingsTarget).then(_ => this.currentIconTheme); } return Promise.resolve(this.currentIconTheme); } - private get configurationWriter(): ConfigurationWriter { - // separate out the ConfigurationWriter to avoid a dependency of the IConfigurationEditingService - if (!this._configurationWriter) { - this._configurationWriter = this.instantiationService.createInstance(ConfigurationWriter); + public writeConfiguration(key: string, value: any, settingsTarget: ConfigurationTarget | 'auto'): Promise { + let settings = this.configurationService.inspect(key); + if (settingsTarget === 'auto') { + if (!types.isUndefined(settings.workspaceFolder)) { + settingsTarget = ConfigurationTarget.WORKSPACE_FOLDER; + } else if (!types.isUndefined(settings.workspace)) { + settingsTarget = ConfigurationTarget.WORKSPACE; + } else { + settingsTarget = ConfigurationTarget.USER; + } } - return this._configurationWriter; + + if (settingsTarget === ConfigurationTarget.USER) { + if (value === settings.user) { + return Promise.resolve(undefined); // nothing to do + } else if (value === settings.default) { + if (types.isUndefined(settings.user)) { + return Promise.resolve(undefined); // nothing to do + } + value = undefined; // remove configuration from user settings + } + } else if (settingsTarget === ConfigurationTarget.WORKSPACE || settingsTarget === ConfigurationTarget.WORKSPACE_FOLDER) { + if (value === settings.value) { + return Promise.resolve(undefined); // nothing to do + } + } + return this.configurationService.updateValue(key, value, settingsTarget); } private getBaseThemeFromContainer() { @@ -585,40 +598,6 @@ function _applyRules(styleSheetContent: string, rulesClassName: string) { registerColorThemeSchemas(); registerFileIconThemeSchemas(); -class ConfigurationWriter { - constructor(@IConfigurationService private readonly configurationService: IConfigurationService) { - } - - public writeConfiguration(key: string, value: any, settingsTarget: ConfigurationTarget | 'auto'): Promise { - let settings = this.configurationService.inspect(key); - if (settingsTarget === 'auto') { - if (!types.isUndefined(settings.workspaceFolder)) { - settingsTarget = ConfigurationTarget.WORKSPACE_FOLDER; - } else if (!types.isUndefined(settings.workspace)) { - settingsTarget = ConfigurationTarget.WORKSPACE; - } else { - settingsTarget = ConfigurationTarget.USER; - } - } - - if (settingsTarget === ConfigurationTarget.USER) { - if (value === settings.user) { - return Promise.resolve(undefined); // nothing to do - } else if (value === settings.default) { - if (types.isUndefined(settings.user)) { - return Promise.resolve(undefined); // nothing to do - } - value = undefined; // remove configuration from user settings - } - } else if (settingsTarget === ConfigurationTarget.WORKSPACE || settingsTarget === ConfigurationTarget.WORKSPACE_FOLDER) { - if (value === settings.value) { - return Promise.resolve(undefined); // nothing to do - } - } - return this.configurationService.updateValue(key, value, settingsTarget); - } -} - // Configuration: Themes const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); diff --git a/src/vs/workbench/services/workspace/node/workspaceEditingService.ts b/src/vs/workbench/services/workspace/node/workspaceEditingService.ts index bb495c6ea73..ec5cc2da056 100644 --- a/src/vs/workbench/services/workspace/node/workspaceEditingService.ts +++ b/src/vs/workbench/services/workspace/node/workspaceEditingService.ts @@ -40,7 +40,7 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { @IJSONEditingService private readonly jsonEditingService: IJSONEditingService, @IWorkspaceContextService private readonly contextService: WorkspaceService, @IWindowService private readonly windowService: IWindowService, - @IConfigurationService private readonly workspaceConfigurationService: IConfigurationService, + @IConfigurationService private readonly configurationService: IConfigurationService, @IStorageService private readonly storageService: IStorageService, @IExtensionService private readonly extensionService: IExtensionService, @IBackupFileService private readonly backupFileService: IBackupFileService, @@ -411,13 +411,13 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { private doCopyWorkspaceSettings(toWorkspace: IWorkspaceIdentifier, filter?: (config: IConfigurationPropertySchema) => boolean): Promise { const configurationProperties = Registry.as(ConfigurationExtensions.Configuration).getConfigurationProperties(); const targetWorkspaceConfiguration = {}; - for (const key of this.workspaceConfigurationService.keys().workspace) { + for (const key of this.configurationService.keys().workspace) { if (configurationProperties[key]) { if (filter && !filter(configurationProperties[key])) { continue; } - targetWorkspaceConfiguration[key] = this.workspaceConfigurationService.inspect(key).workspace; + targetWorkspaceConfiguration[key] = this.configurationService.inspect(key).workspace; } } diff --git a/src/vs/workbench/test/electron-browser/api/extHostDocumentSaveParticipant.test.ts b/src/vs/workbench/test/electron-browser/api/extHostDocumentSaveParticipant.test.ts index e0cc18a3d78..f9a0daec3f3 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostDocumentSaveParticipant.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostDocumentSaveParticipant.test.ts @@ -13,11 +13,10 @@ import { SingleProxyRPCProtocol } from './testRPCProtocol'; import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles'; import * as vscode from 'vscode'; import { mock } from 'vs/workbench/test/electron-browser/api/mock'; -import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import { NullLogService } from 'vs/platform/log/common/log'; import { isResourceTextEdit, ResourceTextEdit } from 'vs/editor/common/modes'; import { timeout } from 'vs/base/common/async'; -import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; suite('ExtHostDocumentSaveParticipant', () => { diff --git a/src/vs/workbench/test/electron-browser/api/extHostTreeViews.test.ts b/src/vs/workbench/test/electron-browser/api/extHostTreeViews.test.ts index 7e626d3985b..ab0f857e459 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostTreeViews.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostTreeViews.test.ts @@ -18,7 +18,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { mock } from 'vs/workbench/test/electron-browser/api/mock'; import { TreeItemCollapsibleState, ITreeItem } from 'vs/workbench/common/views'; import { NullLogService } from 'vs/platform/log/common/log'; -import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; suite('ExtHostTreeView', function () { 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 5974b4713f5..47e576f6f26 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostWorkspace.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostWorkspace.test.ts @@ -8,14 +8,13 @@ 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 { ExtensionIdentifier, IExtensionDescription } 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'; diff --git a/src/vs/workbench/workbench.main.ts b/src/vs/workbench/workbench.main.ts index 4ab6635add5..ae6535134ad 100644 --- a/src/vs/workbench/workbench.main.ts +++ b/src/vs/workbench/workbench.main.ts @@ -108,9 +108,10 @@ 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/configuration/common/jsonEditingService'; import 'vs/workbench/services/textmodelResolver/common/textModelResolverService'; import 'vs/workbench/services/textfile/common/textFileService'; +import 'vs/workbench/services/dialogs/browser/fileDialogService'; import 'vs/workbench/services/dialogs/electron-browser/dialogService'; import 'vs/workbench/services/backup/node/backupFileService'; import 'vs/workbench/services/editor/browser/editorService'; @@ -131,6 +132,7 @@ import 'vs/workbench/services/label/common/labelService'; import 'vs/workbench/services/extensions/electron-browser/extensionManagementServerService'; import 'vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl'; import 'vs/workbench/services/notification/common/notificationService'; +import 'vs/workbench/services/remote/common/remoteEnvironmentService'; registerSingleton(IMenuService, MenuService, true); registerSingleton(IListService, ListService, true); @@ -220,7 +222,8 @@ import 'vs/workbench/contrib/scm/browser/scmViewlet'; // Debug import 'vs/workbench/contrib/debug/electron-browser/debug.contribution'; import 'vs/workbench/contrib/debug/browser/debugQuickOpen'; -import 'vs/workbench/contrib/debug/electron-browser/repl'; +import 'vs/workbench/contrib/debug/browser/debugEditorContribution'; +import 'vs/workbench/contrib/debug/browser/repl'; import 'vs/workbench/contrib/debug/browser/debugViewlet'; // Markers diff --git a/src/vs/workbench/workbench.nodeless.main.ts b/src/vs/workbench/workbench.nodeless.main.ts index b1f93878f58..22034ba3843 100644 --- a/src/vs/workbench/workbench.nodeless.main.ts +++ b/src/vs/workbench/workbench.nodeless.main.ts @@ -111,9 +111,10 @@ 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/common/outputChannelModelService'; -// import 'vs/workbench/services/configuration/node/jsonEditingService'; +import 'vs/workbench/services/configuration/common/jsonEditingService'; import 'vs/workbench/services/textmodelResolver/common/textModelResolverService'; import 'vs/workbench/services/textfile/common/textFileService'; +import 'vs/workbench/services/dialogs/browser/fileDialogService'; // import 'vs/workbench/services/dialogs/electron-browser/dialogService'; // import 'vs/workbench/services/backup/node/backupFileService'; import 'vs/workbench/services/editor/browser/editorService'; @@ -134,6 +135,8 @@ import 'vs/workbench/services/label/common/labelService'; // import 'vs/workbench/services/extensions/electron-browser/extensionManagementServerService'; // import 'vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl'; import 'vs/workbench/services/notification/common/notificationService'; +import 'vs/workbench/services/remote/common/remoteEnvironmentService'; + registerSingleton(IMenuService, MenuService, true); registerSingleton(IListService, ListService, true); @@ -225,7 +228,8 @@ import 'vs/workbench/contrib/scm/browser/scmViewlet'; // Debug // import 'vs/workbench/contrib/debug/electron-browser/debug.contribution'; // import 'vs/workbench/contrib/debug/browser/debugQuickOpen'; -// import 'vs/workbench/contrib/debug/electron-browser/repl'; +// import 'vs/workbench/contrib/debug/browser/debugEditorContribution'; +// import 'vs/workbench/contrib/debug/browser/repl'; // import 'vs/workbench/contrib/debug/browser/debugViewlet'; // Markers @@ -308,7 +312,7 @@ import 'vs/workbench/contrib/watermark/browser/watermark'; import 'vs/workbench/contrib/welcome/walkThrough/browser/walkThrough.contribution'; // import 'vs/workbench/contrib/welcome/gettingStarted/electron-browser/gettingStarted.contribution'; import 'vs/workbench/contrib/welcome/overlay/browser/welcomeOverlay'; -import 'vs/workbench/contrib/welcome/page/browser/welcomePage.contribution'; +// import 'vs/workbench/contrib/welcome/page/browser/welcomePage.contribution'; // Outline import 'vs/workbench/contrib/outline/browser/outline.contribution'; diff --git a/test/smoke/README.md b/test/smoke/README.md index 4154212a0f5..57c8ada6587 100644 --- a/test/smoke/README.md +++ b/test/smoke/README.md @@ -9,7 +9,7 @@ Make sure you are on **Node v10.x**. yarn smoketest # Build -yarn smoketest --build PATH_TO_BUILD +yarn smoketest --build PATH_TO_BUILD --stable-build PATH_TO_STABLE_BUILD ``` ### Run for a release @@ -19,7 +19,7 @@ You must always run the smoketest version which matches the release you are test ```bash git checkout release/1.22 yarn -yarn smoketest --build PATH_TO_RELEASE_BUILD +yarn smoketest --build PATH_TO_RELEASE_BUILD --stable-build PATH_TO_STABLE_BUILD ``` ### Debug diff --git a/test/smoke/src/application.ts b/test/smoke/src/application.ts index 63001eb5fc8..76ea2dd24fc 100644 --- a/test/smoke/src/application.ts +++ b/test/smoke/src/application.ts @@ -60,10 +60,13 @@ export class Application { return this.options.userDataDir; } - async start(): Promise { + async start(expectWalkthroughPart = true): Promise { await this._start(); await this.code.waitForElement('.explorer-folders-view'); - await this.code.waitForActiveElement(`.editor-instance[id="workbench.editor.walkThroughPart"] > div > div[tabIndex="0"]`); + + if (expectWalkthroughPart) { + await this.code.waitForActiveElement(`.editor-instance[id="workbench.editor.walkThroughPart"] > div > div[tabIndex="0"]`); + } } async restart(options: { workspaceOrFolder?: string, extraArgs?: string[] }): Promise { diff --git a/test/smoke/src/areas/workbench/data-migration.test.ts b/test/smoke/src/areas/workbench/data-migration.test.ts index f4084ca624b..55c2f74a362 100644 --- a/test/smoke/src/areas/workbench/data-migration.test.ts +++ b/test/smoke/src/areas/workbench/data-migration.test.ts @@ -3,109 +3,93 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Application, Quality } from '../../application'; -import * as rimraf from 'rimraf'; +import { Application, ApplicationOptions } from '../../application'; +import { join } from 'path'; -export interface ICreateAppFn { - (quality: Quality): Application; -} +export function setup(stableCodePath: string, testDataPath: string) { -export function setup(userDataDir: string, createApp: ICreateAppFn) { - describe('Data Migration', () => { + describe('Data Migration: This test MUST run before releasing by providing the --stable-build command line argument', () => { + it(`verifies opened editors are restored`, async function () { + if (!stableCodePath) { + this.skip(); + } - afterEach(async function () { - await new Promise((c, e) => rimraf(userDataDir, { maxBusyTries: 10 }, err => err ? e(err) : c())); + const userDataDir = join(testDataPath, 'd2'); // different data dir from the other tests + + const stableOptions: ApplicationOptions = Object.assign({}, this.defaultOptions); + stableOptions.codePath = stableCodePath; + stableOptions.userDataDir = userDataDir; + + const stableApp = new Application(stableOptions); + await stableApp!.start(); + + // Open 3 editors and pin 2 of them + await stableApp.workbench.quickopen.openFile('www'); + await stableApp.workbench.quickopen.runCommand('View: Keep Editor'); + + await stableApp.workbench.quickopen.openFile('app.js'); + await stableApp.workbench.quickopen.runCommand('View: Keep Editor'); + + await stableApp.workbench.editors.newUntitledFile(); + + await stableApp.stop(); + + const insiderOptions: ApplicationOptions = Object.assign({}, this.defaultOptions); + insiderOptions.userDataDir = userDataDir; + + const insidersApp = new Application(insiderOptions); + await insidersApp!.start(false /* not expecting walkthrough parth */); + + // Verify 3 editors are open + await insidersApp.workbench.editors.waitForEditorFocus('Untitled-1'); + await insidersApp.workbench.editors.selectTab('app.js'); + await insidersApp.workbench.editors.selectTab('www'); + + await insidersApp.stop(); }); - // it('checks if the Untitled file is restored migrating from stable to latest', async function () { - // const stableApp = createApp(Quality.Stable); + it(`verifies that 'hot exit' works for dirty files`, async function () { + if (!stableCodePath) { + this.skip(); + } - // if (!stableApp) { - // this.skip(); - // return; - // } + const userDataDir = join(testDataPath, 'd3'); // different data dir from the other tests - // await stableApp.start(); + const stableOptions: ApplicationOptions = Object.assign({}, this.defaultOptions); + stableOptions.codePath = stableCodePath; + stableOptions.userDataDir = userDataDir; - // const textToType = 'Very dirty file'; + const stableApp = new Application(stableOptions); + await stableApp!.start(); - // await stableApp.workbench.editors.newUntitledFile(); - // await stableApp.workbench.editor.waitForTypeInEditor('Untitled-1', textToType); + await stableApp.workbench.editors.newUntitledFile(); - // await stableApp.stop(); - // await new Promise(c => setTimeout(c, 500)); // wait until all resources are released (e.g. locked local storage) + const untitled = 'Untitled-1'; + const textToTypeInUntitled = 'Hello, Unitled Code'; + await stableApp.workbench.editor.waitForTypeInEditor(untitled, textToTypeInUntitled); - // // Checking latest version for the restored state - // const app = createApp(Quality.Insiders); + const readmeMd = 'readme.md'; + const textToType = 'Hello, Code'; + await stableApp.workbench.quickopen.openFile(readmeMd); + await stableApp.workbench.editor.waitForTypeInEditor(readmeMd, textToType); - // await app.start(false); + await stableApp.stop(); - // await app.workbench.editors.waitForActiveTab('Untitled-1', true); - // await app.workbench.editor.waitForEditorContents('Untitled-1', c => c.indexOf(textToType) > -1); + const insiderOptions: ApplicationOptions = Object.assign({}, this.defaultOptions); + insiderOptions.userDataDir = userDataDir; - // await app.stop(); - // }); + const insidersApp = new Application(insiderOptions); + await insidersApp!.start(false /* not expecting walkthrough parth */); - // it('checks if the newly created dirty file is restored migrating from stable to latest', async function () { - // const stableApp = createApp(Quality.Stable); + await insidersApp.workbench.editors.waitForActiveTab(readmeMd, true); + await insidersApp.workbench.editor.waitForEditorContents(readmeMd, c => c.indexOf(textToType) > -1); - // if (!stableApp) { - // this.skip(); - // return; - // } + await insidersApp.workbench.editors.waitForTab(untitled, true); + await insidersApp.workbench.editors.selectTab(untitled, true); + await insidersApp.workbench.editor.waitForEditorContents(untitled, c => c.indexOf(textToTypeInUntitled) > -1); - // await stableApp.start(); - - // const fileName = 'app.js'; - // const textPart = 'This is going to be an unsaved file'; - - // await stableApp.workbench.quickopen.openFile(fileName); - - // await stableApp.workbench.editor.waitForTypeInEditor(fileName, textPart); - - // await stableApp.stop(); - // await new Promise(c => setTimeout(c, 500)); // wait until all resources are released (e.g. locked local storage) - - // // Checking latest version for the restored state - // const app = createApp(Quality.Insiders); - - // await app.start(false); - - // await app.workbench.editors.waitForActiveTab(fileName); - // await app.workbench.editor.waitForEditorContents(fileName, c => c.indexOf(textPart) > -1); - - // await app.stop(); - // }); - - // it('checks if opened tabs are restored migrating from stable to latest', async function () { - // const stableApp = createApp(Quality.Stable); - - // if (!stableApp) { - // this.skip(); - // return; - // } - - // await stableApp.start(); - - // const fileName1 = 'app.js', fileName2 = 'jsconfig.json', fileName3 = 'readme.md'; - - // await stableApp.workbench.quickopen.openFile(fileName1); - // await stableApp.workbench.runCommand('View: Keep Editor'); - // await stableApp.workbench.quickopen.openFile(fileName2); - // await stableApp.workbench.runCommand('View: Keep Editor'); - // await stableApp.workbench.quickopen.openFile(fileName3); - // await stableApp.stop(); - - // const app = createApp(Quality.Insiders); - - // await app.start(false); - - // await app.workbench.editors.waitForTab(fileName1); - // await app.workbench.editors.waitForTab(fileName2); - // await app.workbench.editors.waitForTab(fileName3); - - // await app.stop(); - // }); + await insidersApp.stop(); + }); }); } \ No newline at end of file diff --git a/test/smoke/src/main.ts b/test/smoke/src/main.ts index 04b49384dc6..238d72af866 100644 --- a/test/smoke/src/main.ts +++ b/test/smoke/src/main.ts @@ -13,7 +13,7 @@ import * as mkdirp from 'mkdirp'; import { ncp } from 'ncp'; import { Application, Quality, ApplicationOptions } from './application'; -// import { setup as setupDataMigrationTests } from './areas/workbench/data-migration.test'; +import { setup as setupDataMigrationTests } from './areas/workbench/data-migration.test'; import { setup as setupDataLossTests } from './areas/workbench/data-loss.test'; import { setup as setupDataExplorerTests } from './areas/explorer/explorer.test'; import { setup as setupDataPreferencesTests } from './areas/preferences/preferences.test'; @@ -113,16 +113,16 @@ function getBuildElectronPath(root: string): string { } let testCodePath = opts.build; -// let stableCodePath = opts['stable-build']; +let stableCodePath = opts['stable-build']; let electronPath: string; -// let stablePath: string; +let stablePath: string | undefined = undefined; if (testCodePath) { electronPath = getBuildElectronPath(testCodePath); - // if (stableCodePath) { - // stablePath = getBuildElectronPath(stableCodePath); - // } + if (stableCodePath) { + stablePath = getBuildElectronPath(stableCodePath); + } } else { testCodePath = getDevElectronPath(); electronPath = testCodePath; @@ -135,6 +135,10 @@ if (!fs.existsSync(electronPath || '')) { fail(`Can't find Code at ${electronPath}.`); } +if (typeof stablePath === 'string' && !fs.existsSync(stablePath)) { + fail(`Can't find Stable Code at ${stablePath}.`); +} + const userDataDir = path.join(testDataPath, 'd'); let quality: Quality; @@ -223,9 +227,7 @@ after(async function () { await new Promise((c, e) => rimraf(testDataPath, { maxBusyTries: 10 }, err => err ? e(err) : c())); }); -// describe('Data Migration', () => { -// setupDataMigrationTests(userDataDir, createApp); -// }); +setupDataMigrationTests(stableCodePath, testDataPath); describe('Running Code', () => { before(async function () {