diff --git a/.github/calendar.yml b/.github/calendar.yml index 3a5dad30159..d8adda455ea 100644 --- a/.github/calendar.yml +++ b/.github/calendar.yml @@ -19,4 +19,5 @@ '2018-05-08 12:00, US/Pacific': 'development', '2018-05-10 12:00, US/Pacific': 'release', # 1.23.1 '2018-05-15 12:00, US/Pacific': 'development', + '2018-05-28 18:00, US/Pacific': 'endgame', } diff --git a/.github/classifier.yml b/.github/classifier.yml index 7fddcd73904..514910d63d2 100644 --- a/.github/classifier.yml +++ b/.github/classifier.yml @@ -10,7 +10,7 @@ color-picker: [], css-less-sass: [ aeschli ], debug: { - assignees: [ roblourens ], + assignees: [ isidorn ], assignLabel: false }, diff-editor: [], diff --git a/extensions/html-language-features/client/src/htmlMain.ts b/extensions/html-language-features/client/src/htmlMain.ts index 31354268112..f463ff2ba8a 100644 --- a/extensions/html-language-features/client/src/htmlMain.ts +++ b/extensions/html-language-features/client/src/htmlMain.ts @@ -187,7 +187,7 @@ export function activate(context: ExtensionContext) { } return void 0; } - return languages.registerFoldingRangeProvider(documentSelector, { + return languages.registerFoldingRangeProvider('html', { provideFoldingRanges(document: TextDocument, context: FoldingContext, token: CancellationToken) { const param: FoldingRangeRequestParam = { textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document) diff --git a/extensions/npm/package.json b/extensions/npm/package.json index a57db1747a0..b86114ae816 100644 --- a/extensions/npm/package.json +++ b/extensions/npm/package.json @@ -63,6 +63,10 @@ "command": "npm.openScript", "title": "%command.openScript%" }, + { + "command": "npm.runInstall", + "title": "%command.runInstall%" + }, { "command": "npm.refresh", "title": "%command.refresh%", @@ -84,9 +88,13 @@ { "command": "npm.openScript", "when": "view == npm && viewItem == packageJSON", - "group": "navigation" + "group": "navigation@1" }, { + "command": "npm.runInstall", + "when": "view == npm && viewItem == packageJSON", + "group": "navigation@2" + }, { "command": "npm.openScript", "when": "view == npm && viewItem == script", "group": "navigation@1" diff --git a/extensions/npm/package.nls.json b/extensions/npm/package.nls.json index dedd7af6161..ccc6aa3114c 100644 --- a/extensions/npm/package.nls.json +++ b/extensions/npm/package.nls.json @@ -14,5 +14,6 @@ "command.refresh": "Refresh", "command.run": "Run", "command.debug": "Debug", - "command.openScript": "Open" + "command.openScript": "Open", + "command.runInstall": "Run Install" } diff --git a/extensions/npm/src/npmView.ts b/extensions/npm/src/npmView.ts index db2e086d50a..7fbbd73e7a0 100644 --- a/extensions/npm/src/npmView.ts +++ b/extensions/npm/src/npmView.ts @@ -11,7 +11,10 @@ import { WorkspaceFolder, commands, debug, window, workspace, Selection, TaskGroup } from 'vscode'; import { visit, JSONVisitor } from 'jsonc-parser'; -import { NpmTaskDefinition, getPackageJsonUriFromTask, getScripts, isWorkspaceFolder, getPackageManager, getTaskName } from './tasks'; +import { + NpmTaskDefinition, getPackageJsonUriFromTask, getScripts, + isWorkspaceFolder, getPackageManager, getTaskName, createTask +} from './tasks'; import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); @@ -133,6 +136,7 @@ export class NpmScriptsTreeDataProvider implements TreeDataProvider { subscriptions.push(commands.registerCommand('npm.debugScript', this.debugScript, this)); subscriptions.push(commands.registerCommand('npm.openScript', this.openScript, this)); subscriptions.push(commands.registerCommand('npm.refresh', this.refresh, this)); + subscriptions.push(commands.registerCommand('npm.runInstall', this.runInstall, this)); } private scriptIsValid(scripts: any, task: Task): boolean { @@ -258,6 +262,19 @@ export class NpmScriptsTreeDataProvider implements TreeDataProvider { return scriptOffset; } + + private async runInstall(selection: PackageJSON) { + let uri: Uri | undefined = undefined; + if (selection instanceof PackageJSON) { + uri = selection.resourceUri; + } + if (!uri) { + return; + } + let task = createTask('install', 'install', selection.folder.workspaceFolder, uri, []); + workspace.executeTask(task); + } + private async openScript(selection: PackageJSON | NpmScript) { let uri: Uri | undefined = undefined; if (selection instanceof PackageJSON) { diff --git a/extensions/npm/src/tasks.ts b/extensions/npm/src/tasks.ts index 0b99a008fea..63cd80a1409 100644 --- a/extensions/npm/src/tasks.ts +++ b/extensions/npm/src/tasks.ts @@ -205,7 +205,7 @@ export function getTaskName(script: string, relativePath: string | undefined) { return script; } -function createTask(script: string, cmd: string, folder: WorkspaceFolder, packageJsonUri: Uri, matcher?: any): Task { +export function createTask(script: string, cmd: string, folder: WorkspaceFolder, packageJsonUri: Uri, matcher?: any): Task { function getCommandLine(folder: WorkspaceFolder, cmd: string): string { let packageManager = getPackageManager(folder); diff --git a/src/vs/base/test/node/id.test.ts b/src/vs/base/test/node/id.test.ts index 0cddcf54249..85a58de60ff 100644 --- a/src/vs/base/test/node/id.test.ts +++ b/src/vs/base/test/node/id.test.ts @@ -5,13 +5,22 @@ 'use strict'; import * as assert from 'assert'; +import * as getmac from 'getmac'; import { getMachineId } from 'vs/base/node/id'; suite('ID', () => { test('getMachineId', function () { return getMachineId().then(id => { - assert.ok(/^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/.test(id), `Expected a MAC address: ${id}`); + assert.ok(id); + }); + }); + + test('getMac', function () { + return new Promise((resolve, reject) => { + getmac.getMac((err, macAddress) => err ? reject(err) : resolve(macAddress)); + }).then(macAddress => { + assert.ok(/^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/.test(macAddress), `Expected a MAC address, got: ${macAddress}`); }); }); }); \ No newline at end of file diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts index 35ad7ff531b..5f8337eb5a9 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimap.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts @@ -913,6 +913,11 @@ export class Minimap extends ViewPart { minimapCharRenderer.x1BlockRenderChar(target, dx, dy, tokenColor, backgroundColor, useLighterFont); } dx += charWidth; + + if (dx > maxDx) { + // hit edge of minimap + return; + } } } } diff --git a/src/vs/editor/common/config/commonEditorConfig.ts b/src/vs/editor/common/config/commonEditorConfig.ts index a5d84b4f7be..62f27b14835 100644 --- a/src/vs/editor/common/config/commonEditorConfig.ts +++ b/src/vs/editor/common/config/commonEditorConfig.ts @@ -353,9 +353,9 @@ const editorConfiguration: IConfigurationNode = { }, 'editor.wrappingIndent': { 'type': 'string', - 'enum': ['none', 'same', 'indent'], + 'enum': ['none', 'same', 'indent', 'deepIndent'], 'default': 'same', - 'description': nls.localize('wrappingIndent', "Controls the indentation of wrapped lines. Can be one of 'none', 'same' or 'indent'.") + 'description': nls.localize('wrappingIndent', "Controls the indentation of wrapped lines. Can be one of 'none', 'same', 'indent' or 'deepIndent'.") }, 'editor.mouseWheelScrollSensitivity': { 'type': 'number', diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index c7ea7ee336c..af780797a47 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -340,7 +340,7 @@ export interface IEditorOptions { */ wordWrapMinified?: boolean; /** - * Control indentation of wrapped lines. Can be: 'none', 'same' or 'indent'. + * Control indentation of wrapped lines. Can be: 'none', 'same', 'indent' or 'deepIndent'. * Defaults to 'same' in vscode and to 'none' in monaco-editor. */ wrappingIndent?: string; @@ -635,9 +635,13 @@ export enum WrappingIndent { */ Same = 1, /** - * Indent => wrapped lines get +1 indentation as the parent. + * Indent => wrapped lines get +1 indentation toward the parent. */ - Indent = 2 + Indent = 2, + /** + * DeepIndent => wrapped lines get +2 indentation toward the parent. + */ + DeepIndent = 3 } /** @@ -1477,10 +1481,12 @@ function _wrappingIndentFromString(wrappingIndent: string, defaultValue: Wrappin if (typeof wrappingIndent !== 'string') { return defaultValue; } - if (wrappingIndent === 'indent') { - return WrappingIndent.Indent; - } else if (wrappingIndent === 'same') { + if (wrappingIndent === 'same') { return WrappingIndent.Same; + } else if (wrappingIndent === 'indent') { + return WrappingIndent.Indent; + } else if (wrappingIndent === 'deepIndent') { + return WrappingIndent.DeepIndent; } else { return WrappingIndent.None; } diff --git a/src/vs/editor/common/modes/supports/tokenization.ts b/src/vs/editor/common/modes/supports/tokenization.ts index a155e27cf56..85685d0ff41 100644 --- a/src/vs/editor/common/modes/supports/tokenization.ts +++ b/src/vs/editor/common/modes/supports/tokenization.ts @@ -391,6 +391,6 @@ export function generateTokensCSSForColorMap(colorMap: Color[]): string { } rules.push('.mtki { font-style: italic; }'); rules.push('.mtkb { font-weight: bold; }'); - rules.push('.mtku { text-decoration: underline; }'); + rules.push('.mtku { text-decoration: underline; text-underline-position: under; }'); return rules.join('\n'); } diff --git a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts index b3e04d5a0af..fffd50708af 100644 --- a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts +++ b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts @@ -92,14 +92,24 @@ export class CharacterHardWrappingLineMapperFactory implements ILineMapperFactor if (hardWrappingIndent !== WrappingIndent.None) { firstNonWhitespaceIndex = strings.firstNonWhitespaceIndex(lineText); if (firstNonWhitespaceIndex !== -1) { + // Track existing indent wrappedTextIndent = lineText.substring(0, firstNonWhitespaceIndex); for (let i = 0; i < firstNonWhitespaceIndex; i++) { wrappedTextIndentVisibleColumn = CharacterHardWrappingLineMapperFactory.nextVisibleColumn(wrappedTextIndentVisibleColumn, tabSize, lineText.charCodeAt(i) === CharCode.Tab, 1); } + + // Increase indent of continuation lines, if desired + let numberOfAdditionalTabs = 0; if (hardWrappingIndent === WrappingIndent.Indent) { + numberOfAdditionalTabs = 1; + } else if (hardWrappingIndent === WrappingIndent.DeepIndent) { + numberOfAdditionalTabs = 2; + } + for (let i = 0; i < numberOfAdditionalTabs; i++) { wrappedTextIndent += '\t'; wrappedTextIndentVisibleColumn = CharacterHardWrappingLineMapperFactory.nextVisibleColumn(wrappedTextIndentVisibleColumn, tabSize, true, 1); } + // Force sticking to beginning of line if no character would fit except for the indentation if (wrappedTextIndentVisibleColumn + columnsForFullWidthChar > breakingColumn) { wrappedTextIndent = ''; diff --git a/src/vs/editor/contrib/codeAction/codeActionWidget.ts b/src/vs/editor/contrib/codeAction/codeActionWidget.ts index d65b389d1ca..0020eca69a4 100644 --- a/src/vs/editor/contrib/codeAction/codeActionWidget.ts +++ b/src/vs/editor/contrib/codeAction/codeActionWidget.ts @@ -54,7 +54,10 @@ export class CodeActionContextMenu { return at; }, getActions: () => actions, - onHide: () => { this._visible = false; }, + onHide: () => { + this._visible = false; + this._editor.focus(); + }, autoSelectFirstItem: true }); } diff --git a/src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts b/src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts index fb9c6978e74..ccfe8d1dce1 100644 --- a/src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts +++ b/src/vs/editor/test/common/viewModel/characterHardWrappingLineMapper.test.ts @@ -10,7 +10,7 @@ import { CharacterHardWrappingLineMapperFactory } from 'vs/editor/common/viewMod import { ILineMapperFactory, ILineMapping } from 'vs/editor/common/viewModel/splitLinesCollection'; function assertLineMapping(factory: ILineMapperFactory, tabSize: number, breakAfter: number, annotatedText: string, wrappingIndent = WrappingIndent.None): ILineMapping { - + // Create version of `annotatedText` with line break markers removed let rawText = ''; let currentLineIndex = 0; let lineIndices: number[] = []; @@ -25,6 +25,7 @@ function assertLineMapping(factory: ILineMapperFactory, tabSize: number, breakAf let mapper = factory.createLineMapping(rawText, tabSize, breakAfter, 2, wrappingIndent); + // Insert line break markers again, according to algorithm let actualAnnotatedText = ''; if (mapper) { let previousLineIndex = 0; @@ -114,4 +115,10 @@ suite('Editor ViewModel - CharacterHardWrappingLineMapper', () => { let mapper = assertLineMapping(factory, 4, 24, ' t h i s |i s |a l |o n |g l |i n |e', WrappingIndent.Indent); assert.equal(mapper.getWrappedLinesIndent(), ' \t'); }); + + test('CharacterHardWrappingLineMapper - WrappingIndent.DeepIndent', () => { + let factory = new CharacterHardWrappingLineMapperFactory('', ' ', ''); + let mapper = assertLineMapping(factory, 4, 26, ' W e A r e T e s t |i n g D e |e p I n d |e n t a t |i o n', WrappingIndent.DeepIndent); + assert.equal(mapper.getWrappedLinesIndent(), ' \t\t'); + }); }); diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 71a6444b1a4..2b42db1f6e9 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -2679,7 +2679,7 @@ declare namespace monaco.editor { */ wordWrapMinified?: boolean; /** - * Control indentation of wrapped lines. Can be: 'none', 'same' or 'indent'. + * Control indentation of wrapped lines. Can be: 'none', 'same', 'indent' or 'deepIndent'. * Defaults to 'same' in vscode and to 'none' in monaco-editor. */ wrappingIndent?: string; @@ -2977,9 +2977,13 @@ declare namespace monaco.editor { */ Same = 1, /** - * Indent => wrapped lines get +1 indentation as the parent. + * Indent => wrapped lines get +1 indentation toward the parent. */ Indent = 2, + /** + * DeepIndent => wrapped lines get +2 indentation toward the parent. + */ + DeepIndent = 3, } /** diff --git a/src/vs/platform/extensionManagement/common/extensionManagement.ts b/src/vs/platform/extensionManagement/common/extensionManagement.ts index 8a6261c70e6..27024e977b9 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagement.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagement.ts @@ -149,6 +149,7 @@ export interface IGalleryExtensionAssets { icon: IGalleryExtensionAsset; license: IGalleryExtensionAsset; repository: IGalleryExtensionAsset; + coreTranslations: { [languageId: string]: IGalleryExtensionAsset }; } export function isIExtensionIdentifier(thing: any): thing is IExtensionIdentifier { @@ -262,6 +263,7 @@ export interface IExtensionGalleryService { getReadme(extension: IGalleryExtension): TPromise; getManifest(extension: IGalleryExtension): TPromise; getChangelog(extension: IGalleryExtension): TPromise; + getCoreTranslations(extension: IGalleryExtension, languageId: string): TPromise<{}>; loadCompatibleVersion(extension: IGalleryExtension): TPromise; loadAllDependencies(dependencies: IExtensionIdentifier[]): TPromise; getExtensionsReport(): TPromise; diff --git a/src/vs/platform/extensionManagement/node/extensionGalleryService.ts b/src/vs/platform/extensionManagement/node/extensionGalleryService.ts index b212007836e..377c1e25bb9 100644 --- a/src/vs/platform/extensionManagement/node/extensionGalleryService.ts +++ b/src/vs/platform/extensionManagement/node/extensionGalleryService.ts @@ -201,6 +201,15 @@ function getStatistic(statistics: IRawGalleryExtensionStatistics[], name: string return result ? result.value : 0; } +function getCoreTranslationAssets(version: IRawGalleryExtensionVersion): { [languageId: string]: IGalleryExtensionAsset } { + const coreTranslationAssetPrefix = 'Microsoft.VisualStudio.Code.Translation.'; + const result = version.files.filter(f => f.assetType.indexOf(coreTranslationAssetPrefix) === 0); + return result.reduce((result, file) => { + result[file.assetType.substring(coreTranslationAssetPrefix.length)] = getVersionAsset(version, file.assetType); + return result; + }, {}); +} + function getVersionAsset(version: IRawGalleryExtensionVersion, type: string): IGalleryExtensionAsset { const result = version.files.filter(f => f.assetType === type)[0]; @@ -278,6 +287,7 @@ function toExtension(galleryExtension: IRawGalleryExtension, extensionsGalleryUr icon: getVersionAsset(version, AssetType.Icon), license: getVersionAsset(version, AssetType.License), repository: getVersionAsset(version, AssetType.Repository), + coreTranslations: getCoreTranslationAssets(version) }; return { @@ -516,6 +526,16 @@ export class ExtensionGalleryService implements IExtensionGalleryService { .then(JSON.parse); } + getCoreTranslations(extension: IGalleryExtension, languageId: string): TPromise<{}> { + const asset = extension.assets.coreTranslations[languageId.toUpperCase()]; + if (asset) { + return this.getAsset(asset) + .then(asText) + .then(JSON.parse); + } + return TPromise.as(null); + } + getChangelog(extension: IGalleryExtension): TPromise { return this.getAsset(extension.assets.changelog) .then(asText); diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 2f455b8a782..164c42761d3 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -205,7 +205,7 @@ declare module 'vscode' { /** * Get a word-range at the given position. By default words are defined by - * common separators, like space, -, _, etc. In addition, per languge custom + * common separators, like space, -, _, etc. In addition, per language custom * [word definitions](#LanguageConfiguration.wordPattern) can be defined. It * is also possible to provide a custom regular expression. * @@ -483,7 +483,7 @@ declare module 'vscode' { active: Position; /** - * Create a selection from two postions. + * Create a selection from two positions. * * @param anchor A position. * @param active A position. @@ -1038,7 +1038,7 @@ declare module 'vscode' { /** * Render options applied to the current decoration. For performance reasons, keep the - * number of decoration specific options small, and use decoration types whereever possible. + * number of decoration specific options small, and use decoration types wherever possible. */ renderOptions?: DecorationInstanceRenderOptions; } @@ -1120,7 +1120,7 @@ declare module 'vscode' { /** * Insert a [snippet](#SnippetString) and put the editor into snippet mode. "Snippet mode" - * means the editor adds placeholders and additionals cursors so that the user can complete + * means the editor adds placeholders and additional cursors so that the user can complete * or accept the snippet. * * @param snippet The snippet to insert in this edit. @@ -1311,7 +1311,7 @@ declare module 'vscode' { * [Uri.parse](#Uri.parse). * * @param skipEncoding Do not percentage-encode the result, defaults to `false`. Note that - * the `#` and `?` characters occuring in the path will always be encoded. + * the `#` and `?` characters occurring in the path will always be encoded. * @returns A string representation of this Uri. */ toString(skipEncoding?: boolean): string; @@ -1606,7 +1606,7 @@ declare module 'vscode' { * * * Note 1: A dialog can select files, folders, or both. This is not true for Windows * which enforces to open either files or folder, but *not both*. - * * Note 2: Explictly setting `canSelectFiles` and `canSelectFolders` to `false` is futile + * * Note 2: Explicitly setting `canSelectFiles` and `canSelectFolders` to `false` is futile * and the editor then silently adjusts the options to select files. */ export interface OpenDialogOptions { @@ -1856,7 +1856,7 @@ declare module 'vscode' { * to that type `T`. In addition, `null` and `undefined` can be returned - either directly or from a * thenable. * - * The snippets below are all valid implementions of the [`HoverProvider`](#HoverProvider): + * The snippets below are all valid implementations of the [`HoverProvider`](#HoverProvider): * * ```ts * let a: HoverProvider = { @@ -2182,7 +2182,7 @@ declare module 'vscode' { } /** - * The implemenetation provider interface defines the contract between extensions and + * The implementation provider interface defines the contract between extensions and * the go to implementation feature. */ export interface ImplementationProvider { @@ -2719,7 +2719,7 @@ declare module 'vscode' { * Builder-function that appends a tabstop (`$1`, `$2` etc) to * the [`value`](#SnippetString.value) of this snippet string. * - * @param number The number of this tabstop, defaults to an auto-incremet + * @param number The number of this tabstop, defaults to an auto-increment * value starting at 1. * @return This snippet string. */ @@ -2731,7 +2731,7 @@ declare module 'vscode' { * * @param value The value of this placeholder - either a string or a function * with which a nested snippet can be created. - * @param number The number of this tabstop, defaults to an auto-incremet + * @param number The number of this tabstop, defaults to an auto-increment * value starting at 1. * @return This snippet string. */ @@ -3002,7 +3002,7 @@ declare module 'vscode' { /** * A completion item represents a text snippet that is proposed to complete text that is being typed. * - * It is suffient to create a completion item from just a [label](#CompletionItem.label). In that + * It is sufficient to create a completion item from just a [label](#CompletionItem.label). In that * case the completion item will replace the [word](#TextDocument.getWordRangeAtPosition) * until the cursor with the given label or [insertText](#CompletionItem.insertText). Otherwise the * the given [edit](#CompletionItem.textEdit) is used. @@ -3188,7 +3188,7 @@ declare module 'vscode' { * Providers can delay the computation of the [`detail`](#CompletionItem.detail) * and [`documentation`](#CompletionItem.documentation) properties by implementing the * [`resolveCompletionItem`](#CompletionItemProvider.resolveCompletionItem)-function. However, properties that - * are needed for the inital sorting and filtering, like `sortText`, `filterText`, `insertText`, and `range`, must + * are needed for the initial sorting and filtering, like `sortText`, `filterText`, `insertText`, and `range`, must * not be changed during resolve. * * Providers are asked for completions either explicitly by a user gesture or -depending on the configuration- @@ -3268,7 +3268,7 @@ declare module 'vscode' { /** * Given a link fill in its [target](#DocumentLink.target). This method is called when an incomplete - * link is selected in the UI. Providers can implement this method and return incomple links + * link is selected in the UI. Providers can implement this method and return incomplete links * (without target) from the [`provideDocumentLinks`](#DocumentLinkProvider.provideDocumentLinks) method which * often helps to improve performance. * @@ -3308,7 +3308,7 @@ declare module 'vscode' { * * @param red The red component. * @param green The green component. - * @param blue The bluew component. + * @param blue The blue component. * @param alpha The alpha component. */ constructor(red: number, green: number, blue: number, alpha: number); @@ -3320,7 +3320,7 @@ declare module 'vscode' { export class ColorInformation { /** - * The range in the document where this color appers. + * The range in the document where this color appears. */ range: Range; @@ -3388,7 +3388,7 @@ declare module 'vscode' { * * @param document The document in which the command was invoked. * @param token A cancellation token. - * @return An array of [color informations](#ColorInformation) or a thenable that resolves to such. The lack of a result + * @return An array of [color information](#ColorInformation) or a thenable that resolves to such. The lack of a result * can be signaled by returning `undefined`, `null`, or an empty array. */ provideDocumentColors(document: TextDocument, token: CancellationToken): ProviderResult; @@ -3507,7 +3507,7 @@ declare module 'vscode' { */ export interface IndentationRule { /** - * If a line matches this pattern, then all the lines after it should be unindendented once (until another rule matches). + * If a line matches this pattern, then all the lines after it should be unindented once (until another rule matches). */ decreaseIndentPattern: RegExp; /** @@ -3738,7 +3738,7 @@ declare module 'vscode' { * The *effective* value (returned by [`get`](#WorkspaceConfiguration.get)) * is computed like this: `defaultValue` overwritten by `globalValue`, * `globalValue` overwritten by `workspaceValue`. `workspaceValue` overwritten by `workspaceFolderValue`. - * Refer to [Settings Inheritence](https://code.visualstudio.com/docs/getstarted/settings) + * Refer to [Settings Inheritance](https://code.visualstudio.com/docs/getstarted/settings) * for more information. * * *Note:* The configuration name must denote a leaf in the configuration tree @@ -3763,7 +3763,7 @@ declare module 'vscode' { * has no observable effect in that workspace, but in others. Setting a workspace value * in the presence of a more specific folder value has no observable effect for the resources * under respective [folder](#workspace.workspaceFolders), but in others. Refer to - * [Settings Inheritence](https://code.visualstudio.com/docs/getstarted/settings) for more information. + * [Settings Inheritance](https://code.visualstudio.com/docs/getstarted/settings) for more information. * * *Note 2:* To remove a configuration value use `undefined`, like so: `config.update('somekey', undefined)` * @@ -4615,7 +4615,7 @@ declare module 'vscode' { /** * Defines how an argument should be quoted if it contains - * spaces or unsuppoerted characters. + * spaces or unsupported characters. */ export enum ShellQuoting { @@ -5223,7 +5223,7 @@ declare module 'vscode' { * * @param oldUri The existing file. * @param newUri The new location. - * @param options Defines if existing files should be overwriten. + * @param options Defines if existing files should be overwritten. * @throws [`FileNotFound`](#FileSystemError.FileNotFound) when `oldUri` doesn't exist. * @throws [`FileNotFound`](#FileSystemError.FileNotFound) when parent of `newUri` doesn't exist, e.g. no mkdirp-logic required. * @throws [`FileExists`](#FileSystemError.FileExists) when `newUri` exists and when the `overwrite` option is not `true`. @@ -5298,7 +5298,7 @@ declare module 'vscode' { /** * Post a message to the webview content. * - * Messages are only develivered if the webview is visible. + * Messages are only delivered if the webview is visible. * * @param message Body of the message. */ @@ -5423,14 +5423,14 @@ declare module 'vscode' { */ interface WebviewPanelSerializer { /** - * Restore a webview panel from its seriailzed `state`. + * Restore a webview panel from its serialized `state`. * * Called when a serialized webview first becomes visible. * * @param webviewPanel Webview panel to restore. The serializer should take ownership of this panel. - * @param state Persisted state. This state comesfrom the value set inside the webview by `acquireVsCodeApi().setState`. + * @param state Persisted state. This state comes from the value set inside the webview by `acquireVsCodeApi().setState`. * - * @return Thanble indicating that the webview has been fully restored. + * @return Thenable indicating that the webview has been fully restored. */ deserializeWebviewPanel(webviewPanel: WebviewPanel, state: any): Thenable; } @@ -5982,7 +5982,7 @@ declare module 'vscode' { * * @param task A callback returning a promise. Progress increments can be reported with * the provided [progress](#Progress)-object. - * @return The thenable the task did rseturn. + * @return The thenable the task did return. */ export function withScmProgress(task: (progress: Progress) => Thenable): Thenable; @@ -6268,7 +6268,7 @@ declare module 'vscode' { Window = 10, /** - * Show progress as notifiation with an optional cancel button. Supports to show infinite and discrete progress. + * Show progress as notification with an optional cancel button. Supports to show infinite and discrete progress. */ Notification = 15 } @@ -6826,7 +6826,7 @@ declare module 'vscode' { * 1. When the `DocumentFilter` is empty (`{}`) the result is `0` * 2. When `scheme`, `language`, or `pattern` are defined but one doesn’t match, the result is `0` * 3. Matching against `*` gives a score of `5`, matching via equality or via a glob-pattern gives a score of `10` - * 4. The result is the maximun value of each match + * 4. The result is the maximum value of each match * * Samples: * ```js @@ -6865,7 +6865,7 @@ declare module 'vscode' { * all extensions but *not yet* from the task framework. * * @param resource A resource - * @returns An arrary of [diagnostics](#Diagnostic) objects or an empty array. + * @returns An array of [diagnostics](#Diagnostic) objects or an empty array. */ export function getDiagnostics(resource: Uri): Diagnostic[]; @@ -7632,7 +7632,7 @@ declare module 'vscode' { /** - * Register a [debug configuration provider](#DebugConfigurationProvider) for a specifc debug type. + * Register a [debug configuration provider](#DebugConfigurationProvider) for a specific debug type. * More than one provider can be registered for the same type. * * @param type The debug type for which the provider is registered. @@ -7725,7 +7725,7 @@ declare module 'vscode' { /** * Thenable is a common denominator between ES6 promises, Q, jquery.Deferred, WinJS.Promise, - * and others. This API makes no assumption about what promise libary is being used which + * and others. This API makes no assumption about what promise library is being used which * enables reusing existing code without migrating to a specific promise implementation. Still, * we recommend the use of native promises which are available in this editor. */ diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 5f102c6451c..25410e5480a 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -183,7 +183,7 @@ declare module 'vscode' { export interface DebugConfigurationProvider { /** - * This optional method is called just before a debug adapter is started to determine its excutable path and arguments. + * This optional method is called just before a debug adapter is started to determine its executable path and arguments. * Registering more than one debugAdapterExecutable for a type results in an error. * @param folder The workspace folder from which the configuration originates from or undefined for a folderless setup. * @param token A cancellation token. diff --git a/src/vs/workbench/api/browser/viewsContainersExtensionPoint.ts b/src/vs/workbench/api/browser/viewsContainersExtensionPoint.ts index 25c3a9b2916..45c5a846cf1 100644 --- a/src/vs/workbench/api/browser/viewsContainersExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsContainersExtensionPoint.ts @@ -80,12 +80,11 @@ class ViewsContainersExtensionHandler implements IWorkbenchContribution { } private registerTestViewContainer(): void { - const id = 'test'; const title = localize('test', "Test"); - const cssClass = `extensionViewlet-${id}`; + const cssClass = `extensionViewlet-test`; const icon = require.toUrl('./media/test.svg'); - this.registerCustomViewlet({ id, title, icon }, TEST_VIEW_CONTAINER_ORDER, cssClass); + this.registerCustomViewlet({ id: ViewLocation.TEST.id, title, icon }, TEST_VIEW_CONTAINER_ORDER, cssClass); } private handleAndRegisterCustomViewContainers() { @@ -139,13 +138,13 @@ class ViewsContainersExtensionHandler implements IWorkbenchContribution { const cssClass = `extensionViewlet-${descriptor.id}`; // TODO@extensionLocation const icon = join(extension.extensionLocation.fsPath, descriptor.icon); - this.registerCustomViewlet({ id: descriptor.id, title: descriptor.title, icon }, TEST_VIEW_CONTAINER_ORDER + index + 1, cssClass); + this.registerCustomViewlet({ id: `workbench.view.extension.${descriptor.id}`, title: descriptor.title, icon }, TEST_VIEW_CONTAINER_ORDER + index + 1, cssClass); }); } private registerCustomViewlet(descriptor: IUserFriendlyViewsContainerDescriptor, order: number, cssClass: string): void { const viewletRegistry = Registry.as(ViewletExtensions.Viewlets); - const id = `workbench.view.extension.${descriptor.id}`; + const id = descriptor.id; if (!viewletRegistry.getViewlet(id)) { diff --git a/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts b/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts index af2d76abc49..8c8d095db84 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts @@ -6,7 +6,7 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import uri from 'vs/base/common/uri'; -import { IDebugService, IConfig, IDebugConfigurationProvider, IBreakpoint, IFunctionBreakpoint, IBreakpointData, IAdapterExecutable, ITerminalSettings, IDebugAdapter, IDebugAdapterProvider } from 'vs/workbench/parts/debug/common/debug'; +import { IDebugService, IConfig, IDebugConfigurationProvider, IBreakpoint, IFunctionBreakpoint, IBreakpointData, IAdapterExecutable, ITerminalSettings, IDebugAdapter, IDebugAdapterProvider, ITerminalLauncher } from 'vs/workbench/parts/debug/common/debug'; import { TPromise } from 'vs/base/common/winjs.base'; import { ExtHostContext, ExtHostDebugServiceShape, MainThreadDebugServiceShape, DebugSessionUUID, MainContext, @@ -18,6 +18,8 @@ import { AbstractDebugAdapter } from 'vs/workbench/parts/debug/node/debugAdapter import * as paths from 'vs/base/common/paths'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { convertToVSCPaths, convertToDAPaths } from 'vs/workbench/parts/debug/common/debugUtils'; +import { ITerminalService } from 'vs/workbench/parts/terminal/common/terminal'; +import { AbstractTerminalLauncher } from 'vs/workbench/parts/debug/electron-browser/terminalSupport'; @extHostNamedCustomer(MainContext.MainThreadDebugService) @@ -28,11 +30,13 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb private _breakpointEventsActive: boolean; private _debugAdapters: Map; private _debugAdaptersHandleCounter = 1; + private _terminalLauncher: ITerminalLauncher; constructor( extHostContext: IExtHostContext, - @IDebugService private debugService: IDebugService + @IDebugService private debugService: IDebugService, + @ITerminalService private terminalService: ITerminalService, ) { this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostDebugService); this._toDispose = []; @@ -73,7 +77,10 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb } runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): TPromise { - return this._proxy.$runInTerminal(args, config); + if (!this._terminalLauncher) { + this._terminalLauncher = new ExtensionTerminalLauncher(this.terminalService, this._proxy); + } + return this._terminalLauncher.runInTerminal(args, config); } public dispose(): void { @@ -295,3 +302,25 @@ class ExtensionHostDebugAdapter extends AbstractDebugAdapter { return this._proxy.$stopDASession(this._handle); } } + +export class ExtensionTerminalLauncher extends AbstractTerminalLauncher { + + constructor( + @ITerminalService terminalService: ITerminalService, + private _proxy: ExtHostDebugServiceShape + ) { + super(terminalService); + } + + protected runInExternalTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): TPromise { + return this._proxy.$runInTerminal(args, config); + } + + protected isBusy(processId: number): TPromise { + return this._proxy.$isTerminalBusy(processId); + } + + protected prepareCommand(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): TPromise { + return this._proxy.$prepareCommandForTerminal(args, config); + } +} diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 657b9d776ca..8da4c2552f5 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -831,6 +831,8 @@ export interface ISourceMultiBreakpointDto { export interface ExtHostDebugServiceShape { $substituteVariables(folder: UriComponents | undefined, config: IConfig): TPromise; $runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): TPromise; + $isTerminalBusy(processId: number): TPromise; + $prepareCommandForTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): TPromise; $startDASession(handle: number, debugType: string, adapterExecutableInfo: IAdapterExecutable | null, debugPort: number): TPromise; $stopDASession(handle: number): TPromise; $sendDAMessage(handle: number, message: DebugProtocol.ProtocolMessage): TPromise; diff --git a/src/vs/workbench/api/node/extHostDebugService.ts b/src/vs/workbench/api/node/extHostDebugService.ts index 5dfa0140e6a..a16c1525a5e 100644 --- a/src/vs/workbench/api/node/extHostDebugService.ts +++ b/src/vs/workbench/api/node/extHostDebugService.ts @@ -22,7 +22,7 @@ import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace'; import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService'; import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/node/extHostDocumentsAndEditors'; import { IAdapterExecutable, ITerminalSettings, IDebuggerContribution, IConfig, IDebugAdapter } from 'vs/workbench/parts/debug/common/debug'; -import { getTerminalLauncher } from 'vs/workbench/parts/debug/node/terminals'; +import { getTerminalLauncher, hasChildprocesses, prepareCommand } from 'vs/workbench/parts/debug/node/terminals'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { VariableResolver } from 'vs/workbench/services/configurationResolver/node/variableResolver'; import { IStringDictionary } from 'vs/base/common/collections'; @@ -125,6 +125,14 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { return void 0; } + public $isTerminalBusy(processId: number): TPromise { + return asWinJsPromise(token => hasChildprocesses(processId)); + } + + public $prepareCommandForTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): TPromise { + return asWinJsPromise(token => prepareCommand(args, config)); + } + public $substituteVariables(folderUri: UriComponents | undefined, config: IConfig): TPromise { if (!this._variableResolver) { this._variableResolver = new ExtHostVariableResolverService(this._workspace, this._editorsService, this._configurationService); diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts index 338c2a65dee..6f53eadda76 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts @@ -25,7 +25,6 @@ import { ACTIVITY_BAR_BACKGROUND, ACTIVITY_BAR_BORDER, ACTIVITY_BAR_FOREGROUND, import { contrastBorder } from 'vs/platform/theme/common/colorRegistry'; import { CompositeBar } from 'vs/workbench/browser/parts/compositebar/compositeBar'; import { ToggleCompositePinnedAction, ICompositeBar } from 'vs/workbench/browser/parts/compositebar/compositeBarActions'; -import { ViewLocation, ViewsRegistry } from 'vs/workbench/common/views'; import { ViewletDescriptor } from 'vs/workbench/browser/viewlet'; import { Dimension, createCSSRule } from 'vs/base/browser/dom'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; @@ -57,7 +56,6 @@ export class ActivitybarPart extends Part { private globalActivityIdToActions: { [globalActivityId: string]: GlobalActivityAction; }; private placeholderComposites: IPlaceholderComposite[] = []; - private extensionsRegistrationCompleted: boolean = false; private compositeBar: CompositeBar; private compositeActions: { [compositeId: string]: { activityAction: ViewletActivityAction, pinnedAction: ToggleCompositePinnedAction } }; @@ -101,7 +99,6 @@ export class ActivitybarPart extends Part { } private onDidRegisterExtensions(): void { - this.extensionsRegistrationCompleted = true; this.removeNotExistingPlaceholderComposites(); this.updateCompositebar(); } @@ -109,8 +106,6 @@ export class ActivitybarPart extends Part { private registerListeners(): void { this.toUnbind.push(this.viewletService.onDidViewletRegister(() => this.updateCompositebar())); - this.toUnbind.push(ViewsRegistry.onViewsRegistered(() => this.updateCompositebar())); - this.toUnbind.push(ViewsRegistry.onViewsDeregistered(() => this.updateCompositebar())); // Activate viewlet action on opening of a viewlet this.toUnbind.push(this.viewletService.onDidViewletOpen(viewlet => this.compositeBar.activateComposite(viewlet.getId()))); @@ -119,7 +114,7 @@ export class ActivitybarPart extends Part { this.toUnbind.push(this.viewletService.onDidViewletClose(viewlet => this.compositeBar.deactivateComposite(viewlet.getId()))); this.toUnbind.push(this.viewletService.onDidViewletEnablementChange(({ id, enabled }) => { if (enabled) { - this.compositeBar.addComposite(this.viewletService.getViewlet(id), true); + this.compositeBar.addComposite(this.viewletService.getViewlet(id)); } else { this.removeComposite(id); } @@ -226,26 +221,18 @@ export class ActivitybarPart extends Part { private updateCompositebar(): void { const viewlets = this.viewletService.getViewlets(); for (const viewlet of viewlets) { - const hasPlaceholder = this.placeholderComposites.some(c => c.id === viewlet.id); + this.compositeBar.addComposite(viewlet); - // Add the composite if it has views registered or - // If it was existing before and the extensions registration is not yet completed. - if (this.hasRegisteredViews(viewlet) - || (hasPlaceholder && !this.extensionsRegistrationCompleted) - ) { - this.compositeBar.addComposite(viewlet, false); - // Pin it by default if it is new => it does not has a placeholder - if (!hasPlaceholder) { - this.compositeBar.pin(viewlet.id); - } - this.enableCompositeActions(viewlet); - const activeViewlet = this.viewletService.getActiveViewlet(); - if (activeViewlet && activeViewlet.getId() === viewlet.id) { - this.compositeBar.pin(viewlet.id); - this.compositeBar.activateComposite(viewlet.id); - } - } else { - this.removeComposite(viewlet.id); + // Pin it by default if it is new => it does not has a placeholder + if (this.placeholderComposites.every(c => c.id !== viewlet.id)) { + this.compositeBar.pin(viewlet.id); + } + + this.enableCompositeActions(viewlet); + const activeViewlet = this.viewletService.getActiveViewlet(); + if (activeViewlet && activeViewlet.getId() === viewlet.id) { + this.compositeBar.pin(viewlet.id); + this.compositeBar.activateComposite(viewlet.id); } } } @@ -254,7 +241,7 @@ export class ActivitybarPart extends Part { const viewlets = this.viewletService.getViewlets(); for (const { id } of this.placeholderComposites) { if (viewlets.every(viewlet => viewlet.id !== id)) { - this.compositeBar.addComposite({ id, name: id, order: void 0 }, false); + this.compositeBar.addComposite({ id, name: id, order: void 0 }); } } } @@ -288,14 +275,6 @@ export class ActivitybarPart extends Part { } } - private hasRegisteredViews(viewlet: ViewletDescriptor): boolean { - const viewLocation = ViewLocation.get(viewlet.id); - if (viewLocation) { - return ViewsRegistry.getViews(viewLocation).length > 0; - } - return true; - } - public getPinned(): string[] { return this.viewletService.getViewlets().map(v => v.id).filter(id => this.compositeBar.isPinned(id)); } @@ -324,7 +303,7 @@ export class ActivitybarPart extends Part { } public shutdown(): void { - const state = this.viewletService.getViewlets().filter(viewlet => this.hasRegisteredViews(viewlet)).map(viewlet => ({ id: viewlet.id, iconUrl: viewlet.iconUrl })); + const state = this.viewletService.getViewlets().map(viewlet => ({ id: viewlet.id, iconUrl: viewlet.iconUrl })); this.storageService.store(ActivitybarPart.PLACEHOLDER_VIEWLETS, JSON.stringify(state), StorageScope.GLOBAL); super.shutdown(); } diff --git a/src/vs/workbench/browser/parts/compositebar/compositeBar.ts b/src/vs/workbench/browser/parts/compositebar/compositeBar.ts index e9e71d81bf0..54b7a6c74e0 100644 --- a/src/vs/workbench/browser/parts/compositebar/compositeBar.ts +++ b/src/vs/workbench/browser/parts/compositebar/compositeBar.ts @@ -118,7 +118,7 @@ export class CompositeBar extends Widget implements ICompositeBar { this.updateCompositeSwitcher(); } - public addComposite({ id, name, order }: { id: string; name: string, order: number }, open: boolean): void { + public addComposite({ id, name, order }: { id: string; name: string, order: number }): void { const state = this.storedState.filter(s => s.id === id)[0]; const pinned = state ? state.pinned : true; let index = order >= 0 ? order : this.model.items.length; @@ -139,8 +139,8 @@ export class CompositeBar extends Widget implements ICompositeBar { // Add to the model if (this.model.add(id, name, order, index)) { this.computeSizes([this.model.findItem(id)]); - if (pinned || open) { - this.pin(id, open); + if (pinned) { + this.pin(id); } else { this.updateCompositeSwitcher(); } diff --git a/src/vs/workbench/browser/parts/panel/panelPart.ts b/src/vs/workbench/browser/parts/panel/panelPart.ts index 06578335499..68f224cc687 100644 --- a/src/vs/workbench/browser/parts/panel/panelPart.ts +++ b/src/vs/workbench/browser/parts/panel/panelPart.ts @@ -105,7 +105,7 @@ export class PanelPart extends CompositePart implements IPanelService { }); this.toUnbind.push(this.compositeBar); for (const panel of this.getPanels()) { - this.compositeBar.addComposite(panel, false); + this.compositeBar.addComposite(panel); } this.activePanelContextKey = ActivePanelContext.bindTo(contextKeyService); this.onDidPanelOpen(this._onDidPanelOpen, this, this.disposables); @@ -116,7 +116,7 @@ export class PanelPart extends CompositePart implements IPanelService { private registerListeners(): void { - this.toUnbind.push(this.registry.onDidRegister(panelDescriptor => this.compositeBar.addComposite(panelDescriptor, false))); + this.toUnbind.push(this.registry.onDidRegister(panelDescriptor => this.compositeBar.addComposite(panelDescriptor))); // Activate panel action on opening of a panel this.toUnbind.push(this.onDidPanelOpen(panel => { @@ -198,7 +198,7 @@ export class PanelPart extends CompositePart implements IPanelService { if (descriptor && descriptor.enabled !== enabled) { descriptor.enabled = enabled; if (enabled) { - this.compositeBar.addComposite(descriptor, true); + this.compositeBar.addComposite(descriptor); } else { this.removeComposite(id); } diff --git a/src/vs/workbench/browser/parts/quickinput/quickInput.contribution.ts b/src/vs/workbench/browser/parts/quickinput/quickInput.contribution.ts index 05802208d23..5aea4a4cfce 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInput.contribution.ts +++ b/src/vs/workbench/browser/parts/quickinput/quickInput.contribution.ts @@ -4,12 +4,7 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; -import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; -import { QuickPickManyToggleAction } from 'vs/workbench/browser/parts/quickinput/quickInput'; -import { inQuickOpenContext } from 'vs/workbench/browser/parts/quickopen/quickopen'; +import { QuickPickManyToggle } from 'vs/workbench/browser/parts/quickinput/quickInput'; +import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; -const registry = Registry.as(ActionExtensions.WorkbenchActions); - -registry.registerWorkbenchAction(new SyncActionDescriptor(QuickPickManyToggleAction, QuickPickManyToggleAction.ID, QuickPickManyToggleAction.LABEL, null, inQuickOpenContext), 'Toggle Selection in Quick Pick'); +KeybindingsRegistry.registerCommandAndKeybindingRule(QuickPickManyToggle); diff --git a/src/vs/workbench/browser/parts/quickinput/quickInput.ts b/src/vs/workbench/browser/parts/quickinput/quickInput.ts index 230e77eb960..dd5e125d796 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInput.ts +++ b/src/vs/workbench/browser/parts/quickinput/quickInput.ts @@ -35,7 +35,8 @@ import { onUnexpectedError, canceled } from 'vs/base/common/errors'; import Severity from 'vs/base/common/severity'; import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { Action } from 'vs/base/common/actions'; +import { ICommandAndKeybindingRule, KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { inQuickOpenContext } from 'vs/workbench/browser/parts/quickopen/quickopen'; const $ = dom.$; @@ -736,21 +737,13 @@ export class QuickInputService extends Component implements IQuickInputService { } } -export class QuickPickManyToggleAction extends Action { - - public static readonly ID = 'workbench.action.quickPickManyToggle'; - public static readonly LABEL = localize('quickPickManyToggle', "Toggle Selection in Quick Pick"); - - constructor( - id: string, - label: string, - @IQuickInputService private quickInputService: IQuickInputService - ) { - super(id, label); +export const QuickPickManyToggle: ICommandAndKeybindingRule = { + id: 'workbench.action.quickPickManyToggle', + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + when: inQuickOpenContext, + primary: undefined, + handler: accessor => { + const quickInputService = accessor.get(IQuickInputService); + quickInputService.toggle(); } - - public run(event?: any): TPromise { - this.quickInputService.toggle(); - return TPromise.as(true); - } -} +}; diff --git a/src/vs/workbench/browser/parts/views/contributableViews.ts b/src/vs/workbench/browser/parts/views/contributableViews.ts index 42c9ea86328..d1b0d8a3830 100644 --- a/src/vs/workbench/browser/parts/views/contributableViews.ts +++ b/src/vs/workbench/browser/parts/views/contributableViews.ts @@ -55,7 +55,7 @@ export interface IViewItem { active: boolean; } -class ViewDescriptorCollection { +export class ViewDescriptorCollection { private contextKeys = new CounterSet(); private items: IViewItem[] = []; diff --git a/src/vs/workbench/browser/parts/views/customView.ts b/src/vs/workbench/browser/parts/views/views.ts similarity index 91% rename from src/vs/workbench/browser/parts/views/customView.ts rename to src/vs/workbench/browser/parts/views/views.ts index 40431d62f5e..2e127abb4b0 100644 --- a/src/vs/workbench/browser/parts/views/customView.ts +++ b/src/vs/workbench/browser/parts/views/views.ts @@ -33,8 +33,13 @@ import { FileIconThemableWorkbenchTree } from 'vs/workbench/browser/parts/views/ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { isUndefinedOrNull } from 'vs/base/common/types'; import { Emitter, Event } from 'vs/base/common/event'; +import { ViewDescriptorCollection } from './contributableViews'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { ViewletRegistry, Extensions as ViewletExtensions } from 'vs/workbench/browser/viewlet'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; -export class CustomViewsService extends Disposable implements IViewsService { +export class ViewsService extends Disposable implements IViewsService { _serviceBrand: any; @@ -42,9 +47,16 @@ export class CustomViewsService extends Disposable implements IViewsService { constructor( @IInstantiationService private instantiationService: IInstantiationService, - @IViewletService private viewletService: IViewletService + @ILifecycleService private lifecycleService: ILifecycleService, + @IViewletService private viewletService: IViewletService, + @IStorageService private storageService: IStorageService ) { super(); + + ViewLocation.all.forEach(viewLocation => this.onDidRegisterViewLocation(viewLocation)); + this._register(ViewLocation.onDidRegister(viewLocation => this.onDidRegisterViewLocation(viewLocation))); + this._register(Registry.as(ViewletExtensions.Viewlets).onDidRegister(viewlet => this.viewletService.setViewletEnablement(viewlet.id, this.storageService.getBoolean(`viewservice.${viewlet.id}.enablement`, StorageScope.GLOBAL, viewlet.id !== ViewLocation.TEST.id)))); + this.createViewers(ViewsRegistry.getAllViews()); this._register(ViewsRegistry.onViewsRegistered(viewDescriptors => this.createViewers(viewDescriptors))); this._register(ViewsRegistry.onViewsDeregistered(viewDescriptors => this.removeViewers(viewDescriptors))); @@ -72,6 +84,18 @@ export class CustomViewsService extends Disposable implements IViewsService { return TPromise.as(null); } + private onDidRegisterViewLocation(viewLocation: ViewLocation): void { + const viewDescriptorCollection = this._register(this.instantiationService.createInstance(ViewDescriptorCollection, viewLocation)); + this._register(viewDescriptorCollection.onDidChange(() => this.updateViewletEnablement(viewLocation, viewDescriptorCollection))); + this.lifecycleService.when(LifecyclePhase.Eventually).then(() => this.updateViewletEnablement(viewLocation, viewDescriptorCollection)); + } + + private updateViewletEnablement(viewLocation: ViewLocation, viewDescriptorCollection: ViewDescriptorCollection): void { + const enabled = viewDescriptorCollection.viewDescriptors.length > 0; + this.viewletService.setViewletEnablement(viewLocation.id, enabled); + this.storageService.store(`viewservice.${viewLocation.id}.enablement`, enabled, StorageScope.GLOBAL); + } + private createViewers(viewDescriptors: IViewDescriptor[]): void { for (const viewDescriptor of viewDescriptors) { if ((viewDescriptor).treeView) { diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index 52355a0e60c..ff8cf746353 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -14,23 +14,34 @@ import { IViewlet } from 'vs/workbench/common/viewlet'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IDisposable } from 'vs/base/common/lifecycle'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; +import { values } from 'vs/base/common/map'; export class ViewLocation { + private static readonly _onDidRegister: Emitter = new Emitter(); + static readonly onDidRegister: Event = ViewLocation._onDidRegister.event; + private static locations: Map = new Map(); static register(id: string): ViewLocation { - const viewLocation = new ViewLocation(id); - ViewLocation.locations.set(id, viewLocation); - return viewLocation; + if (!ViewLocation.locations.has(id)) { + const viewLocation = new ViewLocation(id); + ViewLocation.locations.set(id, viewLocation); + ViewLocation._onDidRegister.fire(viewLocation); + } + return ViewLocation.get(id); } static get(value: string): ViewLocation { return ViewLocation.locations.get(value); } + static get all(): ViewLocation[] { + return values(ViewLocation.locations); + } static readonly Explorer: ViewLocation = ViewLocation.register('workbench.view.explorer'); static readonly Debug: ViewLocation = ViewLocation.register('workbench.view.debug'); static readonly Extensions: ViewLocation = ViewLocation.register('workbench.view.extensions'); static readonly SCM: ViewLocation = ViewLocation.register('workbench.view.scm.views.contributed'); + static readonly TEST: ViewLocation = ViewLocation.register('workbench.view.extension.test'); private constructor(private _id: string) { } get id(): string { return this._id; } diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts index e1efc9b986d..b66f89c6ce3 100644 --- a/src/vs/workbench/electron-browser/workbench.ts +++ b/src/vs/workbench/electron-browser/workbench.ts @@ -95,7 +95,7 @@ import URI from 'vs/base/common/uri'; import { IListService, ListService } from 'vs/platform/list/browser/listService'; import { InputFocusedContext } from 'vs/platform/workbench/common/contextkeys'; import { IViewsService } from 'vs/workbench/common/views'; -import { CustomViewsService } from 'vs/workbench/browser/parts/views/customView'; +import { ViewsService } from 'vs/workbench/browser/parts/views/views'; 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'; @@ -362,7 +362,7 @@ export class Workbench extends Disposable implements IPartService { serviceCollection.set(IPanelService, this.panelPart); // Custom views service - const customViewsService = this.instantiationService.createInstance(CustomViewsService); + const customViewsService = this.instantiationService.createInstance(ViewsService); serviceCollection.set(IViewsService, customViewsService); // Activity service (activitybar part) @@ -668,9 +668,11 @@ export class Workbench extends Disposable implements IPartService { } perf.mark('willRestoreViewlet'); - restorePromises.push(this.viewletService.openViewlet(viewletIdToRestore).then(() => { - perf.mark('didRestoreViewlet'); - })); + restorePromises.push(this.viewletService.openViewlet(viewletIdToRestore) + .then(viewlet => viewlet || this.viewletService.openViewlet(this.viewletService.getDefaultViewletId())) + .then(() => { + perf.mark('didRestoreViewlet'); + })); } // Restore Panel @@ -1332,7 +1334,8 @@ export class Workbench extends Disposable implements IPartService { else if (!hidden && !this.sidebarPart.getActiveViewlet()) { const viewletToOpen = this.sidebarPart.getLastActiveViewletId(); if (viewletToOpen) { - promise = this.sidebarPart.openViewlet(viewletToOpen, true); + promise = this.viewletService.openViewlet(viewletToOpen, true) + .then(viewlet => viewlet || this.viewletService.openViewlet(this.viewletService.getDefaultViewletId(), true)); } } @@ -1454,4 +1457,4 @@ export class Workbench extends Disposable implements IPartService { } //#endregion -} +} \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/breakpointsView.ts b/src/vs/workbench/parts/debug/browser/breakpointsView.ts index 676e9a5d323..e48a815e6ce 100644 --- a/src/vs/workbench/parts/debug/browser/breakpointsView.ts +++ b/src/vs/workbench/parts/debug/browser/breakpointsView.ts @@ -520,7 +520,7 @@ class FunctionBreakpointInputRenderer implements IRenderer { +export function openBreakpointSource(breakpoint: IBreakpoint, sideBySide: boolean, preserveFocus: boolean, debugService: IDebugService, editorService: IEditorService): TPromise { if (breakpoint.uri.scheme === DEBUG_SCHEME && debugService.state === State.Inactive) { return TPromise.as(null); } diff --git a/src/vs/workbench/parts/debug/browser/debugANSIHandling.ts b/src/vs/workbench/parts/debug/browser/debugANSIHandling.ts new file mode 100644 index 00000000000..07fe64ce7a6 --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/debugANSIHandling.ts @@ -0,0 +1,130 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { LinkDetector } from 'vs/workbench/parts/debug/browser/linkDetector'; + +/** + * @param text The content to stylize. + * @returns An {@link HTMLSpanElement} that contains the potentially stylized text. + */ +export function handleANSIOutput(text: string, linkDetector: LinkDetector): HTMLSpanElement { + + const root: HTMLSpanElement = document.createElement('span'); + const textLength: number = text.length; + + let styleNames: string[] = []; + let currentPos: number = 0; + let buffer: string = ''; + + while (currentPos < textLength) { + + let sequenceFound: boolean = false; + + // Potentially an ANSI escape sequence. + // See http://ascii-table.com/ansi-escape-sequences.php & https://en.wikipedia.org/wiki/ANSI_escape_code + if (text.charCodeAt(currentPos) === 27 && text.charAt(currentPos + 1) === '[') { + + const startPos: number = currentPos; + currentPos += 2; // Ignore 'Esc[' as it's in every sequence. + + let ansiSequence: string = ''; + + while (currentPos < textLength) { + const char: string = text.charAt(currentPos); + ansiSequence += char; + + currentPos++; + + // Look for a known sequence terminating character. + if (char.match(/^[ABCDHIJKfhmpsu]$/)) { + sequenceFound = true; + break; + } + + } + + if (sequenceFound) { + + // Flush buffer with previous styles. + appendStylizedStringToContainer(root, buffer, styleNames, linkDetector); + + buffer = ''; + + /* + * Certain ranges that are matched here do not contain real graphics rendition sequences. For + * the sake of having a simpler expression, they have been included anyway. + */ + if (ansiSequence.match(/^(?:[39][0-7]|[0-8]|39)(?:;(?:[39][0-7]|[0-8]|39))*;?m$/)) { + + const styleCodes: number[] = ansiSequence.slice(0, -1) // Remove final 'm' character. + .split(';') // Separate style codes. + .filter(elem => elem !== '') // Filter empty elems as '34;m' -> ['34', '']. + .map(elem => parseInt(elem, 10)); // Convert to numbers. + + for (let code of styleCodes) { + if (code === 0) { + styleNames = []; + } else if (code === 1) { + styleNames.push('code-bold'); + } else if (code === 4) { + styleNames.push('code-underline'); + } else if ((code >= 30 && code <= 37) || (code >= 90 && code <= 97)) { + styleNames.push('code-foreground-' + code); + } else if (code === 39) { + // Remove all foreground colour codes + styleNames = styleNames.filter(style => !style.match(/^code-foreground-\d+$/)); + } + } + + } else { + // Unsupported sequence so simply hide it. + } + + } else { + currentPos = startPos; + } + + } + + if (sequenceFound === false) { + buffer += text.charAt(currentPos); + currentPos++; + } + + } + + // Flush remaining text buffer if not empty. + if (buffer) { + appendStylizedStringToContainer(root, buffer, styleNames, linkDetector); + } + + return root; + +} + +/** + * @param root The {@link HTMLElement} to append the content to. + * @param stringContent The text content to be appended. + * @param cssClasses The list of CSS styles to apply to the text content. + * @param linkDetector The {@link LinkDetector} responsible for generating links from {@param stringContent}. + */ +export function appendStylizedStringToContainer(root: HTMLElement, stringContent: string, cssClasses: string[], linkDetector: LinkDetector): void { + if (!root || !stringContent) { + return; + } + + const content = linkDetector.handleLinks(stringContent); + let container: HTMLElement; + + if (typeof content === 'string') { + container = document.createElement('span'); + container.textContent = content; + } else { + container = content; + } + + container.className = cssClasses.join(' '); + root.appendChild(container); +} \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/debugEditorActions.ts b/src/vs/workbench/parts/debug/browser/debugEditorActions.ts index 64d2b7d6852..95ba85099ba 100644 --- a/src/vs/workbench/parts/debug/browser/debugEditorActions.ts +++ b/src/vs/workbench/parts/debug/browser/debugEditorActions.ts @@ -8,12 +8,14 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; import { Range } from 'vs/editor/common/core/range'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { ServicesAccessor, registerEditorAction, EditorAction } from 'vs/editor/browser/editorExtensions'; +import { ServicesAccessor, registerEditorAction, EditorAction, IActionOptions } from 'vs/editor/browser/editorExtensions'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IDebugService, CONTEXT_IN_DEBUG_MODE, CONTEXT_NOT_IN_DEBUG_REPL, CONTEXT_DEBUG_STATE, State, REPL_ID, VIEWLET_ID, IDebugEditorContribution, EDITOR_CONTRIBUTION_ID, BreakpointWidgetContext, IBreakpoint } from 'vs/workbench/parts/debug/common/debug'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { IEditorService } from 'vs/platform/editor/common/editor'; +import { openBreakpointSource } from 'vs/workbench/parts/debug/browser/breakpointsView'; class ToggleBreakpointAction extends EditorAction { constructor() { @@ -209,6 +211,68 @@ class ShowDebugHoverAction extends EditorAction { } } +class GoToBreakpointAction extends EditorAction { + constructor(private isNext: boolean, opts: IActionOptions) { + super(opts); + } + + public run(accessor: ServicesAccessor, editor: ICodeEditor, args: any): TPromise { + const debugService = accessor.get(IDebugService); + const editorService = accessor.get(IEditorService); + const currentUri = editor.getModel().uri; + const currentLine = editor.getPosition().lineNumber; + //Breakpoints returned from `getBreakpoints` are already sorted. + const allEnabledBreakpoints = debugService.getModel().getBreakpoints({ enabledOnly: true }); + + //Try to find breakpoint in current file + let moveBreakpoint = + this.isNext + ? allEnabledBreakpoints.filter(bp => bp.uri.toString() === currentUri.toString() && bp.lineNumber > currentLine)[0] + : allEnabledBreakpoints.filter(bp => bp.uri.toString() === currentUri.toString() && bp.lineNumber < currentLine)[0]; + + //Try to find breakpoints in following files + if (!moveBreakpoint) { + moveBreakpoint = + this.isNext + ? allEnabledBreakpoints.filter(bp => bp.uri.toString() > currentUri.toString())[0] + : allEnabledBreakpoints.filter(bp => bp.uri.toString() < currentUri.toString())[0]; + } + + //Move to first possible breakpoint + if (!moveBreakpoint) { + moveBreakpoint = allEnabledBreakpoints[0]; + } + + if (moveBreakpoint) { + return openBreakpointSource(moveBreakpoint, false, true, debugService, editorService); + } + + return TPromise.as(null); + } +} + +class GoToNextBreakpointAction extends GoToBreakpointAction { + constructor() { + super(true, { + id: 'editor.debug.action.goToNextBreakpoint', + label: nls.localize('goToNextBreakpoint', "Debug: Go To Next Breakpoint"), + alias: 'Debug: Go To Next Breakpoint', + precondition: null + }); + } +} + +class GoToPreviousBreakpointAction extends GoToBreakpointAction { + constructor() { + super(false, { + id: 'editor.debug.action.goToPreviousBreakpoint', + label: nls.localize('goToPreviousBreakpoint', "Debug: Go To Previous Breakpoint"), + alias: 'Debug: Go To Previous Breakpoint', + precondition: null + }); + } +} + registerEditorAction(ToggleBreakpointAction); registerEditorAction(ConditionalBreakpointAction); registerEditorAction(LogPointAction); @@ -216,3 +280,5 @@ registerEditorAction(RunToCursorAction); registerEditorAction(SelectionToReplAction); registerEditorAction(SelectionToWatchExpressionsAction); registerEditorAction(ShowDebugHoverAction); +registerEditorAction(GoToNextBreakpointAction); +registerEditorAction(GoToPreviousBreakpointAction); diff --git a/src/vs/workbench/parts/debug/browser/media/repl.css b/src/vs/workbench/parts/debug/browser/media/repl.css index 057a3253ee1..aaa22e8f9bf 100644 --- a/src/vs/workbench/parts/debug/browser/media/repl.css +++ b/src/vs/workbench/parts/debug/browser/media/repl.css @@ -165,32 +165,33 @@ .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-bold { font-weight: bold; } .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-underline { text-decoration: underline; } -.monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code30, .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code90 { color: gray; } -.monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code31, .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code91 { color: #BE1717; } -.monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code32, .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code92 { color: #338A2F; } -.monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code33, .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code93 { color: #BEB817; } -.monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code34, .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code94 { color: darkblue; } -.monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code35, .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code95 { color: darkmagenta; } -.monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code36, .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code96 { color: darkcyan; } -.monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code37, .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code97 { color: #BDBDBD; } +/* Regular and bright color codes are currently treated the same. */ +.monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-30, .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-90 { color: gray; } +.monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-31, .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-91 { color: #BE1717; } +.monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-32, .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-92 { color: #338A2F; } +.monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-33, .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-93 { color: #BEB817; } +.monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-34, .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-94 { color: darkblue; } +.monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-35, .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-95 { color: darkmagenta; } +.monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-36, .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-96 { color: darkcyan; } +.monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-37, .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-97 { color: #BDBDBD; } -.vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code30, .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code90 { color: #A0A0A0; } -.vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code31, .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code91 { color: #A74747; } -.vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code32, .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code92 { color: #348F34; } -.vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code33, .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code93 { color: #5F4C29; } -.vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code34, .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code94 { color: #6286BB; } -.vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code35, .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code95 { color: #914191; } -.vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code36, .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code96 { color: #218D8D; } -.vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code37, .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code97 { color: #707070; } +.vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-30, .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-90 { color: #A0A0A0; } +.vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-31, .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-91 { color: #A74747; } +.vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-32, .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-92 { color: #348F34; } +.vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-33, .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-93 { color: #5F4C29; } +.vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-34, .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-94 { color: #6286BB; } +.vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-35, .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-95 { color: #914191; } +.vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-36, .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-96 { color: #218D8D; } +.vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-37, .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-97 { color: #707070; } -.hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code30, .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code90 { color: gray; } -.hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code31, .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code91 { color: #A74747; } -.hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code32, .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code92 { color: #348F34; } -.hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code33, .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code93 { color: #5F4C29; } -.hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code34, .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code94 { color: #6286BB; } -.hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code35, .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code95 { color: #914191; } -.hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code36, .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code96 { color: #218D8D; } -.hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code37, .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code97 { color: #707070; } +.hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-30, .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-90 { color: gray; } +.hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-31, .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-91 { color: #A74747; } +.hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-32, .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-92 { color: #348F34; } +.hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-33, .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-93 { color: #5F4C29; } +.hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-34, .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-94 { color: #6286BB; } +.hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-35, .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-95 { color: #914191; } +.hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-36, .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-96 { color: #218D8D; } +.hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-37, .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-97 { color: #707070; } /* Links */ .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression a { diff --git a/src/vs/workbench/parts/debug/common/debug.ts b/src/vs/workbench/parts/debug/common/debug.ts index c4258a03d83..6d3484c208e 100644 --- a/src/vs/workbench/parts/debug/common/debug.ts +++ b/src/vs/workbench/parts/debug/common/debug.ts @@ -311,7 +311,7 @@ export interface IViewModel extends ITreeElement { export interface IModel extends ITreeElement { getSessions(): ReadonlyArray; - getBreakpoints(filter?: { uri?: uri, lineNumber?: number, column?: number }): ReadonlyArray; + getBreakpoints(filter?: { uri?: uri, lineNumber?: number, column?: number, enabledOnly?: boolean }): ReadonlyArray; areBreakpointsActivated(): boolean; getFunctionBreakpoints(): ReadonlyArray; getExceptionBreakpoints(): ReadonlyArray; @@ -369,7 +369,6 @@ export interface IEnvConfig { postDebugTask?: string; debugServer?: number; noDebug?: boolean; - logLevel?: string; } export interface IConfig extends IEnvConfig { diff --git a/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts b/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts index 2d6e35a22d1..2510897565f 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts @@ -215,11 +215,6 @@ configurationRegistry.registerConfiguration({ description: nls.localize({ comment: ['This is the description for a setting'], key: 'enableAllHovers' }, "Controls if the non debug hovers should be enabled while debugging. If true the hover providers will be called to provide a hover. Regular hovers will not be shown even if this setting is true."), default: false }, - 'debug.logLevel': { - enum: ['off', 'trace', 'debug', 'info', 'warning', 'error', 'critical'], - description: nls.localize({ comment: ['This is the description for a setting'], key: 'logLevel' }, "Controls what diagnostic output should the debug session produce."), - default: 'info' - }, 'launch': { type: 'object', description: nls.localize({ comment: ['This is the description for a setting'], key: 'launch' }, "Global debug launch configuration. Should be used as an alternative to 'launch.json' that is shared across workspaces"), diff --git a/src/vs/workbench/parts/debug/electron-browser/debugService.ts b/src/vs/workbench/parts/debug/electron-browser/debugService.ts index d09112df0a9..968adec03f8 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugService.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugService.ts @@ -54,7 +54,6 @@ import { IAction, Action } from 'vs/base/common/actions'; import { normalizeDriveLetter } from 'vs/base/common/labels'; import { RunOnceScheduler } from 'vs/base/common/async'; import product from 'vs/platform/node/product'; -import { ILogService, LogLevel } from 'vs/platform/log/common/log'; import { deepClone, equals } from 'vs/base/common/objects'; const DEBUG_BREAKPOINTS_KEY = 'debug.breakpoint'; @@ -107,7 +106,6 @@ export class DebugService implements debug.IDebugService { @ITaskService private taskService: ITaskService, @IFileService private fileService: IFileService, @IConfigurationService private configurationService: IConfigurationService, - @ILogService private logService: ILogService ) { this.toDispose = []; this.toDisposeOnSessionEnd = new Map(); @@ -778,9 +776,6 @@ export class DebugService implements debug.IDebugService { if (noDebug) { config.noDebug = true; } - if (!config.logLevel) { - config.logLevel = LogLevel[this.logService.getLevel()].toLowerCase(); - } return (type ? TPromise.as(null) : this.configurationManager.guessDebugger().then(a => type = a && a.type)).then(() => this.configurationManager.resolveConfigurationByProviders(launch && launch.workspace ? launch.workspace.uri : undefined, type, config).then(config => { diff --git a/src/vs/workbench/parts/debug/electron-browser/replViewer.ts b/src/vs/workbench/parts/debug/electron-browser/replViewer.ts index a44da207de2..0ff3980542b 100644 --- a/src/vs/workbench/parts/debug/electron-browser/replViewer.ts +++ b/src/vs/workbench/parts/debug/electron-browser/replViewer.ts @@ -23,6 +23,7 @@ import { CopyAction, CopyAllAction } from 'vs/workbench/parts/debug/electron-bro import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { LinkDetector } from 'vs/workbench/parts/debug/browser/linkDetector'; +import { handleANSIOutput } from 'vs/workbench/parts/debug/browser/debugANSIHandling'; const $ = dom.$; @@ -252,15 +253,8 @@ export class ReplExpressionsRenderer implements IRenderer { dom.clearNode(templateData.value); // Reset classes to clear ansi decorations since templates are reused templateData.value.className = 'value'; - let result = this.handleANSIOutput(element.value); - if (typeof result === 'string') { - renderExpressionValue(result, templateData.value, { - preserveWhitespace: true, - showHover: false - }); - } else { - templateData.value.appendChild(result); - } + let result = handleANSIOutput(element.value, this.linkDetector); + templateData.value.appendChild(result); dom.addClass(templateData.value, (element.severity === severity.Warning) ? 'warn' : (element.severity === severity.Error) ? 'error' : 'info'); templateData.source.textContent = element.sourceData ? `${element.sourceData.source.name}:${element.sourceData.lineNumber}` : ''; @@ -292,115 +286,6 @@ export class ReplExpressionsRenderer implements IRenderer { } } - private handleANSIOutput(text: string): HTMLElement | string { - let tokensContainer: HTMLSpanElement; - let currentToken: HTMLSpanElement; - let buffer: string = ''; - - for (let i = 0, len = text.length; i < len; i++) { - - // start of ANSI escape sequence (see http://ascii-table.com/ansi-escape-sequences.php) - if (text.charCodeAt(i) === 27) { - let index = i; - let chr = (++index < len ? text.charAt(index) : null); - let codes = []; - if (chr && chr === '[') { - let code: string = null; - while (chr !== 'm' && codes.length <= 7) { - chr = (++index < len ? text.charAt(index) : null); - - if (chr && chr >= '0' && chr <= '9') { - code = chr; - chr = (++index < len ? text.charAt(index) : null); - } - - if (chr && chr >= '0' && chr <= '9') { - code += chr; - chr = (++index < len ? text.charAt(index) : null); - } - - if (code === null) { - code = '0'; - } - - codes.push(code); - } - - if (chr === 'm') { // set text color/mode. - code = null; - // only respect text-foreground ranges and ignore the values for "black" & "white" because those - // only make sense in combination with text-background ranges which we currently not support - let token = document.createElement('span'); - token.className = ''; - while (codes.length > 0) { - code = codes.pop(); - let parsedMode = parseInt(code, 10); - if (token.className.length > 0) { - token.className += ' '; - } - if ((parsedMode >= 30 && parsedMode <= 37) || (parsedMode >= 90 && parsedMode <= 97)) { - token.className += 'code' + parsedMode; - } else if (parsedMode === 1) { - token.className += 'code-bold'; - } else if (parsedMode === 4) { - token.className += 'code-underline'; - } - } - - // we need a tokens container now - if (!tokensContainer) { - tokensContainer = document.createElement('span'); - } - - // flush text buffer if we have any - if (buffer) { - this.insert(this.linkDetector.handleLinks(buffer), currentToken || tokensContainer); - buffer = ''; - } - - currentToken = token; - - // get child until deepest nested node is found - let childPointer: Node = tokensContainer; - while (childPointer.hasChildNodes() && childPointer.firstChild.nodeName !== '#text') { - childPointer = childPointer.firstChild; - } - childPointer.appendChild(token); - - i = index; - } - } - } - - // normal text - else { - buffer += text[i]; - } - } - - // flush remaining text buffer if we have any - if (buffer) { - let res = this.linkDetector.handleLinks(buffer); - if (typeof res !== 'string' || currentToken) { - if (!tokensContainer) { - tokensContainer = document.createElement('span'); - } - - this.insert(res, currentToken || tokensContainer); - } - } - - return tokensContainer || buffer; - } - - private insert(arg: HTMLElement | string, target: HTMLElement): void { - if (typeof arg === 'string') { - target.textContent = arg; - } else { - target.appendChild(arg); - } - } - public disposeTemplate(tree: ITree, templateId: string, templateData: any): void { if (templateData.toDispose) { lifecycle.dispose(templateData.toDispose); diff --git a/src/vs/workbench/parts/debug/electron-browser/terminalSupport.ts b/src/vs/workbench/parts/debug/electron-browser/terminalSupport.ts index d6901928791..e9abd1de5f1 100644 --- a/src/vs/workbench/parts/debug/electron-browser/terminalSupport.ts +++ b/src/vs/workbench/parts/debug/electron-browser/terminalSupport.ts @@ -4,31 +4,25 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import * as platform from 'vs/base/common/platform'; -import * as cp from 'child_process'; import { IDisposable } from 'vs/base/common/lifecycle'; import { TPromise } from 'vs/base/common/winjs.base'; import { ITerminalService, ITerminalInstance } from 'vs/workbench/parts/terminal/common/terminal'; import { ITerminalService as IExternalTerminalService } from 'vs/workbench/parts/execution/common/execution'; import { ITerminalLauncher, ITerminalSettings } from 'vs/workbench/parts/debug/common/debug'; +import { hasChildprocesses, prepareCommand } from 'vs/workbench/parts/debug/node/terminals'; -const enum ShellType { cmd, powershell, bash } - -export class TerminalLauncher implements ITerminalLauncher { +export class AbstractTerminalLauncher implements ITerminalLauncher { private integratedTerminalInstance: ITerminalInstance; private terminalDisposedListener: IDisposable; - constructor( - @ITerminalService private terminalService: ITerminalService, - @IExternalTerminalService private nativeTerminalService: IExternalTerminalService - ) { + constructor(private terminalService: ITerminalService) { } - runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): TPromise { + async runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): TPromise { if (args.kind === 'external') { - return this.nativeTerminalService.runInTerminal(args.title, args.cwd, args.args, args.env || {}); + return this.runInExternalTerminal(args, config); } if (!this.terminalDisposedListener) { @@ -41,14 +35,14 @@ export class TerminalLauncher implements ITerminalLauncher { } let t = this.integratedTerminalInstance; - if ((t && this.isBusy(t)) || !t) { + if ((t && await this.isBusy(t.processId)) || !t) { t = this.terminalService.createTerminal({ name: args.title || nls.localize('debug.terminal.title', "debuggee") }); this.integratedTerminalInstance = t; } this.terminalService.setActiveInstance(t); this.terminalService.showPanel(true); - const command = this.prepareCommand(args, config); + const command = await this.prepareCommand(args, config); return new TPromise((resolve, error) => { setTimeout(_ => { @@ -58,158 +52,29 @@ export class TerminalLauncher implements ITerminalLauncher { }); } - private isBusy(t: ITerminalInstance): boolean { - if (t.processId) { - try { - // if shell has at least one child process, assume that shell is busy - if (platform.isWindows) { - const result = cp.spawnSync('wmic', ['process', 'get', 'ParentProcessId']); - if (result.stdout) { - const pids = result.stdout.toString().split('\r\n'); - if (!pids.some(p => parseInt(p) === t.processId)) { - return false; - } - } - } else { - const result = cp.spawnSync('/usr/bin/pgrep', ['-lP', String(t.processId)]); - if (result.stdout) { - const r = result.stdout.toString().trim(); - if (r.length === 0 || r.indexOf(' tmux') >= 0) { // ignore 'tmux'; see #43683 - return false; - } - } - } - } - catch (e) { - // silently ignore - } - } - // fall back to safe side - return true; + protected runInExternalTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): TPromise { + return void 0; } - private prepareCommand(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): string { + protected isBusy(processId: number): TPromise { + return TPromise.as(hasChildprocesses(processId)); + } - let shellType: ShellType; - - // get the shell configuration for the current platform - let shell: string; - const shell_config = config.integrated.shell; - if (platform.isWindows) { - shell = shell_config.windows; - shellType = ShellType.cmd; - } else if (platform.isLinux) { - shell = shell_config.linux; - shellType = ShellType.bash; - } else if (platform.isMacintosh) { - shell = shell_config.osx; - shellType = ShellType.bash; - } - - // try to determine the shell type - shell = shell.trim().toLowerCase(); - if (shell.indexOf('powershell') >= 0) { - shellType = ShellType.powershell; - } else if (shell.indexOf('cmd.exe') >= 0) { - shellType = ShellType.cmd; - } else if (shell.indexOf('bash') >= 0) { - shellType = ShellType.bash; - } else if (shell.indexOf('git\\bin\\bash.exe') >= 0) { - shellType = ShellType.bash; - } - - let quote: (s: string) => string; - let command = ''; - - switch (shellType) { - - case ShellType.powershell: - - quote = (s: string) => { - s = s.replace(/\'/g, '\'\''); - return `'${s}'`; - //return s.indexOf(' ') >= 0 || s.indexOf('\'') >= 0 || s.indexOf('"') >= 0 ? `'${s}'` : s; - }; - - if (args.cwd) { - command += `cd '${args.cwd}'; `; - } - if (args.env) { - for (let key in args.env) { - const value = args.env[key]; - if (value === null) { - command += `Remove-Item env:${key}; `; - } else { - command += `\${env:${key}}='${value}'; `; - } - } - } - if (args.args && args.args.length > 0) { - const cmd = quote(args.args.shift()); - command += (cmd[0] === '\'') ? `& ${cmd} ` : `${cmd} `; - for (let a of args.args) { - command += `${quote(a)} `; - } - } - break; - - case ShellType.cmd: - - quote = (s: string) => { - s = s.replace(/\"/g, '""'); - return (s.indexOf(' ') >= 0 || s.indexOf('"') >= 0) ? `"${s}"` : s; - }; - - if (args.cwd) { - command += `cd ${quote(args.cwd)} && `; - } - if (args.env) { - command += 'cmd /C "'; - for (let key in args.env) { - const value = args.env[key]; - if (value === null) { - command += `set "${key}=" && `; - } else { - command += `set "${key}=${args.env[key]}" && `; - } - } - } - for (let a of args.args) { - command += `${quote(a)} `; - } - if (args.env) { - command += '"'; - } - break; - - case ShellType.bash: - - quote = (s: string) => { - s = s.replace(/\"/g, '\\"'); - return (s.indexOf(' ') >= 0 || s.indexOf('\\') >= 0) ? `"${s}"` : s; - }; - - if (args.cwd) { - command += `cd ${quote(args.cwd)} ; `; - } - if (args.env) { - command += 'env'; - for (let key in args.env) { - const value = args.env[key]; - if (value === null) { - command += ` -u "${key}"`; - } else { - command += ` "${key}=${value}"`; - } - } - command += ' '; - } - for (let a of args.args) { - command += `${quote(a)} `; - } - break; - } - - return command; + protected prepareCommand(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): TPromise { + return TPromise.as(prepareCommand(args, config)); + } +} + +export class TerminalLauncher extends AbstractTerminalLauncher { + + constructor( + @ITerminalService terminalService: ITerminalService, + @IExternalTerminalService private nativeTerminalService: IExternalTerminalService + ) { + super(terminalService); + } + + runInExternalTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): TPromise { + return this.nativeTerminalService.runInTerminal(args.title, args.cwd, args.args, args.env || {}); } } diff --git a/src/vs/workbench/parts/debug/node/terminals.ts b/src/vs/workbench/parts/debug/node/terminals.ts index c0cfca5db35..a46ea642248 100644 --- a/src/vs/workbench/parts/debug/node/terminals.ts +++ b/src/vs/workbench/parts/debug/node/terminals.ts @@ -258,3 +258,161 @@ function quote(args: string[]): string { } return r; } + + +export function hasChildprocesses(processId: number): boolean { + if (processId) { + try { + // if shell has at least one child process, assume that shell is busy + if (env.isWindows) { + const result = cp.spawnSync('wmic', ['process', 'get', 'ParentProcessId']); + if (result.stdout) { + const pids = result.stdout.toString().split('\r\n'); + if (!pids.some(p => parseInt(p) === processId)) { + return false; + } + } + } else { + const result = cp.spawnSync('/usr/bin/pgrep', ['-lP', String(processId)]); + if (result.stdout) { + const r = result.stdout.toString().trim(); + if (r.length === 0 || r.indexOf(' tmux') >= 0) { // ignore 'tmux'; see #43683 + return false; + } + } + } + } + catch (e) { + // silently ignore + } + } + // fall back to safe side + return true; +} + +const enum ShellType { cmd, powershell, bash } + +export function prepareCommand(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): string { + + let shellType: ShellType; + + // get the shell configuration for the current platform + let shell: string; + const shell_config = config.integrated.shell; + if (env.isWindows) { + shell = shell_config.windows; + shellType = ShellType.cmd; + } else if (env.isLinux) { + shell = shell_config.linux; + shellType = ShellType.bash; + } else if (env.isMacintosh) { + shell = shell_config.osx; + shellType = ShellType.bash; + } + + // try to determine the shell type + shell = shell.trim().toLowerCase(); + if (shell.indexOf('powershell') >= 0) { + shellType = ShellType.powershell; + } else if (shell.indexOf('cmd.exe') >= 0) { + shellType = ShellType.cmd; + } else if (shell.indexOf('bash') >= 0) { + shellType = ShellType.bash; + } else if (shell.indexOf('git\\bin\\bash.exe') >= 0) { + shellType = ShellType.bash; + } + + let quote: (s: string) => string; + let command = ''; + + switch (shellType) { + + case ShellType.powershell: + + quote = (s: string) => { + s = s.replace(/\'/g, '\'\''); + return `'${s}'`; + //return s.indexOf(' ') >= 0 || s.indexOf('\'') >= 0 || s.indexOf('"') >= 0 ? `'${s}'` : s; + }; + + if (args.cwd) { + command += `cd '${args.cwd}'; `; + } + if (args.env) { + for (let key in args.env) { + const value = args.env[key]; + if (value === null) { + command += `Remove-Item env:${key}; `; + } else { + command += `\${env:${key}}='${value}'; `; + } + } + } + if (args.args && args.args.length > 0) { + const cmd = quote(args.args.shift()); + command += (cmd[0] === '\'') ? `& ${cmd} ` : `${cmd} `; + for (let a of args.args) { + command += `${quote(a)} `; + } + } + break; + + case ShellType.cmd: + + quote = (s: string) => { + s = s.replace(/\"/g, '""'); + return (s.indexOf(' ') >= 0 || s.indexOf('"') >= 0) ? `"${s}"` : s; + }; + + if (args.cwd) { + command += `cd ${quote(args.cwd)} && `; + } + if (args.env) { + command += 'cmd /C "'; + for (let key in args.env) { + const value = args.env[key]; + if (value === null) { + command += `set "${key}=" && `; + } else { + command += `set "${key}=${args.env[key]}" && `; + } + } + } + for (let a of args.args) { + command += `${quote(a)} `; + } + if (args.env) { + command += '"'; + } + break; + + case ShellType.bash: + + quote = (s: string) => { + s = s.replace(/\"/g, '\\"'); + return (s.indexOf(' ') >= 0 || s.indexOf('\\') >= 0) ? `"${s}"` : s; + }; + + if (args.cwd) { + command += `cd ${quote(args.cwd)} ; `; + } + if (args.env) { + command += 'env'; + for (let key in args.env) { + const value = args.env[key]; + if (value === null) { + command += ` -u "${key}"`; + } else { + command += ` "${key}=${value}"`; + } + } + command += ' '; + } + for (let a of args.args) { + command += `${quote(a)} `; + } + break; + } + + return command; +} \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/test/browser/debugANSIHandling.test.ts b/src/vs/workbench/parts/debug/test/browser/debugANSIHandling.test.ts new file mode 100644 index 00000000000..2b2cf4f3297 --- /dev/null +++ b/src/vs/workbench/parts/debug/test/browser/debugANSIHandling.test.ts @@ -0,0 +1,264 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import * as dom from 'vs/base/browser/dom'; +import { generateUuid } from 'vs/base/common/uuid'; +import { appendStylizedStringToContainer, handleANSIOutput } from 'vs/workbench/parts/debug/browser/debugANSIHandling'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import { workbenchInstantiationService } from 'vs/workbench/test/workbenchTestServices'; +import { LinkDetector } from 'vs/workbench/parts/debug/browser/linkDetector'; + +suite('Debug - ANSI Handling', () => { + + let linkDetector: LinkDetector; + + /** + * Instantiate a {@link LinkDetector} for use by the functions being tested. + */ + setup(() => { + const instantiationService: TestInstantiationService = workbenchInstantiationService(); + linkDetector = instantiationService.createInstance(LinkDetector); + }); + + test('appendStylizedStringToContainer', () => { + const root: HTMLSpanElement = document.createElement('span'); + let child: Node; + + assert.equal(0, root.children.length); + + appendStylizedStringToContainer(root, 'content1', ['class1', 'class2'], linkDetector); + appendStylizedStringToContainer(root, 'content2', ['class2', 'class3'], linkDetector); + + assert.equal(2, root.children.length); + + child = root.firstChild; + if (child instanceof HTMLSpanElement) { + assert.equal('content1', child.textContent); + assert(dom.hasClass(child, 'class1')); + assert(dom.hasClass(child, 'class2')); + } else { + assert.fail(); + } + + child = root.lastChild; + if (child instanceof HTMLSpanElement) { + assert.equal('content2', child.textContent); + assert(dom.hasClass(child, 'class2')); + assert(dom.hasClass(child, 'class3')); + } else { + assert.fail(); + } + }); + + /** + * Apply an ANSI sequence to {@link #getSequenceOutput}. + * + * @param sequence The ANSI sequence to stylize. + * @returns An {@link HTMLSpanElement} that contains the stylized text. + */ + function getSequenceOutput(sequence: string): HTMLSpanElement { + const root: HTMLSpanElement = handleANSIOutput(sequence, linkDetector); + assert.equal(1, root.children.length); + const child: Node = root.lastChild; + if (child instanceof HTMLSpanElement) { + return child; + } else { + assert.fail(); + return null; + } + } + + /** + * Assert that a given ANSI sequence maintains added content following the ANSI code, and that + * the provided {@param assertion} passes. + * + * @param sequence The ANSI sequence to verify. The provided sequence should contain ANSI codes + * only, and should not include actual text content as it is provided by this function. + * @param assertion The function used to verify the output. + */ + function assertSingleSequenceElement(sequence: string, assertion: (child: HTMLSpanElement) => void): void { + const child: HTMLSpanElement = getSequenceOutput(sequence + 'content'); + assert.equal('content', child.textContent); + assertion(child); + } + + test('Expected single sequence operation', () => { + + // Bold code + assertSingleSequenceElement('\x1b[1m', (child) => { + assert(dom.hasClass(child, 'code-bold')); + }); + + // Underline code + assertSingleSequenceElement('\x1b[4m', (child) => { + assert(dom.hasClass(child, 'code-underline')); + }); + + for (let i = 30; i <= 37; i++) { + const style: string = 'code-foreground-' + i; + + // Foreground colour codes + assertSingleSequenceElement('\x1b[' + i + 'm', (child) => { + assert(dom.hasClass(child, style)); + }); + + // Cancellation code removes colour code + assertSingleSequenceElement('\x1b[' + i + ';39m', (child) => { + assert(dom.hasClass(child, style) === false); + }); + } + + // Codes do not interfere + assertSingleSequenceElement('\x1b[1;4;30;31;32;33;34;35;36;37m', (child) => { + assert.equal(10, child.classList.length); + + assert(dom.hasClass(child, 'code-bold')); + assert(dom.hasClass(child, 'code-underline')); + for (let i = 30; i <= 37; i++) { + assert(dom.hasClass(child, 'code-foreground-' + i)); + } + }); + + // Duplicate codes do not change output + assertSingleSequenceElement('\x1b[1;1;4;1;4;4;1;4m', (child) => { + assert(dom.hasClass(child, 'code-bold')); + assert(dom.hasClass(child, 'code-underline')); + }); + + // Extra terminating semicolon does not change output + assertSingleSequenceElement('\x1b[1;4;m', (child) => { + assert(dom.hasClass(child, 'code-bold')); + assert(dom.hasClass(child, 'code-underline')); + }); + + // Cancellation code removes all codes + assertSingleSequenceElement('\x1b[1;4;30;31;32;33;34;35;36;37;0m', (child) => { + assert.equal(0, child.classList.length); + }); + + }); + + /** + * Assert that a given ANSI sequence produces the expected number of {@link HTMLSpanElement} children. For + * each child, run the provided assertion. + * + * @param sequence The ANSI sequence to verify. + * @param assertions A set of assertions to run on the resulting children. + */ + function assertMultipleSequenceElements(sequence: string, assertions: Array<(child: HTMLSpanElement) => void>): void { + const root: HTMLSpanElement = handleANSIOutput(sequence, linkDetector); + assert.equal(assertions.length, root.children.length); + for (let i = 0; i < assertions.length; i++) { + const child: Node = root.children[i]; + if (child instanceof HTMLSpanElement) { + assertions[i](child); + } else { + assert.fail(); + } + } + } + + test('Expected multiple sequence operation', () => { + + // Multiple codes affect the same text + assertSingleSequenceElement('\x1b[1m\x1b[4m\x1b[32m', (child) => { + assert(dom.hasClass(child, 'code-bold')); + assert(dom.hasClass(child, 'code-underline')); + assert(dom.hasClass(child, 'code-foreground-32')); + }); + + // Consecutive codes do not affect previous ones + assertMultipleSequenceElements('\x1b[1mbold\x1b[32mgreen\x1b[4munderline\x1b[0mnothing', [ + (bold) => { + assert.equal(1, bold.classList.length); + assert(dom.hasClass(bold, 'code-bold')); + }, + (green) => { + assert.equal(2, green.classList.length); + assert(dom.hasClass(green, 'code-bold')); + assert(dom.hasClass(green, 'code-foreground-32')); + }, + (underline) => { + assert.equal(3, underline.classList.length); + assert(dom.hasClass(underline, 'code-bold')); + assert(dom.hasClass(underline, 'code-foreground-32')); + assert(dom.hasClass(underline, 'code-underline')); + }, + (nothing) => { + assert.equal(0, nothing.classList.length); + }, + ]); + + }); + + /** + * Assert that the provided ANSI sequence exactly matches the text content of the resulting + * {@link HTMLSpanElement}. + * + * @param sequence The ANSI sequence to verify. + */ + function assertSequenceEqualToContent(sequence: string): void { + const child: HTMLSpanElement = getSequenceOutput(sequence); + assert(child.textContent === sequence); + } + + test('Invalid codes treated as regular text', () => { + + // Individual components of ANSI code start are printed + assertSequenceEqualToContent('\x1b'); + assertSequenceEqualToContent('['); + + // Unsupported sequence prints both characters + assertSequenceEqualToContent('\x1b['); + + // Random strings are displayed properly + for (let i = 0; i < 50; i++) { + const uuid: string = generateUuid(); + assertSequenceEqualToContent(uuid); + } + + }); + + /** + * Assert that a given ANSI sequence maintains added content following the ANSI code, and that + * the expression itself is thrown away. + * + * @param sequence The ANSI sequence to verify. The provided sequence should contain ANSI codes + * only, and should not include actual text content as it is provided by this function. + */ + function assertEmptyOutput(sequence: string) { + const child: HTMLSpanElement = getSequenceOutput(sequence + 'content'); + assert.equal('content', child.textContent); + assert.equal(0, child.classList.length); + } + + test('Empty sequence output', () => { + + const sequences: string[] = [ + // No colour codes + '', + '\x1b[;m', + '\x1b[1;;m', + '\x1b[m', + // Unsupported colour codes + '\x1b[30;40m', + '\x1b[100m' + ]; + + sequences.forEach(sequence => { + assertEmptyOutput(sequence); + }); + + // Check other possible ANSI terminators + const terminators: string[] = 'ABCDHIJKfhmpsu'.split(''); + + terminators.forEach(terminator => { + assertEmptyOutput('\x1b[content' + terminator); + }); + + }); + +}); diff --git a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsTipsService.test.ts b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsTipsService.test.ts index e99f9a52370..3f7af9a154c 100644 --- a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsTipsService.test.ts +++ b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsTipsService.test.ts @@ -70,6 +70,7 @@ const mockExtensionGallery: IGalleryExtension[] = [ icon: { uri: 'uri:icon', fallbackUri: 'fallback:icon' }, license: { uri: 'uri:license', fallbackUri: 'fallback:license' }, repository: { uri: 'uri:repository', fallbackUri: 'fallback:repository' }, + coreTranslations: {} }), aGalleryExtension('MockExtension2', { displayName: 'Mock Extension 2', @@ -91,6 +92,7 @@ const mockExtensionGallery: IGalleryExtension[] = [ icon: { uri: 'uri:icon', fallbackUri: 'fallback:icon' }, license: { uri: 'uri:license', fallbackUri: 'fallback:license' }, repository: { uri: 'uri:repository', fallbackUri: 'fallback:repository' }, + coreTranslations: {} }) ]; @@ -148,7 +150,8 @@ const noAssets: IGalleryExtensionAssets = { license: null, manifest: null, readme: null, - repository: null + repository: null, + coreTranslations: null }; function aGalleryExtension(name: string, properties: any = {}, galleryExtensionProperties: any = {}, assets: IGalleryExtensionAssets = noAssets): IGalleryExtension { diff --git a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsWorkbenchService.test.ts b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsWorkbenchService.test.ts index 2789c6b4322..40c885f86e0 100644 --- a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsWorkbenchService.test.ts +++ b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsWorkbenchService.test.ts @@ -116,6 +116,7 @@ suite('ExtensionsWorkbenchService Test', () => { icon: { uri: 'uri:icon', fallbackUri: 'fallback:icon' }, license: { uri: 'uri:license', fallbackUri: 'fallback:license' }, repository: { uri: 'uri:repository', fallbackUri: 'fallback:repository' }, + coreTranslations: {} }); testObject = instantiationService.createInstance(ExtensionsWorkbenchService); @@ -265,6 +266,7 @@ suite('ExtensionsWorkbenchService Test', () => { icon: { uri: 'uri:icon', fallbackUri: 'fallback:icon' }, license: { uri: 'uri:license', fallbackUri: 'fallback:license' }, repository: { uri: 'uri:repository', fallbackUri: 'fallback:repository' }, + coreTranslations: {} }); instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local1, local2]); instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery1)); @@ -1207,7 +1209,8 @@ suite('ExtensionsWorkbenchService Test', () => { license: null, manifest: null, readme: null, - repository: null + repository: null, + coreTranslations: null }; function aGalleryExtension(name: string, properties: any = {}, galleryExtensionProperties: any = {}, assets: IGalleryExtensionAssets = noAssets): IGalleryExtension { diff --git a/src/vs/workbench/parts/localizations/electron-browser/localizations.contribution.ts b/src/vs/workbench/parts/localizations/electron-browser/localizations.contribution.ts index 6bc4493acf9..c571c09b1b2 100644 --- a/src/vs/workbench/parts/localizations/electron-browser/localizations.contribution.ts +++ b/src/vs/workbench/parts/localizations/electron-browser/localizations.contribution.ts @@ -73,15 +73,17 @@ export class LocalizationWorkbenchContribution extends Disposable implements IWo if (!this.storageService.getBoolean(donotAskUpdateKey) && e.local && e.operation === InstallOperation.Install && e.local.manifest.contributes && e.local.manifest.contributes.localizations && e.local.manifest.contributes.localizations.length) { const locale = e.local.manifest.contributes.localizations[0].languageId; if (platform.language !== locale) { + const updateAndRestart = platform.locale !== locale; this.notificationService.prompt( Severity.Info, - localize('updateLocale', "Would you like to change VS Code's UI language to {0} and restart?", e.local.manifest.contributes.localizations[0].languageName || e.local.manifest.contributes.localizations[0].languageId), + updateAndRestart ? localize('updateLocale', "Would you like to change VS Code's UI language to {0} and restart?", e.local.manifest.contributes.localizations[0].languageName || e.local.manifest.contributes.localizations[0].languageId) + : localize('activateLanguagePack', "Would you like to restart VS Code to activate the language pack that was just installed?"), [{ label: localize('yes', "Yes"), run: () => { const file = URI.file(join(this.environmentService.appSettingsHome, 'locale.json')); - this.jsonEditingService.write(file, { key: 'locale', value: locale }, true) - .then(() => this.windowsService.relaunch({}), e => this.notificationService.error(e)); + const updatePromise = updateAndRestart ? this.jsonEditingService.write(file, { key: 'locale', value: locale }, true) : TPromise.as(null); + updatePromise.then(() => this.windowsService.relaunch({}), e => this.notificationService.error(e)); } }, { label: localize('no', "No"), @@ -96,31 +98,39 @@ export class LocalizationWorkbenchContribution extends Disposable implements IWo } } + private migrateToMarketplaceLanguagePack(language: string): void { + this.isLanguageInstalled(language) + .then(installed => { + if (!installed) { + this.getLanguagePackExtension(language) + .then(extension => { + if (extension) { + this.notificationService.prompt(Severity.Warning, localize('install language pack', "In the near future, VS Code will only support language packs in the form of Marketplace extensions. Please install the '{0}' extension in order to continue to use the currently configured language. ", extension.displayName || extension.displayName), + [ + { label: localize('install', "Install"), run: () => this.installExtension(extension) }, + { label: localize('more information', "More Information..."), run: () => window.open('https://go.microsoft.com/fwlink/?linkid=872941') } + ]); + } + }); + } + }); + } + private checkAndInstall(): void { const language = platform.language; const locale = platform.locale; - if (language !== 'en' && language !== 'en_us') { - this.isLanguageInstalled(language) - .then(installed => { - if (!installed) { - this.getLanguagePackExtension(language) - .then(extension => { - if (extension) { - this.notificationService.prompt(Severity.Warning, localize('install language pack', "In the near future, VS Code will only support language packs in the form of Marketplace extensions. Please install the '{0}' extension in order to continue to use the currently configured language. ", extension.displayName || extension.displayName), - [ - { label: localize('install', "Install"), run: () => this.installExtension(extension) }, - { label: localize('more information', "More Information..."), run: () => window.open('https://go.microsoft.com/fwlink/?linkid=872941') } - ]); - } - }); - } - }); + const languagePackSuggestionIgnoreList = JSON.parse(this.storageService.get('extensionsAssistant/languagePackSuggestionIgnore', StorageScope.GLOBAL, '[]')); + + if (!this.galleryService.isEnabled()) { + return; + } + if (language !== 'en' && language !== 'en_us') { + this.migrateToMarketplaceLanguagePack(language); + return; + } + if (locale === 'en' || locale === 'en_us') { return; } - - const languagePackSuggestionIgnoreList = JSON.parse(this.storageService.get - ('extensionsAssistant/languagePackSuggestionIgnore', StorageScope.GLOBAL, '[]')); - if (language === locale || languagePackSuggestionIgnoreList.indexOf(language) > -1) { return; } @@ -146,86 +156,78 @@ export class LocalizationWorkbenchContribution extends Disposable implements IWo return; } - this.galleryService.getManifest(extensionToFetchTranslationsFrom).then(x => { - if (!x.contributes || !x.contributes.localizations) { - return; - } - const locContribution = x.contributes.localizations.filter(x => x.languageId.toLowerCase() === locale)[0]; - if (!locContribution) { - return; - } + this.galleryService.getCoreTranslations(extensionToFetchTranslationsFrom, locale) + .then(coreTranslation => { + const translations = { + ...minimumTranslatedStrings, + ...(coreTranslation ? coreTranslation['vs/platform/node/minimalTranslations'] : {}) + }; + const logUserReaction = (userReaction: string) => { + /* __GDPR__ + "languagePackSuggestion:popup" : { + "userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "language": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + } + */ + this.telemetryService.publicLog('languagePackSuggestion:popup', { userReaction, language }); + }; - const translations = { - ...minimumTranslatedStrings, - ...(locContribution.minimalTranslations || {}) - }; - - const logUserReaction = (userReaction: string) => { - /* __GDPR__ - "languagePackSuggestion:popup" : { - "userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "language": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - this.telemetryService.publicLog('languagePackSuggestion:popup', { userReaction, language }); - }; - - const searchAction = { - label: translations['searchMarketplace'], - run: () => { - logUserReaction('search'); - this.viewletService.openViewlet(EXTENSIONS_VIEWLET_ID, true) - .then(viewlet => viewlet as IExtensionsViewlet) - .then(viewlet => { - viewlet.search(`tag:lp-${locale}`); - viewlet.focus(); - }); - } - }; - - const installAction = { - label: translations['install'], - run: () => { - logUserReaction('install'); - this.installExtension(extensionToInstall); - } - }; - - const installAndRestartAction = { - label: translations['installAndRestart'], - run: () => { - logUserReaction('installAndRestart'); - this.installExtension(extensionToInstall).then(() => this.windowsService.relaunch({})); - } - }; - - const mainActions = extensionToInstall ? [installAndRestartAction, installAction] : [searchAction]; - const promptMessage = translations[extensionToInstall ? 'installAndRestartMessage' : 'showLanguagePackExtensions'] - .replace('{0}', locContribution.languageNameLocalized || locContribution.languageName || locale); - - this.notificationService.prompt( - Severity.Info, - promptMessage, - [...mainActions, - { - label: localize('neverAgain', "Don't Show Again"), - isSecondary: true, + const searchAction = { + label: translations['searchMarketplace'], run: () => { - languagePackSuggestionIgnoreList.push(language); - this.storageService.store( - 'extensionsAssistant/languagePackSuggestionIgnore', - JSON.stringify(languagePackSuggestionIgnoreList), - StorageScope.GLOBAL - ); - logUserReaction('neverShowAgain'); + logUserReaction('search'); + this.viewletService.openViewlet(EXTENSIONS_VIEWLET_ID, true) + .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => { + viewlet.search(`tag:lp-${locale}`); + viewlet.focus(); + }); } - }], - () => { - logUserReaction('cancelled'); - } - ); + }; - }); + const installAction = { + label: translations['install'], + run: () => { + logUserReaction('install'); + this.installExtension(extensionToInstall); + } + }; + + const installAndRestartAction = { + label: translations['installAndRestart'], + run: () => { + logUserReaction('installAndRestart'); + this.installExtension(extensionToInstall).then(() => this.windowsService.relaunch({})); + } + }; + + const mainActions = extensionToInstall ? [installAndRestartAction, installAction] : [searchAction]; + const promptMessage = translations[extensionToInstall ? 'installAndRestartMessage' : 'showLanguagePackExtensions'] + .replace('{0}', locale); + + this.notificationService.prompt( + Severity.Info, + promptMessage, + [...mainActions, + { + label: localize('neverAgain', "Don't Show Again"), + isSecondary: true, + run: () => { + languagePackSuggestionIgnoreList.push(language); + this.storageService.store( + 'extensionsAssistant/languagePackSuggestionIgnore', + JSON.stringify(languagePackSuggestionIgnoreList), + StorageScope.GLOBAL + ); + logUserReaction('neverShowAgain'); + } + }], + () => { + logUserReaction('cancelled'); + } + ); + + }); }); }); diff --git a/src/vs/workbench/parts/search/browser/searchViewLocationUpdater.ts b/src/vs/workbench/parts/search/browser/searchViewLocationUpdater.ts index 851e30dc4e5..d5b6bf21e19 100644 --- a/src/vs/workbench/parts/search/browser/searchViewLocationUpdater.ts +++ b/src/vs/workbench/parts/search/browser/searchViewLocationUpdater.ts @@ -16,23 +16,29 @@ export class SearchViewLocationUpdater implements IWorkbenchContribution { @IPanelService panelService: IPanelService, @IConfigurationService configurationService: IConfigurationService ) { - const updateSearchViewLocation = () => { + const updateSearchViewLocation = (open: boolean) => { const config = configurationService.getValue(); if (config.search.location === 'panel') { viewletService.setViewletEnablement(VIEW_ID, false); panelService.setPanelEnablement(VIEW_ID, true); + if (open) { + panelService.openPanel(VIEW_ID); + } } else { panelService.setPanelEnablement(VIEW_ID, false); viewletService.setViewletEnablement(VIEW_ID, true); + if (open) { + viewletService.openViewlet(VIEW_ID); + } } }; configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration('search.location')) { - updateSearchViewLocation(); + updateSearchViewLocation(true); } }); - updateSearchViewLocation(); + updateSearchViewLocation(false); } } diff --git a/src/vs/workbench/parts/tasks/common/tasks.ts b/src/vs/workbench/parts/tasks/common/tasks.ts index 47cf212ecb4..f1528ca95ce 100644 --- a/src/vs/workbench/parts/tasks/common/tasks.ts +++ b/src/vs/workbench/parts/tasks/common/tasks.ts @@ -431,6 +431,8 @@ export interface CustomTask extends CommonTask, ConfigurationProperties { identifier: string; + hasDefinedMatchers: boolean; + /** * The command configuration */ diff --git a/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts b/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts index 51af22e467d..54719671354 100644 --- a/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts +++ b/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts @@ -847,7 +847,7 @@ class TaskService implements ITaskService { } if (CustomTask.is(task)) { let configProperties: TaskConfig.ConfigurationProperties = task._source.config.element; - return configProperties.problemMatcher === void 0; + return configProperties.problemMatcher === void 0 && !task.hasDefinedMatchers; } return false; } diff --git a/src/vs/workbench/parts/tasks/node/taskConfiguration.ts b/src/vs/workbench/parts/tasks/node/taskConfiguration.ts index c4fbf4293a6..0ad38de152a 100644 --- a/src/vs/workbench/parts/tasks/node/taskConfiguration.ts +++ b/src/vs/workbench/parts/tasks/node/taskConfiguration.ts @@ -1337,6 +1337,7 @@ namespace CustomTask { _label: taskName, name: taskName, identifier: taskName, + hasDefinedMatchers: false, command: undefined }; let configuration = ConfigurationProperties.from(external, context, false); @@ -1375,6 +1376,10 @@ namespace CustomTask { if (CommandConfiguration.hasCommand(task.command) || task.dependsOn === void 0) { task.command = CommandConfiguration.fillGlobals(task.command, globals.command, task.name); } + if (task.problemMatchers === void 0 && globals.problemMatcher !== void 0) { + task.problemMatchers = Objects.deepClone(globals.problemMatcher); + task.hasDefinedMatchers = true; + } // promptOnClose is inferred from isBackground if available if (task.promptOnClose === void 0 && task.isBackground === void 0 && globals.promptOnClose !== void 0) { task.promptOnClose = globals.promptOnClose; @@ -1405,7 +1410,8 @@ namespace CustomTask { type: 'custom', command: contributedTask.command, name: configuredProps.name || contributedTask.name, - identifier: configuredProps.identifier || contributedTask.identifier + identifier: configuredProps.identifier || contributedTask.identifier, + hasDefinedMatchers: false }; let resultConfigProps: Tasks.ConfigurationProperties = result; @@ -1430,6 +1436,10 @@ namespace CustomTask { result.command.presentation, contributedConfigProps.presentation); result.command.options = CommandOptions.fillProperties(result.command.options, contributedConfigProps.options); + if (contributedTask.hasDefinedMatchers === true) { + result.hasDefinedMatchers = true; + } + return result; } } @@ -1545,6 +1555,7 @@ namespace TaskParser { interface Globals { command?: Tasks.CommandConfiguration; + problemMatcher?: ProblemMatcher[]; promptOnClose?: boolean; suppressTaskName?: boolean; } @@ -1581,6 +1592,9 @@ namespace Globals { if (config.promptOnClose !== void 0) { result.promptOnClose = !!config.promptOnClose; } + if (config.problemMatcher) { + result.problemMatcher = ProblemMatcherConverter.from(config.problemMatcher, context); + } return result; } @@ -1830,7 +1844,8 @@ class ConfigurationParser { suppressTaskName: true }, isBackground: isBackground, - problemMatchers: matchers + problemMatchers: matchers, + hasDefinedMatchers: false, }; let value = GroupKind.from(fileConfig.group); if (value) { diff --git a/src/vs/workbench/parts/tasks/test/electron-browser/configuration.test.ts b/src/vs/workbench/parts/tasks/test/electron-browser/configuration.test.ts index 725807575b4..ff3f2ef5297 100644 --- a/src/vs/workbench/parts/tasks/test/electron-browser/configuration.test.ts +++ b/src/vs/workbench/parts/tasks/test/electron-browser/configuration.test.ts @@ -188,7 +188,8 @@ class CustomTaskBuilder { command: this.commandBuilder.result, isBackground: false, promptOnClose: true, - problemMatchers: [] + problemMatchers: [], + hasDefinedMatchers: false }; } diff --git a/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.ts b/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.ts index 7de5dc572e2..335ba65618e 100644 --- a/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.ts +++ b/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.ts @@ -6,8 +6,8 @@ import { localize } from 'vs/nls'; import { WalkThroughInput } from 'vs/workbench/parts/welcome/walkThrough/node/walkThroughInput'; -import { WalkThroughPart, WALK_THROUGH_FOCUS } from 'vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart'; -import { WalkThroughArrowUpAction, WalkThroughArrowDownAction, WalkThroughPageUpAction, WalkThroughPageDownAction } from 'vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions'; +import { WalkThroughPart } from 'vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart'; +import { WalkThroughArrowUp, WalkThroughArrowDown, WalkThroughPageUp, WalkThroughPageDown } from 'vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions'; import { WalkThroughContentProvider, WalkThroughSnippetContentProvider } from 'vs/workbench/parts/welcome/walkThrough/node/walkThroughContentProvider'; import { EditorWalkThroughAction, EditorWalkThroughInputFactory } from 'vs/workbench/parts/welcome/walkThrough/electron-browser/editor/editorWalkThrough'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -16,11 +16,9 @@ import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; -import { KeyCode } from 'vs/base/common/keyCodes'; -import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IEditorRegistry, Extensions as EditorExtensions, EditorDescriptor } from 'vs/workbench/browser/editor'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; Registry.as(EditorExtensions.Editors) .registerEditor(new EditorDescriptor( @@ -43,14 +41,10 @@ Registry.as(WorkbenchExtensions.Workbench) Registry.as(WorkbenchExtensions.Workbench) .registerWorkbenchContribution(WalkThroughSnippetContentProvider, LifecyclePhase.Starting); -Registry.as(Extensions.WorkbenchActions) - .registerWorkbenchAction(new SyncActionDescriptor(WalkThroughArrowUpAction, WalkThroughArrowUpAction.ID, WalkThroughArrowUpAction.LABEL, { primary: KeyCode.UpArrow }, ContextKeyExpr.and(WALK_THROUGH_FOCUS, EditorContextKeys.editorTextFocus.toNegated())), 'Interactive Playground: Scroll Up (Line)', localize('interactivePlayground', "Interactive Playground")); +KeybindingsRegistry.registerCommandAndKeybindingRule(WalkThroughArrowUp); -Registry.as(Extensions.WorkbenchActions) - .registerWorkbenchAction(new SyncActionDescriptor(WalkThroughArrowDownAction, WalkThroughArrowDownAction.ID, WalkThroughArrowDownAction.LABEL, { primary: KeyCode.DownArrow }, ContextKeyExpr.and(WALK_THROUGH_FOCUS, EditorContextKeys.editorTextFocus.toNegated())), 'Interactive Playground: Scroll Down (Line)', localize('interactivePlayground', "Interactive Playground")); +KeybindingsRegistry.registerCommandAndKeybindingRule(WalkThroughArrowDown); -Registry.as(Extensions.WorkbenchActions) - .registerWorkbenchAction(new SyncActionDescriptor(WalkThroughPageUpAction, WalkThroughPageUpAction.ID, WalkThroughPageUpAction.LABEL, { primary: KeyCode.PageUp }, ContextKeyExpr.and(WALK_THROUGH_FOCUS, EditorContextKeys.editorTextFocus.toNegated())), 'Interactive Playground: Scroll Up (Page)', localize('interactivePlayground', "Interactive Playground")); +KeybindingsRegistry.registerCommandAndKeybindingRule(WalkThroughPageUp); -Registry.as(Extensions.WorkbenchActions) - .registerWorkbenchAction(new SyncActionDescriptor(WalkThroughPageDownAction, WalkThroughPageDownAction.ID, WalkThroughPageDownAction.LABEL, { primary: KeyCode.PageDown }, ContextKeyExpr.and(WALK_THROUGH_FOCUS, EditorContextKeys.editorTextFocus.toNegated())), 'Interactive Playground: Scroll Down (Page)', localize('interactivePlayground', "Interactive Playground")); +KeybindingsRegistry.registerCommandAndKeybindingRule(WalkThroughPageDown); diff --git a/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.ts b/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.ts index 1e5bfeabad6..5a838046aa8 100644 --- a/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.ts +++ b/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.ts @@ -4,96 +4,65 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { localize } from 'vs/nls'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { Action } from 'vs/base/common/actions'; -import { TPromise } from 'vs/base/common/winjs.base'; -import { WalkThroughPart } from 'vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart'; +import { WalkThroughPart, WALK_THROUGH_FOCUS } from 'vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart'; +import { ICommandAndKeybindingRule, KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { KeyCode } from 'vs/base/common/keyCodes'; -export class WalkThroughArrowUpAction extends Action { - - public static readonly ID = 'workbench.action.interactivePlayground.arrowUp'; - public static readonly LABEL = localize('editorWalkThrough.arrowUp', "Scroll Up (Line)"); - - constructor( - id: string, - label: string, - @IEditorService private editorService: IEditorService - ) { - super(id, label); - } - - public run(): TPromise { - const activeControl = this.editorService.activeControl; +export const WalkThroughArrowUp: ICommandAndKeybindingRule = { + id: 'workbench.action.interactivePlayground.arrowUp', + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + when: ContextKeyExpr.and(WALK_THROUGH_FOCUS, EditorContextKeys.editorTextFocus.toNegated()), + primary: KeyCode.UpArrow, + handler: accessor => { + const editorService = accessor.get(IEditorService); + const activeControl = editorService.activeControl; if (activeControl instanceof WalkThroughPart) { activeControl.arrowUp(); } - return null; } -} +}; -export class WalkThroughArrowDownAction extends Action { - - public static readonly ID = 'workbench.action.interactivePlayground.arrowDown'; - public static readonly LABEL = localize('editorWalkThrough.arrowDown', "Scroll Down (Line)"); - - constructor( - id: string, - label: string, - @IEditorService private editorService: IEditorService - ) { - super(id, label); - } - - public run(): TPromise { - const activeControl = this.editorService.activeControl; +export const WalkThroughArrowDown: ICommandAndKeybindingRule = { + id: 'workbench.action.interactivePlayground.arrowDown', + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + when: ContextKeyExpr.and(WALK_THROUGH_FOCUS, EditorContextKeys.editorTextFocus.toNegated()), + primary: KeyCode.DownArrow, + handler: accessor => { + const editorService = accessor.get(IEditorService); + const activeControl = editorService.activeControl; if (activeControl instanceof WalkThroughPart) { activeControl.arrowDown(); } - return null; } -} +}; -export class WalkThroughPageUpAction extends Action { - - public static readonly ID = 'workbench.action.interactivePlayground.pageUp'; - public static readonly LABEL = localize('editorWalkThrough.pageUp', "Scroll Up (Page)"); - - constructor( - id: string, - label: string, - @IEditorService private editorService: IEditorService - ) { - super(id, label); - } - - public run(): TPromise { - const activeControl = this.editorService.activeControl; +export const WalkThroughPageUp: ICommandAndKeybindingRule = { + id: 'workbench.action.interactivePlayground.pageUp', + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + when: ContextKeyExpr.and(WALK_THROUGH_FOCUS, EditorContextKeys.editorTextFocus.toNegated()), + primary: KeyCode.PageUp, + handler: accessor => { + const editorService = accessor.get(IEditorService); + const activeControl = editorService.activeControl; if (activeControl instanceof WalkThroughPart) { activeControl.pageUp(); } - return null; } -} +}; -export class WalkThroughPageDownAction extends Action { - - public static readonly ID = 'workbench.action.interactivePlayground.pageDown'; - public static readonly LABEL = localize('editorWalkThrough.pageDown', "Scroll Down (Page)"); - - constructor( - id: string, - label: string, - @IEditorService private editorService: IEditorService - ) { - super(id, label); - } - - public run(): TPromise { - const activeControl = this.editorService.activeControl; +export const WalkThroughPageDown: ICommandAndKeybindingRule = { + id: 'workbench.action.interactivePlayground.pageDown', + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + when: ContextKeyExpr.and(WALK_THROUGH_FOCUS, EditorContextKeys.editorTextFocus.toNegated()), + primary: KeyCode.PageDown, + handler: accessor => { + const editorService = accessor.get(IEditorService); + const activeControl = editorService.activeControl; if (activeControl instanceof WalkThroughPart) { activeControl.pageDown(); } - return null; } -} \ No newline at end of file +}; \ No newline at end of file diff --git a/src/vs/workbench/services/viewlet/browser/viewletService.ts b/src/vs/workbench/services/viewlet/browser/viewletService.ts index 0a6b990f23f..354410b5820 100644 --- a/src/vs/workbench/services/viewlet/browser/viewletService.ts +++ b/src/vs/workbench/services/viewlet/browser/viewletService.ts @@ -62,7 +62,7 @@ export class ViewletService implements IViewletService { } public setViewletEnablement(id: string, enabled: boolean): void { - const descriptor = this.getBuiltInViewlets().filter(desc => desc.id === id).pop(); + const descriptor = this.getAllViewlets().filter(desc => desc.id === id).pop(); if (descriptor && descriptor.enabled !== enabled) { descriptor.enabled = enabled; this._onDidViewletEnable.fire({ id, enabled }); @@ -74,7 +74,12 @@ export class ViewletService implements IViewletService { return this.sidebarPart.openViewlet(id, focus); } return this.extensionService.whenInstalledExtensionsRegistered() - .then(() => this.sidebarPart.openViewlet(id, focus)); + .then(() => { + if (this.getViewlet(id)) { + return this.sidebarPart.openViewlet(id, focus); + } + return null; + }); } public getActiveViewlet(): IViewlet { @@ -82,11 +87,11 @@ export class ViewletService implements IViewletService { } public getViewlets(): ViewletDescriptor[] { - return this.getBuiltInViewlets() + return this.getAllViewlets() .filter(v => v.enabled); } - private getBuiltInViewlets(): ViewletDescriptor[] { + private getAllViewlets(): ViewletDescriptor[] { return this.viewletRegistry.getViewlets() .sort((v1, v2) => v1.order - v2.order); } diff --git a/test/index.html b/test/index.html index 785f4e3abc0..384c987fed1 100644 --- a/test/index.html +++ b/test/index.html @@ -7,8 +7,6 @@
- diff --git a/yarn.lock b/yarn.lock index 7f0b53d77e7..d3bfde961b3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3280,7 +3280,7 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.10.0" -keytar@^4.2.1: +keytar@4.2.1: version "4.2.1" resolved "https://registry.yarnpkg.com/keytar/-/keytar-4.2.1.tgz#8a06a6577fdf6373e0aa6b112277e63dec77fd12" dependencies: