From 01292b41740349747ed2748cb7aefa7123f6e386 Mon Sep 17 00:00:00 2001 From: Ilya Biryukov Date: Tue, 11 Dec 2018 10:38:36 -0800 Subject: [PATCH 001/274] Add Git log, globalConfig, and tree diff API --- extensions/git/src/api/api1.ts | 40 +++++-- extensions/git/src/api/git.d.ts | 13 +++ extensions/git/src/git.ts | 161 +++++++++++++++++++++++++--- extensions/git/src/repository.ts | 33 ++++-- extensions/git/src/test/git.test.ts | 12 ++- 5 files changed, 226 insertions(+), 33 deletions(-) diff --git a/extensions/git/src/api/api1.ts b/extensions/git/src/api/api1.ts index 9469580d97b..9d8a5901786 100644 --- a/extensions/git/src/api/api1.ts +++ b/extensions/git/src/api/api1.ts @@ -5,7 +5,7 @@ import { Model } from '../model'; import { Repository as BaseRepository, Resource } from '../repository'; -import { InputBox, Git, API, Repository, Remote, RepositoryState, Branch, Ref, Submodule, Commit, Change, RepositoryUIState, Status } from './git'; +import { InputBox, Git, API, Repository, Remote, RepositoryState, Branch, Ref, Submodule, Commit, Change, RepositoryUIState, Status, GitLogOptions } from './git'; import { Event, SourceControlInputBox, Uri, SourceControl } from 'vscode'; import { mapEvent } from '../util'; @@ -76,6 +76,10 @@ export class ApiRepository implements Repository { return this._repository.setConfig(key, value); } + getGlobalConfig(key: string): Promise { + return this._repository.getGlobalConfig(key); + } + getObjectDetails(treeish: string, path: string): Promise<{ mode: string; object: string; size: number; }> { return this._repository.getObjectDetails(treeish, path); } @@ -104,28 +108,38 @@ export class ApiRepository implements Repository { return this._repository.diff(cached); } - diffWithHEAD(path: string): Promise { - return this._repository.diffWithHEAD(path); + diffWithHEAD(): Promise; + diffWithHEAD(path: string): Promise; + diffWithHEAD(path?: string): Promise { + return path ? this._repository.diffWithHEAD(path) : this._repository.diffWithHEAD(); } - diffWith(ref: string, path: string): Promise { - return this._repository.diffWith(ref, path); + diffWith(ref: string): Promise; + diffWith(ref: string, path: string): Promise; + diffWith(ref: string, path?: string): Promise { + return path ? this._repository.diffWith(ref, path) : this._repository.diffWith(ref); } - diffIndexWithHEAD(path: string): Promise { - return this._repository.diffIndexWithHEAD(path); + diffIndexWithHEAD(): Promise; + diffIndexWithHEAD(path: string): Promise; + diffIndexWithHEAD(path?: string): Promise { + return path ? this._repository.diffIndexWithHEAD(path) : this._repository.diffIndexWithHEAD(); } - diffIndexWith(ref: string, path: string): Promise { - return this._repository.diffIndexWith(ref, path); + diffIndexWith(ref: string): Promise; + diffIndexWith(ref: string, path: string): Promise; + diffIndexWith(ref: string, path?: string): Promise { + return path ? this._repository.diffIndexWith(ref, path) : this._repository.diffIndexWith(ref); } diffBlobs(object1: string, object2: string): Promise { return this._repository.diffBlobs(object1, object2); } - diffBetween(ref1: string, ref2: string, path: string): Promise { - return this._repository.diffBetween(ref1, ref2, path); + diffBetween(ref1: string, ref2: string): Promise; + diffBetween(ref1: string, ref2: string, path: string): Promise; + diffBetween(ref1: string, ref2: string, path?: string): Promise { + return path ? this._repository.diffBetween(ref1, ref2, path) : this._repository.diffBetween(ref1, ref2); } hashObject(data: string): Promise { @@ -179,6 +193,10 @@ export class ApiRepository implements Repository { push(remoteName?: string, branchName?: string, setUpstream: boolean = false): Promise { return this._repository.pushTo(remoteName, branchName, setUpstream); } + + getLog(options?: GitLogOptions): Promise { + return this._repository.getLog(options); + } } export class ApiGit implements Git { diff --git a/extensions/git/src/api/git.d.ts b/extensions/git/src/api/git.d.ts index 1b3ed4d2dec..ff6ce04ab99 100644 --- a/extensions/git/src/api/git.d.ts +++ b/extensions/git/src/api/git.d.ts @@ -41,6 +41,7 @@ export interface Commit { readonly hash: string; readonly message: string; readonly parents: string[]; + readonly authorEmail?: string | undefined; } export interface Submodule { @@ -109,6 +110,10 @@ export interface RepositoryUIState { readonly onDidChange: Event; } +export interface GitLogOptions { + readonly maxEntries?: number; +} + export interface Repository { readonly rootUri: Uri; @@ -119,6 +124,7 @@ export interface Repository { getConfigs(): Promise<{ key: string; value: string; }[]>; getConfig(key: string): Promise; setConfig(key: string, value: string): Promise; + getGlobalConfig(key: string): Promise; getObjectDetails(treeish: string, path: string): Promise<{ mode: string, object: string, size: number }>; detectObjectType(object: string): Promise<{ mimetype: string, encoding?: string }>; @@ -130,11 +136,16 @@ export interface Repository { apply(patch: string, reverse?: boolean): Promise; diff(cached?: boolean): Promise; + diffWithHEAD(): Promise; diffWithHEAD(path: string): Promise; + diffWith(ref: string): Promise; diffWith(ref: string, path: string): Promise; + diffIndexWithHEAD(): Promise; diffIndexWithHEAD(path: string): Promise; + diffIndexWith(ref: string): Promise; diffIndexWith(ref: string, path: string): Promise; diffBlobs(object1: string, object2: string): Promise; + diffBetween(ref1: string, ref2: string): Promise; diffBetween(ref1: string, ref2: string, path: string): Promise; hashObject(data: string): Promise; @@ -155,6 +166,8 @@ export interface Repository { fetch(remote?: string, ref?: string): Promise; pull(): Promise; push(remoteName?: string, branchName?: string, setUpstream?: boolean): Promise; + + getLog(options?: GitLogOptions): Promise; } export interface API { diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index 380ee42f411..d104d3cbdf4 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -12,9 +12,9 @@ import { EventEmitter } from 'events'; import iconv = require('iconv-lite'); import * as filetype from 'file-type'; import { assign, groupBy, denodeify, IDisposable, toDisposable, dispose, mkdirp, readBytes, detectUnicodeEncoding, Encoding, onceEvent } from './util'; -import { CancellationToken } from 'vscode'; +import { CancellationToken, Uri } from 'vscode'; import { detectEncoding } from './encoding'; -import { Ref, RefType, Branch, Remote, GitErrorCodes } from './api/git'; +import { Ref, RefType, Branch, Remote, GitErrorCodes, GitLogOptions, Change, Status } from './api/git'; const readfile = denodeify(fs.readFile); @@ -311,6 +311,8 @@ function getGitErrorCode(stderr: string): string | undefined { return void 0; } +const COMMIT_FORMAT = '%H\n%ae\n%P\n%B'; + export class Git { readonly path: string; @@ -450,6 +452,7 @@ export interface Commit { hash: string; message: string; parents: string[]; + authorEmail?: string | undefined; } export class GitStatusParser { @@ -581,13 +584,13 @@ export function parseGitmodules(raw: string): Submodule[] { } export function parseGitCommit(raw: string): Commit | null { - const match = /^([0-9a-f]{40})\n(.*)\n([^]*)$/m.exec(raw.trim()); + const match = /^([0-9a-f]{40})\n(.*)\n(.*)\n([^]*)$/m.exec(raw.trim()); if (!match) { return null; } - const parents = match[2] ? match[2].split(' ') : []; - return { hash: match[1], message: match[3], parents }; + const parents = match[3] ? match[3].split(' ') : []; + return { hash: match[1], message: match[4], parents, authorEmail: match[2] }; } interface LsTreeElement { @@ -697,6 +700,38 @@ export class Repository { }); } + async getLog(options?: GitLogOptions): Promise { + const args = ['log']; + if (options) { + if (typeof options.maxEntries === 'number' && options.maxEntries > 0) { + args.push('-' + options.maxEntries); + } + } + + args.push(`--pretty=format:${COMMIT_FORMAT}%x00%x00`); + const gitResult = await this.run(args); + if (gitResult.exitCode) { + // An empty repo. + return []; + } + + const entries = gitResult.stdout.split('\x00\x00'); + const result: Commit[] = []; + for (let entry of entries) { + if (entry.startsWith('\n')) { + entry = entry.substring(1); + } + const commit = parseGitCommit(entry); + if (!commit) { + break; + } + + result.push(commit); + } + + return result; + } + async bufferString(object: string, encoding: string = 'utf8', autoGuessEncoding = false): Promise { const stdout = await this.buffer(object); @@ -851,25 +886,41 @@ export class Repository { return result.stdout; } - async diffWithHEAD(path: string): Promise { + async diffWithHEAD(path?: string): Promise { + if (!path) { + return await this.diffFiles(false); + } + const args = ['diff', '--', path]; const result = await this.run(args); return result.stdout; } - async diffWith(ref: string, path: string): Promise { + async diffWith(ref: string, path?: string): Promise { + if (!path) { + return await this.diffFiles(false, ref); + } + const args = ['diff', ref, '--', path]; const result = await this.run(args); return result.stdout; } - async diffIndexWithHEAD(path: string): Promise { + async diffIndexWithHEAD(path?: string): Promise { + if (!path) { + return await this.diffFiles(true); + } + const args = ['diff', '--cached', '--', path]; const result = await this.run(args); return result.stdout; } - async diffIndexWith(ref: string, path: string): Promise { + async diffIndexWith(ref: string, path?: string): Promise { + if (!path) { + return await this.diffFiles(true, ref); + } + const args = ['diff', '--cached', ref, '--', path]; const result = await this.run(args); return result.stdout; @@ -881,13 +932,99 @@ export class Repository { return result.stdout; } - async diffBetween(ref1: string, ref2: string, path: string): Promise { - const args = ['diff', `${ref1}...${ref2}`, '--', path]; + async diffBetween(ref1: string, ref2: string, path?: string): Promise { + const range = `${ref1}...${ref2}`; + if (!path) { + return await this.diffFiles(false, range); + } + + const args = ['diff', range, '--', path]; const result = await this.run(args); return result.stdout.trim(); } + private async diffFiles(cached: boolean, ref?: string): Promise { + const args = ['diff', '--name-status', '-z', '--diff-filter=ADMR']; + if (cached) { + args.push('--cached'); + } + + if (ref) { + args.push(ref); + } + + const gitResult = await this.run(args); + if (gitResult.exitCode) { + return []; + } + + const entries = gitResult.stdout.split('\x00'); + let index = 0; + const result: Change[] = []; + + entriesLoop: + while (index < entries.length - 1) { + const change = entries[index++]; + const resourcePath = entries[index++]; + if (!change || !resourcePath) { + break; + } + + const originalUri = Uri.file(path.isAbsolute(resourcePath) ? resourcePath : path.join(this.repositoryRoot, resourcePath)); + let status: Status = Status.UNTRACKED; + + // Copy or Rename status comes with a number, e.g. 'R100'. We don't need the number, so we use only first character of the status. + switch (change[0]) { + case 'M': + status = Status.MODIFIED; + break; + + case 'A': + status = Status.INDEX_ADDED; + break; + + case 'D': + status = Status.DELETED; + break; + + // Rename contains two paths, the second one is what the file is renamed/copied to. + case 'R': + if (index >= entries.length) { + break; + } + + const newPath = entries[index++]; + if (!newPath) { + break; + } + + const uri = Uri.file(path.isAbsolute(newPath) ? newPath : path.join(this.repositoryRoot, newPath)); + result.push({ + uri, + renameUri: uri, + originalUri, + status: Status.INDEX_RENAMED + }); + + continue; + + default: + // Unknown status + break entriesLoop; + } + + result.push({ + status, + originalUri, + uri: originalUri, + renameUri: originalUri, + }); + } + + return result; + } + async getMergeBase(ref1: string, ref2: string): Promise { const args = ['merge-base', ref1, ref2]; const result = await this.run(args); @@ -1529,7 +1666,7 @@ export class Repository { } async getCommit(ref: string): Promise { - const result = await this.run(['show', '-s', '--format=%H\n%P\n%B', ref]); + const result = await this.run(['show', '-s', `--format=${COMMIT_FORMAT}`, ref]); return parseGitCommit(result.stdout) || Promise.reject('bad commit format'); } diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index d188eea026e..e9650b905a7 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -13,7 +13,7 @@ import * as path from 'path'; import * as nls from 'vscode-nls'; import * as fs from 'fs'; import { StatusBarCommands } from './statusbar'; -import { Branch, Ref, Remote, RefType, GitErrorCodes, Status } from './api/git'; +import { Branch, Ref, Remote, RefType, GitErrorCodes, Status, GitLogOptions, Change } from './api/git'; const timeout = (millis: number) => new Promise(c => setTimeout(c, millis)); @@ -295,7 +295,8 @@ export const enum Operation { GetObjectDetails = 'GetObjectDetails', SubmoduleUpdate = 'SubmoduleUpdate', RebaseContinue = 'RebaseContinue', - Apply = 'Apply' + Apply = 'Apply', + Log = 'Log', } function isReadOnly(operation: Operation): boolean { @@ -697,10 +698,18 @@ export class Repository implements Disposable { return this.run(Operation.Config, () => this.repository.config('local', key)); } + getGlobalConfig(key: string): Promise { + return this.run(Operation.Config, () => this.repository.config('global', key)); + } + setConfig(key: string, value: string): Promise { return this.run(Operation.Config, () => this.repository.config('local', key, value)); } + getLog(options?: GitLogOptions): Promise { + return this.run(Operation.Log, () => this.repository.getLog(options)); + } + @throttle async status(): Promise { await this.run(Operation.Status); @@ -710,19 +719,27 @@ export class Repository implements Disposable { return this.run(Operation.Diff, () => this.repository.diff(cached)); } - diffWithHEAD(path: string): Promise { + diffWithHEAD(): Promise; + diffWithHEAD(path: string): Promise; + diffWithHEAD(path?: string | undefined): Promise { return this.run(Operation.Diff, () => this.repository.diffWithHEAD(path)); } - diffWith(ref: string, path: string): Promise { + diffWith(ref: string): Promise; + diffWith(ref: string, path: string): Promise; + diffWith(ref: string, path?: string): Promise { return this.run(Operation.Diff, () => this.repository.diffWith(ref, path)); } - diffIndexWithHEAD(path: string): Promise { + diffIndexWithHEAD(): Promise; + diffIndexWithHEAD(path: string): Promise; + diffIndexWithHEAD(path?: string): Promise { return this.run(Operation.Diff, () => this.repository.diffIndexWithHEAD(path)); } - diffIndexWith(ref: string, path: string): Promise { + diffIndexWith(ref: string): Promise; + diffIndexWith(ref: string, path: string): Promise; + diffIndexWith(ref: string, path?: string): Promise { return this.run(Operation.Diff, () => this.repository.diffIndexWith(ref, path)); } @@ -730,7 +747,9 @@ export class Repository implements Disposable { return this.run(Operation.Diff, () => this.repository.diffBlobs(object1, object2)); } - diffBetween(ref1: string, ref2: string, path: string): Promise { + diffBetween(ref1: string, ref2: string): Promise; + diffBetween(ref1: string, ref2: string, path: string): Promise; + diffBetween(ref1: string, ref2: string, path?: string): Promise { return this.run(Operation.Diff, () => this.repository.diffBetween(ref1, ref2, path)); } diff --git a/extensions/git/src/test/git.test.ts b/extensions/git/src/test/git.test.ts index 6b7af8c8684..e28cf10d192 100644 --- a/extensions/git/src/test/git.test.ts +++ b/extensions/git/src/test/git.test.ts @@ -177,37 +177,43 @@ suite('git', () => { suite('parseGitCommit', () => { test('single parent commit', function () { const GIT_OUTPUT_SINGLE_PARENT = `52c293a05038d865604c2284aa8698bd087915a1 +john.doe@mail.com 8e5a374372b8393906c7e380dbb09349c5385554 This is a commit message.`; assert.deepEqual(parseGitCommit(GIT_OUTPUT_SINGLE_PARENT), { hash: '52c293a05038d865604c2284aa8698bd087915a1', message: 'This is a commit message.', - parents: ['8e5a374372b8393906c7e380dbb09349c5385554'] + parents: ['8e5a374372b8393906c7e380dbb09349c5385554'], + authorEmail: 'john.doe@mail.com', }); }); test('multiple parent commits', function () { const GIT_OUTPUT_MULTIPLE_PARENTS = `52c293a05038d865604c2284aa8698bd087915a1 +john.doe@mail.com 8e5a374372b8393906c7e380dbb09349c5385554 df27d8c75b129ab9b178b386077da2822101b217 This is a commit message.`; assert.deepEqual(parseGitCommit(GIT_OUTPUT_MULTIPLE_PARENTS), { hash: '52c293a05038d865604c2284aa8698bd087915a1', message: 'This is a commit message.', - parents: ['8e5a374372b8393906c7e380dbb09349c5385554', 'df27d8c75b129ab9b178b386077da2822101b217'] + parents: ['8e5a374372b8393906c7e380dbb09349c5385554', 'df27d8c75b129ab9b178b386077da2822101b217'], + authorEmail: 'john.doe@mail.com', }); }); test('no parent commits', function () { const GIT_OUTPUT_NO_PARENTS = `52c293a05038d865604c2284aa8698bd087915a1 +john.doe@mail.com This is a commit message.`; assert.deepEqual(parseGitCommit(GIT_OUTPUT_NO_PARENTS), { hash: '52c293a05038d865604c2284aa8698bd087915a1', message: 'This is a commit message.', - parents: [] + parents: [], + authorEmail: 'john.doe@mail.com', }); }); }); From fb813aaa111d303b93e33c32f5f57728c9a0f32a Mon Sep 17 00:00:00 2001 From: Ilya Biryukov Date: Wed, 12 Dec 2018 10:21:45 -0800 Subject: [PATCH 002/274] Fix updating comment positions When a comment thread changes, it may also has a different position. This fix implements moving the thread if this happens. --- .../comments/electron-browser/commentThreadWidget.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts b/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts index 28ea3a90d69..d8770984ab2 100644 --- a/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts +++ b/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts @@ -265,6 +265,15 @@ export class ReviewZoneWidget extends ZoneWidget { this._commentThread = commentThread; this._commentElements = newCommentNodeList; this.createThreadLabel(); + + // Move comment glyph widget and show position if the line has changed. + const lineNumber = this._commentThread.range.startLineNumber; + if (this._commentGlyph.getPosition().position.lineNumber !== lineNumber) { + this._commentGlyph.setLineNumber(lineNumber); + if (!this._isCollapsed) { + this.show({ lineNumber, column: 1 }, 2); + } + } } updateDraftMode(draftMode: modes.DraftMode) { From cfc11ee38c9febaa1dfdb9a6f7bd6e15799f68dd Mon Sep 17 00:00:00 2001 From: Ilya Biryukov Date: Wed, 12 Dec 2018 16:32:43 -0800 Subject: [PATCH 003/274] Fix new comment position and event leak Use the current comment position when creating a new thread, not the one that was when user started editing the comment, because the position may change, e.g. if the user added new lines before the comment. Keep the editor selection when comments are updated via documment comment provider event so user flow is not disturbed when comments are updated. Add button styler to local disposables to fix event leak. --- .../electron-browser/commentGlyphWidget.ts | 23 +++++++++---------- .../electron-browser/commentThreadWidget.ts | 11 +++++---- .../commentsEditorContribution.ts | 6 ++--- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/vs/workbench/parts/comments/electron-browser/commentGlyphWidget.ts b/src/vs/workbench/parts/comments/electron-browser/commentGlyphWidget.ts index 35ce6e139da..a268f68a24c 100644 --- a/src/vs/workbench/parts/comments/electron-browser/commentGlyphWidget.ts +++ b/src/vs/workbench/parts/comments/electron-browser/commentGlyphWidget.ts @@ -23,9 +23,8 @@ export class CommentGlyphWidget { constructor(editor: ICodeEditor, lineNumber: number) { this._commentsOptions = this.createDecorationOptions(); - this._lineNumber = lineNumber; this._editor = editor; - this.update(); + this.setLineNumber(lineNumber); } private createDecorationOptions(): ModelDecorationOptions { @@ -41,11 +40,12 @@ export class CommentGlyphWidget { return ModelDecorationOptions.createDynamic(decorationOptions); } - update() { + setLineNumber(lineNumber: number): void { + this._lineNumber = lineNumber; let commentsDecorations = [{ range: { - startLineNumber: this._lineNumber, startColumn: 1, - endLineNumber: this._lineNumber, endColumn: 1 + startLineNumber: lineNumber, startColumn: 1, + endLineNumber: lineNumber, endColumn: 1 }, options: this._commentsOptions }]; @@ -53,15 +53,14 @@ export class CommentGlyphWidget { this.commentsDecorations = this._editor.deltaDecorations(this.commentsDecorations, commentsDecorations); } - setLineNumber(lineNumber: number): void { - this._lineNumber = lineNumber; - this.update(); - } - getPosition(): IContentWidgetPosition { + const range = this._editor.hasModel() && this.commentsDecorations && this.commentsDecorations.length + ? this._editor.getModel().getDecorationRange(this.commentsDecorations[0]) + : null; + return { position: { - lineNumber: this._lineNumber, + lineNumber: range ? range.startLineNumber : this._lineNumber, column: 1 }, preference: [ContentWidgetPositionPreference.EXACT] @@ -73,4 +72,4 @@ export class CommentGlyphWidget { this._editor.deltaDecorations(this.commentsDecorations, []); } } -} \ No newline at end of file +} diff --git a/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts b/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts index d8770984ab2..b2bbaea9d5a 100644 --- a/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts +++ b/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts @@ -98,7 +98,7 @@ export class ReviewZoneWidget extends ZoneWidget { commentThread: modes.CommentThread, pendingComment: string, draftMode: modes.DraftMode, - options: IOptions = {} + options: IOptions = { keepEditorSelection: true } ) { super(editor, options); this._resizeObserver = null; @@ -270,9 +270,10 @@ export class ReviewZoneWidget extends ZoneWidget { const lineNumber = this._commentThread.range.startLineNumber; if (this._commentGlyph.getPosition().position.lineNumber !== lineNumber) { this._commentGlyph.setLineNumber(lineNumber); - if (!this._isCollapsed) { - this.show({ lineNumber, column: 1 }, 2); - } + } + + if (!this._isCollapsed) { + this.show({ lineNumber, column: 1 }, 2); } } @@ -383,7 +384,7 @@ export class ReviewZoneWidget extends ZoneWidget { private createCommentWidgetActions(container: HTMLElement, model: ITextModel) { const button = new Button(container); - attachButtonStyler(button, this.themeService); + this._localToDispose.push(attachButtonStyler(button, this.themeService)); button.label = 'Add comment'; button.enabled = model.getValueLength() > 0; diff --git a/src/vs/workbench/parts/comments/electron-browser/commentsEditorContribution.ts b/src/vs/workbench/parts/comments/electron-browser/commentsEditorContribution.ts index f81a733ae41..3c7af990670 100644 --- a/src/vs/workbench/parts/comments/electron-browser/commentsEditorContribution.ts +++ b/src/vs/workbench/parts/comments/electron-browser/commentsEditorContribution.ts @@ -389,7 +389,7 @@ export class ReviewController implements IEditorContribution { } }); added.forEach(thread => { - let zoneWidget = new ReviewZoneWidget(this.instantiationService, this.modeService, this.modelService, this.themeService, this.commentService, this.openerService, this.dialogService, this.notificationService, this.editor, e.owner, thread, null, draftMode, {}); + let zoneWidget = new ReviewZoneWidget(this.instantiationService, this.modeService, this.modelService, this.themeService, this.commentService, this.openerService, this.dialogService, this.notificationService, this.editor, e.owner, thread, null, draftMode); zoneWidget.display(thread.range.startLineNumber); this._commentWidgets.push(zoneWidget); this._commentInfos.filter(info => info.owner === e.owner)[0].threads.push(thread); @@ -420,7 +420,7 @@ export class ReviewController implements IEditorContribution { }, reply: replyCommand, collapsibleState: CommentThreadCollapsibleState.Expanded, - }, pendingComment, draftMode, {}); + }, pendingComment, draftMode); this.localToDispose.push(this._newCommentWidget.onDidClose(e => { this.clearNewCommentWidget(); @@ -565,7 +565,7 @@ export class ReviewController implements IEditorContribution { thread.collapsibleState = modes.CommentThreadCollapsibleState.Expanded; } - let zoneWidget = new ReviewZoneWidget(this.instantiationService, this.modeService, this.modelService, this.themeService, this.commentService, this.openerService, this.dialogService, this.notificationService, this.editor, info.owner, thread, pendingComment, info.draftMode, {}); + let zoneWidget = new ReviewZoneWidget(this.instantiationService, this.modeService, this.modelService, this.themeService, this.commentService, this.openerService, this.dialogService, this.notificationService, this.editor, info.owner, thread, pendingComment, info.draftMode); zoneWidget.display(thread.range.startLineNumber); this._commentWidgets.push(zoneWidget); }); From 99ce253309b4ba6a21df73a2829eea65960ed061 Mon Sep 17 00:00:00 2001 From: Ilya Biryukov Date: Thu, 13 Dec 2018 17:45:23 -0800 Subject: [PATCH 004/274] Fix comment thread update handler When comment thread update comes, the code would go through deleted and remaining comments. The bug was that if there was a deleted comment, the code would delete all the comments in the thread past the deleted comment as well, and then would erroneousely consider remaining comments as new ones, basically duplicating them in UI. Fix for #64042 --- .../parts/comments/electron-browser/commentThreadWidget.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts b/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts index b2bbaea9d5a..6ed54c6fd80 100644 --- a/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts +++ b/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts @@ -236,7 +236,7 @@ export class ReviewZoneWidget extends ZoneWidget { // del removed elements for (let i = commentElementsToDel.length - 1; i >= 0; i--) { - this._commentElements.splice(commentElementsToDelIndex[i]); + this._commentElements.splice(commentElementsToDelIndex[i], 1); this._commentsElement.removeChild(commentElementsToDel[i].domNode); } From 45e479386b60845a0d83d0ec0c03cafc970a7616 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 14 Dec 2018 09:20:49 +0100 Subject: [PATCH 005/274] add typings to support Promise#finally --- src/typings/lib.es2018.promise.d.ts | 27 +++++++++++++++++++ src/vs/base/common/async.ts | 3 +++ .../api/node/extHostSearch.fileIndex.ts | 3 +++ .../services/extensions/node/lazyPromise.ts | 4 +++ .../services/search/node/rawSearchService.ts | 3 +++ 5 files changed, 40 insertions(+) create mode 100644 src/typings/lib.es2018.promise.d.ts diff --git a/src/typings/lib.es2018.promise.d.ts b/src/typings/lib.es2018.promise.d.ts new file mode 100644 index 00000000000..9f7b2d38cb2 --- /dev/null +++ b/src/typings/lib.es2018.promise.d.ts @@ -0,0 +1,27 @@ +/*! ***************************************************************************** +Copyright (c) Microsoft Corporation. All rights reserved. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the +License at http://www.apache.org/licenses/LICENSE-2.0 + +THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED +WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, +MERCHANTABLITY OR NON-INFRINGEMENT. + +See the Apache Version 2.0 License for specific language governing permissions +and limitations under the License. +***************************************************************************** */ + +/** + * Represents the completion of an asynchronous operation + */ +interface Promise { + /** + * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The + * resolved value cannot be modified from the callback. + * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected). + * @returns A Promise for the completion of the callback. + */ + finally(onfinally?: (() => void) | undefined | null): Promise; +} diff --git a/src/vs/base/common/async.ts b/src/vs/base/common/async.ts index ace08b77709..27971239294 100644 --- a/src/vs/base/common/async.ts +++ b/src/vs/base/common/async.ts @@ -44,6 +44,9 @@ export function createCancelablePromise(callback: (token: CancellationToken) catch(reject?: ((reason: any) => TResult | Promise) | undefined | null): Promise { return this.then(undefined, reject); } + finally(onfinally?: (() => void) | undefined | null): Promise { + return this.finally(onfinally); + } }; } diff --git a/src/vs/workbench/api/node/extHostSearch.fileIndex.ts b/src/vs/workbench/api/node/extHostSearch.fileIndex.ts index cda0bf53c19..ea67c609251 100644 --- a/src/vs/workbench/api/node/extHostSearch.fileIndex.ts +++ b/src/vs/workbench/api/node/extHostSearch.fileIndex.ts @@ -576,6 +576,9 @@ export class FileIndexSearchManager { catch(reject?) { return this.then(undefined, reject); } + finally(onFinally) { + return promise.finally(onFinally); + } }; } } diff --git a/src/vs/workbench/services/extensions/node/lazyPromise.ts b/src/vs/workbench/services/extensions/node/lazyPromise.ts index df9e72be45c..412a2a2370e 100644 --- a/src/vs/workbench/services/extensions/node/lazyPromise.ts +++ b/src/vs/workbench/services/extensions/node/lazyPromise.ts @@ -82,4 +82,8 @@ export class LazyPromise implements Promise { public catch(error: any): any { return this._ensureActual().then(undefined, error); } + + public finally(callback): any { + return this._ensureActual().finally(callback); + } } diff --git a/src/vs/workbench/services/search/node/rawSearchService.ts b/src/vs/workbench/services/search/node/rawSearchService.ts index d8c2a7eacce..14fe258bc37 100644 --- a/src/vs/workbench/services/search/node/rawSearchService.ts +++ b/src/vs/workbench/services/search/node/rawSearchService.ts @@ -394,6 +394,9 @@ export class SearchService implements IRawSearchService { catch(reject?) { return this.then(undefined, reject); } + finally(onFinally) { + return promise.finally(onFinally); + } }; } } From c14ac109a2fc8e6bd51deaac246ed2018ee8e0ce Mon Sep 17 00:00:00 2001 From: Rudi Chen Date: Thu, 13 Dec 2018 22:35:06 -0800 Subject: [PATCH 006/274] strickNullChecks breadcrumbs.ts --- src/tsconfig.strictNullChecks.json | 1 + src/vs/workbench/browser/parts/editor/breadcrumbs.ts | 12 ++++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index 17b2175f78e..2fd13caa0ad 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -526,6 +526,7 @@ "./vs/workbench/browser/composite.ts", "./vs/workbench/browser/panel.ts", "./vs/workbench/browser/part.ts", + "./vs/workbench/browser/parts/editor/breadcrumbs.ts", "./vs/workbench/browser/parts/editor/editorWidgets.ts", "./vs/workbench/browser/parts/editor/rangeDecorations.ts", "./vs/workbench/browser/parts/notifications/notificationsAlerts.ts", diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbs.ts b/src/vs/workbench/browser/parts/editor/breadcrumbs.ts index 841ae7146fd..04570fc3df4 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbs.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbs.ts @@ -90,10 +90,18 @@ export abstract class BreadcrumbsConfig { readonly name = name; readonly onDidChange = onDidChange.event; getValue(overrides?: IConfigurationOverrides): T { - return service.getValue(name, overrides); + if (overrides) { + return service.getValue(name, overrides); + } else { + return service.getValue(name); + } } updateValue(newValue: T, overrides?: IConfigurationOverrides): Promise { - return service.updateValue(name, newValue, overrides); + if (overrides) { + return service.updateValue(name, newValue, overrides); + } else { + return service.updateValue(name, newValue); + } } dispose(): void { listener.dispose(); From 9bbe60f59b8bd3cc158a1ad293439b3bf4b97d20 Mon Sep 17 00:00:00 2001 From: Rudi Chen Date: Thu, 13 Dec 2018 23:48:34 -0800 Subject: [PATCH 007/274] strictNullCheck outlineModel.ts except class assignment error --- src/tsconfig.strictNullChecks.json | 1 + .../contrib/documentSymbols/outlineModel.ts | 59 +++++++++---------- 2 files changed, 29 insertions(+), 31 deletions(-) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index 2fd13caa0ad..27fa9d15541 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -196,6 +196,7 @@ "./vs/editor/contrib/comment/test/blockCommentCommand.test.ts", "./vs/editor/contrib/contextmenu/contextmenu.ts", "./vs/editor/contrib/cursorUndo/cursorUndo.ts", + "./vs/editor/contrib/documentSymbols/outlineModel.ts", "./vs/editor/contrib/dnd/dnd.ts", "./vs/editor/contrib/dnd/dragAndDropCommand.ts", "./vs/editor/contrib/find/findController.ts", diff --git a/src/vs/editor/contrib/documentSymbols/outlineModel.ts b/src/vs/editor/contrib/documentSymbols/outlineModel.ts index c3c2473bf5a..e5ee483aee8 100644 --- a/src/vs/editor/contrib/documentSymbols/outlineModel.ts +++ b/src/vs/editor/contrib/documentSymbols/outlineModel.ts @@ -49,7 +49,7 @@ export abstract class TreeElement { return id; } - static getElementById(id: string, element: TreeElement): TreeElement { + static getElementById(id: string, element: TreeElement): TreeElement | undefined { if (!id) { return undefined; } @@ -88,8 +88,8 @@ export abstract class TreeElement { export class OutlineElement extends TreeElement { children: { [id: string]: OutlineElement; } = Object.create(null); - score: FuzzyScore = FuzzyScore.Default; - marker: { count: number, topSev: MarkerSeverity }; + score: FuzzyScore | undefined = FuzzyScore.Default; + marker: { count: number, topSev: MarkerSeverity } | undefined; constructor( readonly id: string, @@ -125,20 +125,20 @@ export class OutlineGroup extends TreeElement { return res; } - updateMatches(pattern: string, topMatch: OutlineElement): OutlineElement { + updateMatches(pattern: string, topMatch: OutlineElement | undefined): OutlineElement | undefined { for (const key in this.children) { topMatch = this._updateMatches(pattern, this.children[key], topMatch); } return topMatch; } - private _updateMatches(pattern: string, item: OutlineElement, topMatch: OutlineElement): OutlineElement { + private _updateMatches(pattern: string, item: OutlineElement, topMatch: OutlineElement | undefined): OutlineElement | undefined { item.score = pattern ? fuzzyScore(pattern, pattern.toLowerCase(), 0, item.symbol.name, item.symbol.name.toLowerCase(), 0, true) : FuzzyScore.Default; - if (item.score && (!topMatch || item.score[0] > topMatch.score[0])) { + if (item.score && (!topMatch || !topMatch.score || item.score[0] > topMatch.score[0])) { topMatch = item; } for (const key in item.children) { @@ -152,11 +152,11 @@ export class OutlineGroup extends TreeElement { return topMatch; } - getItemEnclosingPosition(position: IPosition): OutlineElement { + getItemEnclosingPosition(position: IPosition): OutlineElement | undefined { return position ? this._getItemEnclosingPosition(position, this.children) : undefined; } - private _getItemEnclosingPosition(position: IPosition, children: { [id: string]: OutlineElement }): OutlineElement { + private _getItemEnclosingPosition(position: IPosition, children: { [id: string]: OutlineElement }): OutlineElement | undefined { for (let key in children) { let item = children[key]; if (!item.symbol.range || !Range.containsPosition(item.symbol.range, position)) { @@ -174,6 +174,7 @@ export class OutlineGroup extends TreeElement { } private _updateMarker(markers: IMarker[], item: OutlineElement): void { + let filteredMarkers: Array = markers; item.marker = undefined; @@ -190,14 +191,14 @@ export class OutlineGroup extends TreeElement { } let myMarkers: IMarker[] = []; - let myTopSev: MarkerSeverity; + let myTopSev: MarkerSeverity | undefined = undefined; for (; start < markers.length && Range.areIntersecting(item.symbol.range, markers[start]); start++) { // remove markers intersecting with this outline element // and store them in a 'private' array. let marker = markers[start]; myMarkers.push(marker); - markers[start] = undefined; + filteredMarkers[start] = undefined; if (!myTopSev || marker.severity > myTopSev) { myTopSev = marker.severity; } @@ -218,13 +219,13 @@ export class OutlineGroup extends TreeElement { }; } - coalesceInPlace(markers); + coalesceInPlace(filteredMarkers); } } export class OutlineModel extends TreeElement { - private static readonly _requests = new LRUCache, model: OutlineModel }>(9, .75); + private static readonly _requests = new LRUCache, model: OutlineModel | undefined }>(9, .75); private static readonly _keys = new class { private _counter = 1; @@ -265,25 +266,25 @@ export class OutlineModel extends TreeElement { OutlineModel._requests.set(key, data); } - if (data.model) { + if (data!.model) { // resolved -> return data return Promise.resolve(data.model); } // increase usage counter - data.promiseCnt += 1; + data!.promiseCnt += 1; token.onCancellationRequested(() => { // last -> cancel provider request, remove cached promise - if (--data.promiseCnt === 0) { - data.source.cancel(); + if (--data!.promiseCnt === 0) { + data!.source.cancel(); OutlineModel._requests.delete(key); } }); return new Promise((resolve, reject) => { - data.promise.then(model => { - data.model = model; + data!.promise.then(model => { + data!.model = model; resolve(model); }, err => { OutlineModel._requests.delete(key); @@ -331,7 +332,7 @@ export class OutlineModel extends TreeElement { container.children[res.id] = res; } - static get(element: TreeElement): OutlineModel { + static get(element: TreeElement): OutlineModel | undefined { while (element) { if (element instanceof OutlineModel) { return element; @@ -358,26 +359,22 @@ export class OutlineModel extends TreeElement { } private _compact(): this { - let count = 0; for (const key in this._groups) { let group = this._groups[key]; if (first(group.children) === undefined) { // empty delete this._groups[key]; - } else { - count += 1; } } - if (count !== 1) { - // - this.children = this._groups; - } else { + let group = first(this._groups); + if (group) { // adopt all elements of the first group - let group = first(this._groups); for (let key in group.children) { let child = group.children[key]; child.parent = this; this.children[child.id] = child; } + } else { + this.children = this._groups; } return this; } @@ -396,11 +393,11 @@ export class OutlineModel extends TreeElement { private _matches: [string, OutlineElement]; - updateMatches(pattern: string): OutlineElement { + updateMatches(pattern: string): OutlineElement | undefined { if (this._matches && this._matches[0] === pattern) { return this._matches[1]; } - let topMatch: OutlineElement; + let topMatch: OutlineElement | undefined; for (const key in this._groups) { topMatch = this._groups[key].updateMatches(pattern, topMatch); } @@ -408,9 +405,9 @@ export class OutlineModel extends TreeElement { return topMatch; } - getItemEnclosingPosition(position: IPosition, context?: OutlineElement): OutlineElement { + getItemEnclosingPosition(position: IPosition, context?: OutlineElement): OutlineElement | undefined { - let preferredGroup: OutlineGroup; + let preferredGroup: OutlineGroup | undefined; if (context) { let candidate = context.parent; while (candidate && !preferredGroup) { From 38d4a547d7f219e83d5e8a922f5ba85f283d7a86 Mon Sep 17 00:00:00 2001 From: Rudi Chen Date: Thu, 13 Dec 2018 23:58:54 -0800 Subject: [PATCH 008/274] strictNullCheck outlineModel.ts inheritance errors --- .../contrib/documentSymbols/outlineModel.ts | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/vs/editor/contrib/documentSymbols/outlineModel.ts b/src/vs/editor/contrib/documentSymbols/outlineModel.ts index e5ee483aee8..ff7caa8d0ee 100644 --- a/src/vs/editor/contrib/documentSymbols/outlineModel.ts +++ b/src/vs/editor/contrib/documentSymbols/outlineModel.ts @@ -20,12 +20,14 @@ export abstract class TreeElement { abstract id: string; abstract children: { [id: string]: TreeElement }; - abstract parent: TreeElement; + abstract parent: TreeElement | undefined; abstract adopt(newParent: TreeElement): TreeElement; remove(): void { - delete this.parent.children[this.id]; + if (this.parent) { + delete this.parent.children[this.id]; + } } static findId(candidate: DocumentSymbol | string, container: TreeElement): string { @@ -93,13 +95,13 @@ export class OutlineElement extends TreeElement { constructor( readonly id: string, - public parent: OutlineModel | OutlineGroup | OutlineElement, + public parent: TreeElement | undefined, readonly symbol: DocumentSymbol ) { super(); } - adopt(parent: OutlineModel | OutlineGroup | OutlineElement): OutlineElement { + adopt(parent: TreeElement): OutlineElement { let res = new OutlineElement(this.id, parent, this.symbol); forEach(this.children, entry => res.children[entry.key] = entry.value.adopt(res)); return res; @@ -112,14 +114,14 @@ export class OutlineGroup extends TreeElement { constructor( readonly id: string, - public parent: OutlineModel, + public parent: TreeElement | undefined, readonly provider: DocumentSymbolProvider, readonly providerIndex: number, ) { super(); } - adopt(parent: OutlineModel): OutlineGroup { + adopt(parent: TreeElement): OutlineGroup { let res = new OutlineGroup(this.id, parent, this.provider, this.providerIndex); forEach(this.children, entry => res.children[entry.key] = entry.value.adopt(res)); return res; @@ -332,7 +334,7 @@ export class OutlineModel extends TreeElement { container.children[res.id] = res; } - static get(element: TreeElement): OutlineModel | undefined { + static get(element: TreeElement | undefined): OutlineModel | undefined { while (element) { if (element instanceof OutlineModel) { return element; @@ -391,7 +393,7 @@ export class OutlineModel extends TreeElement { return true; } - private _matches: [string, OutlineElement]; + private _matches: [string, OutlineElement | undefined]; updateMatches(pattern: string): OutlineElement | undefined { if (this._matches && this._matches[0] === pattern) { @@ -429,7 +431,7 @@ export class OutlineModel extends TreeElement { return result; } - getItemById(id: string): TreeElement { + getItemById(id: string): TreeElement | undefined { return TreeElement.getElementById(id, this); } From e48c4a2f248935d051cb3b825e7067677e25a5ac Mon Sep 17 00:00:00 2001 From: Rudi Chen Date: Fri, 14 Dec 2018 00:14:18 -0800 Subject: [PATCH 009/274] strictNullChecks breadcrumbsModel --- src/tsconfig.strictNullChecks.json | 1 + .../browser/parts/editor/breadcrumbsModel.ts | 36 +++++++++++-------- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index 27fa9d15541..550a75928be 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -528,6 +528,7 @@ "./vs/workbench/browser/panel.ts", "./vs/workbench/browser/part.ts", "./vs/workbench/browser/parts/editor/breadcrumbs.ts", + "./vs/workbench/browser/parts/editor/breadcrumbsModel.ts", "./vs/workbench/browser/parts/editor/editorWidgets.ts", "./vs/workbench/browser/parts/editor/rangeDecorations.ts", "./vs/workbench/browser/parts/notifications/notificationsAlerts.ts", diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts index 1e1de8c7dca..7b6fdca6045 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts @@ -31,7 +31,7 @@ export class FileElement { export type BreadcrumbElement = FileElement | OutlineModel | OutlineGroup | OutlineElement; -type FileInfo = { path: FileElement[], folder: IWorkspaceFolder }; +type FileInfo = { path: FileElement[], folder?: IWorkspaceFolder }; export class EditorBreadcrumbsModel { @@ -105,16 +105,17 @@ export class EditorBreadcrumbsModel { } let info: FileInfo = { - folder: workspaceService.getWorkspaceFolder(uri), + folder: workspaceService.getWorkspaceFolder(uri) || undefined, path: [] }; - while (uri.path !== '/') { - if (info.folder && isEqual(info.folder.uri, uri)) { + let uriPrefix: URI | null = uri; + while (uriPrefix && uriPrefix.path !== '/') { + if (info.folder && isEqual(info.folder.uri, uriPrefix)) { break; } - info.path.unshift(new FileElement(uri, info.path.length === 0 ? FileKind.FILE : FileKind.FOLDER)); - uri = dirname(uri); + info.path.unshift(new FileElement(uriPrefix, info.path.length === 0 ? FileKind.FILE : FileKind.FOLDER)); + uriPrefix = dirname(uriPrefix); } if (info.folder && workspaceService.getWorkbenchState() === WorkbenchState.WORKSPACE) { @@ -145,7 +146,12 @@ export class EditorBreadcrumbsModel { this._updateOutlineElements([]); } - const buffer = this._editor.getModel(); + const editor = this._editor; + if (!editor) { + return; + } + + const buffer = editor.getModel(); if (!buffer || !DocumentSymbolProviderRegistry.has(buffer) || !isEqual(buffer.uri, this._uri)) { return; } @@ -171,11 +177,11 @@ export class EditorBreadcrumbsModel { // copy the model model = model.adopt(); - this._updateOutlineElements(this._getOutlineElements(model, this._editor.getPosition())); - this._outlineDisposables.push(this._editor.onDidChangeCursorPosition(_ => { + this._updateOutlineElements(this._getOutlineElements(model, editor.getPosition())); + this._outlineDisposables.push(editor.onDidChangeCursorPosition(_ => { timeout.cancelAndSet(() => { - if (!buffer.isDisposed() && versionIdThen === buffer.getVersionId() && this._editor.getModel()) { - this._updateOutlineElements(this._getOutlineElements(model, this._editor.getPosition())); + if (!buffer.isDisposed() && versionIdThen === buffer.getVersionId() && editor.getModel()) { + this._updateOutlineElements(this._getOutlineElements(model, editor.getPosition())); } }, 150); })); @@ -186,11 +192,11 @@ export class EditorBreadcrumbsModel { }); } - private _getOutlineElements(model: OutlineModel, position: IPosition): Array { - if (!model) { + private _getOutlineElements(model: OutlineModel, position: IPosition | null): Array { + if (!model || !position) { return []; } - let item: OutlineGroup | OutlineElement = model.getItemEnclosingPosition(position); + let item: OutlineGroup | OutlineElement | undefined = model.getItemEnclosingPosition(position); if (!item) { return [model]; } @@ -201,7 +207,7 @@ export class EditorBreadcrumbsModel { if (parent instanceof OutlineModel) { break; } - if (parent instanceof OutlineGroup && size(parent.parent.children) === 1) { + if (parent instanceof OutlineGroup && parent.parent && size(parent.parent.children) === 1) { break; } item = parent; From e69d6ce6d4c20b3d736889d36997210ed70d5517 Mon Sep 17 00:00:00 2001 From: Rudi Chen Date: Mon, 17 Dec 2018 17:19:17 -0800 Subject: [PATCH 010/274] Review: reintroduce count --- src/vs/editor/contrib/documentSymbols/outlineModel.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/documentSymbols/outlineModel.ts b/src/vs/editor/contrib/documentSymbols/outlineModel.ts index ff7caa8d0ee..cb26e950de1 100644 --- a/src/vs/editor/contrib/documentSymbols/outlineModel.ts +++ b/src/vs/editor/contrib/documentSymbols/outlineModel.ts @@ -361,14 +361,17 @@ export class OutlineModel extends TreeElement { } private _compact(): this { + let count = 0; for (const key in this._groups) { let group = this._groups[key]; if (first(group.children) === undefined) { // empty delete this._groups[key]; + } else { + count += 1; } } let group = first(this._groups); - if (group) { + if (group && count === 1) { // adopt all elements of the first group for (let key in group.children) { let child = group.children[key]; From 3b11ac46d6930320bbffca9a08677fa0d5422eb8 Mon Sep 17 00:00:00 2001 From: Rudi Chen Date: Tue, 18 Dec 2018 12:57:33 -0800 Subject: [PATCH 011/274] Review round 2 --- .../contrib/documentSymbols/outlineModel.ts | 15 ++++++++------- .../browser/parts/editor/breadcrumbsModel.ts | 5 +---- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/vs/editor/contrib/documentSymbols/outlineModel.ts b/src/vs/editor/contrib/documentSymbols/outlineModel.ts index cb26e950de1..ce8d22bf1ad 100644 --- a/src/vs/editor/contrib/documentSymbols/outlineModel.ts +++ b/src/vs/editor/contrib/documentSymbols/outlineModel.ts @@ -193,7 +193,7 @@ export class OutlineGroup extends TreeElement { } let myMarkers: IMarker[] = []; - let myTopSev: MarkerSeverity | undefined = undefined; + let myTopSev: MarkerSeverity | undefined; for (; start < markers.length && Range.areIntersecting(item.symbol.range, markers[start]); start++) { // remove markers intersecting with this outline element @@ -370,16 +370,17 @@ export class OutlineModel extends TreeElement { count += 1; } } - let group = first(this._groups); - if (group && count === 1) { + if (count !== 1) { + // + this.children = this._groups; + } else { + let group = first(this._groups); // adopt all elements of the first group - for (let key in group.children) { - let child = group.children[key]; + for (let key in group!.children) { + let child = group!.children[key]; child.parent = this; this.children[child.id] = child; } - } else { - this.children = this._groups; } return this; } diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts index 7b6fdca6045..4a4a612fe22 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts @@ -146,10 +146,7 @@ export class EditorBreadcrumbsModel { this._updateOutlineElements([]); } - const editor = this._editor; - if (!editor) { - return; - } + const editor = this._editor!; const buffer = editor.getModel(); if (!buffer || !DocumentSymbolProviderRegistry.has(buffer) || !isEqual(buffer.uri, this._uri)) { From 760081d4a691250dc26733c66bb833b6c4a2beee Mon Sep 17 00:00:00 2001 From: Rudi Chen Date: Thu, 20 Dec 2018 15:30:55 -0500 Subject: [PATCH 012/274] Put comment back in place --- src/vs/editor/contrib/documentSymbols/outlineModel.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/documentSymbols/outlineModel.ts b/src/vs/editor/contrib/documentSymbols/outlineModel.ts index ce8d22bf1ad..673e8bc614b 100644 --- a/src/vs/editor/contrib/documentSymbols/outlineModel.ts +++ b/src/vs/editor/contrib/documentSymbols/outlineModel.ts @@ -374,8 +374,8 @@ export class OutlineModel extends TreeElement { // this.children = this._groups; } else { - let group = first(this._groups); // adopt all elements of the first group + let group = first(this._groups); for (let key in group!.children) { let child = group!.children[key]; child.parent = this; From 4c02c554ce5880822c4f5bcdc999466201d50850 Mon Sep 17 00:00:00 2001 From: Krish De Souza Date: Sat, 22 Dec 2018 09:39:52 +0530 Subject: [PATCH 013/274] Removed snapUpdate.sh and replaced with inline command +Simplifies the build process. +Avoids creating a new shell that simply spawns another shell environment. +Simplifies the update mechanism by removing the need to track the existence of snapUpdate.sh. +Keeps the logic for snapUpdate.sh close to updateService.snap.ts --- build/gulpfile.vscode.linux.js | 5 +---- resources/linux/snap/snapUpdate.sh | 3 --- .../update/electron-main/updateService.linux.ts | 6 ++++-- .../update/electron-main/updateService.snap.ts | 17 +++-------------- 4 files changed, 8 insertions(+), 23 deletions(-) delete mode 100755 resources/linux/snap/snapUpdate.sh diff --git a/build/gulpfile.vscode.linux.js b/build/gulpfile.vscode.linux.js index 33972ba398b..2f823cdf705 100644 --- a/build/gulpfile.vscode.linux.js +++ b/build/gulpfile.vscode.linux.js @@ -203,13 +203,10 @@ function prepareSnapPackage(arch) { .pipe(replace('@@VERSION@@', commit.substr(0, 8))) .pipe(rename('snap/snapcraft.yaml')); - const snapUpdate = gulp.src('resources/linux/snap/snapUpdate.sh', { base: '.' }) - .pipe(rename(`usr/share/${product.applicationName}/snapUpdate.sh`)); - const electronLaunch = gulp.src('resources/linux/snap/electron-launch', { base: '.' }) .pipe(rename('electron-launch')); - const all = es.merge(desktop, icon, code, snapcraft, electronLaunch, snapUpdate); + const all = es.merge(desktop, icon, code, snapcraft, electronLaunch); return all.pipe(vfs.dest(destination)); }; diff --git a/resources/linux/snap/snapUpdate.sh b/resources/linux/snap/snapUpdate.sh deleted file mode 100755 index 77569bfc16a..00000000000 --- a/resources/linux/snap/snapUpdate.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -sleep 2 -$SNAP_NAME \ No newline at end of file diff --git a/src/vs/platform/update/electron-main/updateService.linux.ts b/src/vs/platform/update/electron-main/updateService.linux.ts index 7f4261df7ea..0d73ce81733 100644 --- a/src/vs/platform/update/electron-main/updateService.linux.ts +++ b/src/vs/platform/update/electron-main/updateService.linux.ts @@ -125,9 +125,11 @@ export class LinuxUpdateService extends AbstractUpdateService { } // Allow 3 seconds for VS Code to close - spawn('bash', ['-c', path.join(snap, `usr/share/${product.applicationName}/snapUpdate.sh`)], { + spawn('sleep 3 && $SNAP_NAME', { + shell: true, detached: true, - stdio: ['ignore', 'ignore', 'ignore'] + stdio: 'ignore', }); + } } diff --git a/src/vs/platform/update/electron-main/updateService.snap.ts b/src/vs/platform/update/electron-main/updateService.snap.ts index c11676fed4b..fa574427195 100644 --- a/src/vs/platform/update/electron-main/updateService.snap.ts +++ b/src/vs/platform/update/electron-main/updateService.snap.ts @@ -6,7 +6,6 @@ import { Event, Emitter } from 'vs/base/common/event'; import { timeout } from 'vs/base/common/async'; import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain'; -import product from 'vs/platform/node/product'; import { IUpdateService, State, StateType, AvailableForDownload, UpdateType } from 'vs/platform/update/common/update'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ILogService } from 'vs/platform/log/common/log'; @@ -14,7 +13,6 @@ import * as path from 'path'; import { realpath, watch } from 'fs'; import { spawn } from 'child_process'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { stat } from 'vs/base/node/pfs'; abstract class AbstractUpdateService2 implements IUpdateService { @@ -137,8 +135,6 @@ export class SnapUpdateService extends AbstractUpdateService2 { _serviceBrand: any; - private snapUpdatePath: string; - constructor( private snap: string, private snapRevision: string, @@ -149,8 +145,6 @@ export class SnapUpdateService extends AbstractUpdateService2 { ) { super(lifecycleService, environmentService, logService); - this.snapUpdatePath = path.join(this.snap, `usr/share/${product.applicationName}/snapUpdate.sh`); - const watcher = watch(path.dirname(this.snap)); const onChange = Event.fromNodeEventEmitter(watcher, 'change', (_, fileName: string) => fileName); const onCurrentChange = Event.filter(onChange, n => n === 'current'); @@ -196,19 +190,14 @@ export class SnapUpdateService extends AbstractUpdateService2 { this.logService.trace('update#quitAndInstall(): running raw#quitAndInstall()'); // Allow 3 seconds for VS Code to close - spawn('bash', ['-c', this.snapUpdatePath], { + spawn('sleep 3 && $SNAP_NAME', { + shell: true, detached: true, - stdio: ['ignore', 'ignore', 'ignore'] + stdio: 'ignore', }); } private async isUpdateAvailable(): Promise { - try { - await stat(this.snapUpdatePath); - } catch (err) { - return false; - } - const resolvedCurrentSnapPath = await new Promise((c, e) => realpath(`${path.dirname(this.snap)}/current`, (err, r) => err ? e(err) : c(r))); const currentRevision = path.basename(resolvedCurrentSnapPath); return this.snapRevision !== currentRevision; From 04c3dde6e49613d716dd16ef253c6351d8b5077d Mon Sep 17 00:00:00 2001 From: Ilya Biryukov Date: Mon, 7 Jan 2019 11:31:28 -0800 Subject: [PATCH 014/274] Address PR feedback on Git provide APIs --- extensions/git/src/api/api1.ts | 16 ++++++------ extensions/git/src/api/git.d.ts | 8 ++++-- extensions/git/src/git.ts | 44 ++++++++++++++++++++++---------- extensions/git/src/repository.ts | 11 +++++--- 4 files changed, 53 insertions(+), 26 deletions(-) diff --git a/extensions/git/src/api/api1.ts b/extensions/git/src/api/api1.ts index 9d8a5901786..769a646baeb 100644 --- a/extensions/git/src/api/api1.ts +++ b/extensions/git/src/api/api1.ts @@ -5,7 +5,7 @@ import { Model } from '../model'; import { Repository as BaseRepository, Resource } from '../repository'; -import { InputBox, Git, API, Repository, Remote, RepositoryState, Branch, Ref, Submodule, Commit, Change, RepositoryUIState, Status, GitLogOptions } from './git'; +import { InputBox, Git, API, Repository, Remote, RepositoryState, Branch, Ref, Submodule, Commit, Change, RepositoryUIState, Status, LogOptions } from './git'; import { Event, SourceControlInputBox, Uri, SourceControl } from 'vscode'; import { mapEvent } from '../util'; @@ -111,25 +111,25 @@ export class ApiRepository implements Repository { diffWithHEAD(): Promise; diffWithHEAD(path: string): Promise; diffWithHEAD(path?: string): Promise { - return path ? this._repository.diffWithHEAD(path) : this._repository.diffWithHEAD(); + return this._repository.diffWithHEAD(path); } diffWith(ref: string): Promise; diffWith(ref: string, path: string): Promise; diffWith(ref: string, path?: string): Promise { - return path ? this._repository.diffWith(ref, path) : this._repository.diffWith(ref); + return this._repository.diffWith(ref, path); } diffIndexWithHEAD(): Promise; diffIndexWithHEAD(path: string): Promise; diffIndexWithHEAD(path?: string): Promise { - return path ? this._repository.diffIndexWithHEAD(path) : this._repository.diffIndexWithHEAD(); + return this._repository.diffIndexWithHEAD(path); } diffIndexWith(ref: string): Promise; diffIndexWith(ref: string, path: string): Promise; diffIndexWith(ref: string, path?: string): Promise { - return path ? this._repository.diffIndexWith(ref, path) : this._repository.diffIndexWith(ref); + return this._repository.diffIndexWith(ref, path); } diffBlobs(object1: string, object2: string): Promise { @@ -139,7 +139,7 @@ export class ApiRepository implements Repository { diffBetween(ref1: string, ref2: string): Promise; diffBetween(ref1: string, ref2: string, path: string): Promise; diffBetween(ref1: string, ref2: string, path?: string): Promise { - return path ? this._repository.diffBetween(ref1, ref2, path) : this._repository.diffBetween(ref1, ref2); + return this._repository.diffBetween(ref1, ref2, path); } hashObject(data: string): Promise { @@ -194,8 +194,8 @@ export class ApiRepository implements Repository { return this._repository.pushTo(remoteName, branchName, setUpstream); } - getLog(options?: GitLogOptions): Promise { - return this._repository.getLog(options); + log(options?: LogOptions): Promise { + return this._repository.log(options); } } diff --git a/extensions/git/src/api/git.d.ts b/extensions/git/src/api/git.d.ts index ff6ce04ab99..c5e2b3bc9d2 100644 --- a/extensions/git/src/api/git.d.ts +++ b/extensions/git/src/api/git.d.ts @@ -110,7 +110,11 @@ export interface RepositoryUIState { readonly onDidChange: Event; } -export interface GitLogOptions { +/** + * Log options. + */ +export interface LogOptions { + /** Max number of log entries to retrieve. If not specified, the default is 32. */ readonly maxEntries?: number; } @@ -167,7 +171,7 @@ export interface Repository { pull(): Promise; push(remoteName?: string, branchName?: string, setUpstream?: boolean): Promise; - getLog(options?: GitLogOptions): Promise; + log(options?: LogOptions): Promise; } export interface API { diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index d104d3cbdf4..a06598f5974 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -14,7 +14,7 @@ import * as filetype from 'file-type'; import { assign, groupBy, denodeify, IDisposable, toDisposable, dispose, mkdirp, readBytes, detectUnicodeEncoding, Encoding, onceEvent } from './util'; import { CancellationToken, Uri } from 'vscode'; import { detectEncoding } from './encoding'; -import { Ref, RefType, Branch, Remote, GitErrorCodes, GitLogOptions, Change, Status } from './api/git'; +import { Ref, RefType, Branch, Remote, GitErrorCodes, LogOptions, Change, Status } from './api/git'; const readfile = denodeify(fs.readFile); @@ -700,33 +700,36 @@ export class Repository { }); } - async getLog(options?: GitLogOptions): Promise { - const args = ['log']; - if (options) { - if (typeof options.maxEntries === 'number' && options.maxEntries > 0) { - args.push('-' + options.maxEntries); - } - } - - args.push(`--pretty=format:${COMMIT_FORMAT}%x00%x00`); + async log(options?: LogOptions): Promise { + const maxEntries = options && typeof options.maxEntries === 'number' && options.maxEntries > 0 ? options.maxEntries : 32; + const args = ['log', '-' + maxEntries, `--pretty=format:${COMMIT_FORMAT}%x00%x00`]; const gitResult = await this.run(args); if (gitResult.exitCode) { // An empty repo. return []; } - const entries = gitResult.stdout.split('\x00\x00'); + const s = gitResult.stdout; const result: Commit[] = []; - for (let entry of entries) { + let index = 0; + while (index < s.length) { + let nextIndex = s.indexOf('\x00\x00', index); + if (nextIndex === -1) { + nextIndex = s.length; + } + + let entry = s.substr(index, nextIndex - index); if (entry.startsWith('\n')) { entry = entry.substring(1); } + const commit = parseGitCommit(entry); if (!commit) { break; } result.push(commit); + index = nextIndex + 2; } return result; @@ -886,7 +889,10 @@ export class Repository { return result.stdout; } - async diffWithHEAD(path?: string): Promise { + diffWithHEAD(): Promise; + diffWithHEAD(path: string): Promise; + diffWithHEAD(path?: string | undefined): Promise; + async diffWithHEAD(path?: string | undefined): Promise { if (!path) { return await this.diffFiles(false); } @@ -896,6 +902,9 @@ export class Repository { return result.stdout; } + diffWith(ref: string): Promise; + diffWith(ref: string, path: string): Promise; + diffWith(ref: string, path?: string | undefined): Promise; async diffWith(ref: string, path?: string): Promise { if (!path) { return await this.diffFiles(false, ref); @@ -906,6 +915,9 @@ export class Repository { return result.stdout; } + diffIndexWithHEAD(): Promise; + diffIndexWithHEAD(path: string): Promise; + diffIndexWithHEAD(path?: string | undefined): Promise; async diffIndexWithHEAD(path?: string): Promise { if (!path) { return await this.diffFiles(true); @@ -916,6 +928,9 @@ export class Repository { return result.stdout; } + diffIndexWith(ref: string): Promise; + diffIndexWith(ref: string, path: string): Promise; + diffIndexWith(ref: string, path?: string | undefined): Promise; async diffIndexWith(ref: string, path?: string): Promise { if (!path) { return await this.diffFiles(true, ref); @@ -932,6 +947,9 @@ export class Repository { return result.stdout; } + diffBetween(ref1: string, ref2: string): Promise; + diffBetween(ref1: string, ref2: string, path: string): Promise; + diffBetween(ref1: string, ref2: string, path?: string | undefined): Promise; async diffBetween(ref1: string, ref2: string, path?: string): Promise { const range = `${ref1}...${ref2}`; if (!path) { diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index e9650b905a7..dc3399e3ec9 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -13,7 +13,7 @@ import * as path from 'path'; import * as nls from 'vscode-nls'; import * as fs from 'fs'; import { StatusBarCommands } from './statusbar'; -import { Branch, Ref, Remote, RefType, GitErrorCodes, Status, GitLogOptions, Change } from './api/git'; +import { Branch, Ref, Remote, RefType, GitErrorCodes, Status, LogOptions, Change } from './api/git'; const timeout = (millis: number) => new Promise(c => setTimeout(c, millis)); @@ -706,8 +706,8 @@ export class Repository implements Disposable { return this.run(Operation.Config, () => this.repository.config('local', key, value)); } - getLog(options?: GitLogOptions): Promise { - return this.run(Operation.Log, () => this.repository.getLog(options)); + log(options?: LogOptions): Promise { + return this.run(Operation.Log, () => this.repository.log(options)); } @throttle @@ -721,24 +721,28 @@ export class Repository implements Disposable { diffWithHEAD(): Promise; diffWithHEAD(path: string): Promise; + diffWithHEAD(path?: string | undefined): Promise; diffWithHEAD(path?: string | undefined): Promise { return this.run(Operation.Diff, () => this.repository.diffWithHEAD(path)); } diffWith(ref: string): Promise; diffWith(ref: string, path: string): Promise; + diffWith(ref: string, path?: string | undefined): Promise; diffWith(ref: string, path?: string): Promise { return this.run(Operation.Diff, () => this.repository.diffWith(ref, path)); } diffIndexWithHEAD(): Promise; diffIndexWithHEAD(path: string): Promise; + diffIndexWithHEAD(path?: string | undefined): Promise; diffIndexWithHEAD(path?: string): Promise { return this.run(Operation.Diff, () => this.repository.diffIndexWithHEAD(path)); } diffIndexWith(ref: string): Promise; diffIndexWith(ref: string, path: string): Promise; + diffIndexWith(ref: string, path?: string | undefined): Promise; diffIndexWith(ref: string, path?: string): Promise { return this.run(Operation.Diff, () => this.repository.diffIndexWith(ref, path)); } @@ -749,6 +753,7 @@ export class Repository implements Disposable { diffBetween(ref1: string, ref2: string): Promise; diffBetween(ref1: string, ref2: string, path: string): Promise; + diffBetween(ref1: string, ref2: string, path?: string | undefined): Promise; diffBetween(ref1: string, ref2: string, path?: string): Promise { return this.run(Operation.Diff, () => this.repository.diffBetween(ref1, ref2, path)); } From ec8b1907ad92f36100f39f6935834cb86692d4ba Mon Sep 17 00:00:00 2001 From: Asaf Cohen Date: Tue, 15 Jan 2019 12:37:22 +0200 Subject: [PATCH 015/274] Add kill terminal action in terminal context menu --- .../parts/terminal/common/terminalCommands.ts | 1 + .../electron-browser/terminalActions.ts | 20 +++++++++++++++++++ .../electron-browser/terminalPanel.ts | 4 +++- 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/terminal/common/terminalCommands.ts b/src/vs/workbench/parts/terminal/common/terminalCommands.ts index c9d27764e85..94936c13316 100644 --- a/src/vs/workbench/parts/terminal/common/terminalCommands.ts +++ b/src/vs/workbench/parts/terminal/common/terminalCommands.ts @@ -46,6 +46,7 @@ export const enum TERMINAL_COMMAND_ID { SCROLL_UP_PAGE = 'workbench.action.terminal.scrollUpPage', SCROLL_TO_TOP = 'workbench.action.terminal.scrollToTop', CLEAR = 'workbench.action.terminal.clear', + EXIT = 'workbench.action.terminal.exit', CLEAR_SELECTION = 'workbench.action.terminal.clearSelection', WORKSPACE_SHELL_ALLOW = 'workbench.action.terminal.allowWorkspaceShell', WORKSPACE_SHELL_DISALLOW = 'workbench.action.terminal.disallowWorkspaceShell', diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts index c77fbe34407..3e2aa31b390 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts @@ -885,6 +885,26 @@ export class ClearTerminalAction extends Action { return Promise.resolve(undefined); } } +export class ExitTerminalAction extends Action { + + public static readonly ID = TERMINAL_COMMAND_ID.CLEAR; + public static readonly LABEL = nls.localize('workbench.action.terminal.exit', "Exit"); + + constructor( + id: string, label: string, + @ITerminalService private readonly terminalService: ITerminalService + ) { + super(id, label); + } + + public run(event?: any): Promise { + const terminalInstance = this.terminalService.getActiveInstance(); + if (terminalInstance) { + terminalInstance.dispose(true); + } + return Promise.resolve(undefined); + } +} export class ClearSelectionTerminalAction extends Action { diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts index 3d726e0cb7a..07346f8625d 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts @@ -151,7 +151,9 @@ export class TerminalPanel extends Panel { this._instantiationService.createInstance(TerminalPasteAction, TerminalPasteAction.ID, TerminalPasteAction.SHORT_LABEL), this._instantiationService.createInstance(SelectAllTerminalAction, SelectAllTerminalAction.ID, SelectAllTerminalAction.LABEL), new Separator(), - this._instantiationService.createInstance(ClearTerminalAction, ClearTerminalAction.ID, ClearTerminalAction.LABEL) + this._instantiationService.createInstance(ClearTerminalAction, ClearTerminalAction.ID, ClearTerminalAction.LABEL), + this._instantiationService.createInstance(KillTerminalAction, KillTerminalAction.ID, KillTerminalAction.PANEL_LABEL) + ]; this._contextMenuActions.forEach(a => { this._register(a); From 6c37c4921418798b7ff9ed85ac2eb6038ed794b2 Mon Sep 17 00:00:00 2001 From: Asaf Cohen Date: Tue, 15 Jan 2019 12:38:57 +0200 Subject: [PATCH 016/274] Remove unused function and strings --- .../parts/terminal/common/terminalCommands.ts | 1 - .../electron-browser/terminalActions.ts | 20 ------------------- 2 files changed, 21 deletions(-) diff --git a/src/vs/workbench/parts/terminal/common/terminalCommands.ts b/src/vs/workbench/parts/terminal/common/terminalCommands.ts index 94936c13316..c9d27764e85 100644 --- a/src/vs/workbench/parts/terminal/common/terminalCommands.ts +++ b/src/vs/workbench/parts/terminal/common/terminalCommands.ts @@ -46,7 +46,6 @@ export const enum TERMINAL_COMMAND_ID { SCROLL_UP_PAGE = 'workbench.action.terminal.scrollUpPage', SCROLL_TO_TOP = 'workbench.action.terminal.scrollToTop', CLEAR = 'workbench.action.terminal.clear', - EXIT = 'workbench.action.terminal.exit', CLEAR_SELECTION = 'workbench.action.terminal.clearSelection', WORKSPACE_SHELL_ALLOW = 'workbench.action.terminal.allowWorkspaceShell', WORKSPACE_SHELL_DISALLOW = 'workbench.action.terminal.disallowWorkspaceShell', diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts index 3e2aa31b390..c77fbe34407 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts @@ -885,26 +885,6 @@ export class ClearTerminalAction extends Action { return Promise.resolve(undefined); } } -export class ExitTerminalAction extends Action { - - public static readonly ID = TERMINAL_COMMAND_ID.CLEAR; - public static readonly LABEL = nls.localize('workbench.action.terminal.exit', "Exit"); - - constructor( - id: string, label: string, - @ITerminalService private readonly terminalService: ITerminalService - ) { - super(id, label); - } - - public run(event?: any): Promise { - const terminalInstance = this.terminalService.getActiveInstance(); - if (terminalInstance) { - terminalInstance.dispose(true); - } - return Promise.resolve(undefined); - } -} export class ClearSelectionTerminalAction extends Action { From ac0a708d08d9d92d744826027841befdab69efa5 Mon Sep 17 00:00:00 2001 From: Rich Evans Date: Wed, 16 Jan 2019 00:02:26 -0800 Subject: [PATCH 017/274] Add xterm mouseleave to dismiss widget Add a disposable listener to the 'mouseleave' event on the terminalInstance xterm.element to dismiss any showing widget, in case the user moves their mouse out of the xterm area directly, without leaving the link that generated the widget/tooltip. The real problem is xterm.js does not appear to be listening to the 'mouseleave' event of its own dom element, and so it does not fire leaveCallback for registered CustomLinkHandler, WebLinkHandler, or LocalLinkHandlers. As a result, if the user has the terminal split near the link that generated the widget/tooltip, the widget remains until the mouse cursor re-enters the xterm area that generated it, for xterm to dismiss itself. See gif in #66421 for demonstration of the problem. fixes #66421 --- .../parts/terminal/electron-browser/terminalInstance.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts index 598227eb4fc..b59ae63ed23 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts @@ -575,6 +575,9 @@ export class TerminalInstance implements ITerminalInstance { if (this._processManager) { this._widgetManager = new TerminalWidgetManager(this._wrapperElement); + this._disposables.push(dom.addDisposableListener(this._xterm.element, 'mouseleave', () => { + this._widgetManager.closeMessage(); + })); this._linkHandler.setWidgetManager(this._widgetManager); } From 2934fcecee1d595bc5ee79aead9b19a41502d5e8 Mon Sep 17 00:00:00 2001 From: Asaf Cohen Date: Sat, 19 Jan 2019 14:47:03 +0200 Subject: [PATCH 018/274] Add separator between clear&kill --- .../workbench/parts/terminal/electron-browser/terminalPanel.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts index 07346f8625d..6395712ee95 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts @@ -152,6 +152,7 @@ export class TerminalPanel extends Panel { this._instantiationService.createInstance(SelectAllTerminalAction, SelectAllTerminalAction.ID, SelectAllTerminalAction.LABEL), new Separator(), this._instantiationService.createInstance(ClearTerminalAction, ClearTerminalAction.ID, ClearTerminalAction.LABEL), + new Separator(), this._instantiationService.createInstance(KillTerminalAction, KillTerminalAction.ID, KillTerminalAction.PANEL_LABEL) ]; From ea59a710ca419d66063592b9efd97b0e361676b5 Mon Sep 17 00:00:00 2001 From: Romain Marcadier-Muller Date: Mon, 21 Jan 2019 16:22:36 -0800 Subject: [PATCH 019/274] Fix the "tsc watch" task when references are used Moves the `--watch` option at the end of the `tsc` invocation, as `--build` is required to be the first option on the call. Fixes #66875 --- extensions/typescript-language-features/src/features/task.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/typescript-language-features/src/features/task.ts b/extensions/typescript-language-features/src/features/task.ts index c71f3c4435d..a084fc9e495 100644 --- a/extensions/typescript-language-features/src/features/task.ts +++ b/extensions/typescript-language-features/src/features/task.ts @@ -197,7 +197,7 @@ class TscTaskProvider implements vscode.TaskProvider { project.workspaceFolder || vscode.TaskScope.Workspace, localize('buildAndWatchTscLabel', 'watch - {0}', label), 'tsc', - new vscode.ShellExecution(command, ['--watch', ...args]), + new vscode.ShellExecution(command, [...args, '--watch']), '$tsc-watch'); watchTask.group = vscode.TaskGroup.Build; watchTask.isBackground = true; @@ -272,4 +272,4 @@ export default class TypeScriptTaskProviderManager { this.taskProviderSub = vscode.workspace.registerTaskProvider('typescript', new TscTaskProvider(this.client)); } } -} \ No newline at end of file +} From 82aab5a16ebd44eaaaaaf1396f82430b83723108 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Mon, 21 Jan 2019 22:15:39 -0800 Subject: [PATCH 020/274] HTML selections --- .../client/src/htmlMain.ts | 19 +- .../client/src/vscode.proposed.d.ts | 1142 +++++++++++++++++ .../html-language-features/package.json | 1 + .../server/src/htmlServerMain.ts | 15 + .../server/src/modes/htmlMode.ts | 17 +- .../server/src/modes/languageModes.ts | 1 + 6 files changed, 1192 insertions(+), 3 deletions(-) create mode 100644 extensions/html-language-features/client/src/vscode.proposed.d.ts diff --git a/extensions/html-language-features/client/src/htmlMain.ts b/extensions/html-language-features/client/src/htmlMain.ts index a95a8a7fc25..40825b84131 100644 --- a/extensions/html-language-features/client/src/htmlMain.ts +++ b/extensions/html-language-features/client/src/htmlMain.ts @@ -8,8 +8,8 @@ import * as fs from 'fs'; import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); -import { languages, ExtensionContext, IndentAction, Position, TextDocument, Range, CompletionItem, CompletionItemKind, SnippetString, workspace } from 'vscode'; -import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind, RequestType, TextDocumentPositionParams } from 'vscode-languageclient'; +import { languages, ExtensionContext, IndentAction, Position, TextDocument, Range, CompletionItem, CompletionItemKind, SnippetString, workspace, SelectionRange, SelectionRangeKind } from 'vscode'; +import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind, RequestType, TextDocumentPositionParams, TextDocumentIdentifier } from 'vscode-languageclient'; import { EMPTY_ELEMENTS } from './htmlEmptyTagsShared'; import { activateTagClosing } from './tagClosing'; import TelemetryReporter from 'vscode-extension-telemetry'; @@ -86,6 +86,21 @@ export function activate(context: ExtensionContext) { toDispose.push(disposable); }); + languages.registerSelectionRangeProvider('html', { + async provideSelectionRanges(document: TextDocument, position: Position): Promise { + const textDocument = TextDocumentIdentifier.create(document.uri.toString()); + const rawRanges: Range[] = await client.sendRequest('$/selection', { textDocument, position }); + + return rawRanges.map(r => { + const actualRange = new Range(new Position(r.start.line, r.start.character), new Position(r.end.line, r.end.character)); + return { + range: actualRange, + kind: SelectionRangeKind.Declaration + }; + }); + } + }); + languages.setLanguageConfiguration('html', { indentationRules: { increaseIndentPattern: /<(?!\?|(?:area|base|br|col|frame|hr|html|img|input|link|meta|param)\b|[^>]*\/>)([-_\.A-Za-z0-9]+)(?=\s|>)\b[^>]*>(?!.*<\/\1>)|)|\{[^}"']*$/, diff --git a/extensions/html-language-features/client/src/vscode.proposed.d.ts b/extensions/html-language-features/client/src/vscode.proposed.d.ts new file mode 100644 index 00000000000..44ad9a1d01b --- /dev/null +++ b/extensions/html-language-features/client/src/vscode.proposed.d.ts @@ -0,0 +1,1142 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/** + * This is the place for API experiments and proposals. + * These API are NOT stable and subject to change. They are only available in the Insiders + * distribution and CANNOT be used in published extensions. + * + * To test these API in local environment: + * - Use Insiders release of VS Code. + * - Add `"enableProposedApi": true` to your package.json. + * - Copy this file to your project. + */ + +declare module 'vscode' { + + //#region Joh - vscode.open + + export namespace env { + + /** + * Opens an *external* item, e.g. a http(s) or mailto-link, using the + * default application. + * + * *Note* that [`showTextDocument`](#window.showTextDocument) is the right + * way to open a text document inside the editor, not this function. + * + * @param target The uri that should be opened. + * @returns A promise indicating if open was successful. + */ + export function open(target: Uri): Thenable; + } + + //#endregion + + //#region Joh - selection range provider + + export class SelectionRangeKind { + + /** + * Empty Kind. + */ + static readonly Empty: SelectionRangeKind; + + /** + * The statment kind, its value is `statement`, possible extensions can be + * `statement.if` etc + */ + static readonly Statement: SelectionRangeKind; + + /** + * The declaration kind, its value is `declaration`, possible extensions can be + * `declaration.function`, `declaration.class` etc. + */ + static readonly Declaration: SelectionRangeKind; + + readonly value: string; + + private constructor(value: string); + + append(value: string): SelectionRangeKind; + } + + export class SelectionRange { + kind: SelectionRangeKind; + range: Range; + constructor(range: Range, kind: SelectionRangeKind); + } + + export interface SelectionRangeProvider { + /** + * Provide selection ranges starting at a given position. The first range must [contain](#Range.contains) + * position and subsequent ranges must contain the previous range. + */ + provideSelectionRanges(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; + } + + export namespace languages { + export function registerSelectionRangeProvider(selector: DocumentSelector, provider: SelectionRangeProvider): Disposable; + } + + //#endregion + + //#region Joh - read/write in chunks + + export interface FileSystemProvider { + open?(resource: Uri): number | Thenable; + close?(fd: number): void | Thenable; + read?(fd: number, pos: number, data: Uint8Array, offset: number, length: number): number | Thenable; + write?(fd: number, pos: number, data: Uint8Array, offset: number, length: number): number | Thenable; + } + + //#endregion + + //#region Rob: search provider + + /** + * The parameters of a query for text search. + */ + export interface TextSearchQuery { + /** + * The text pattern to search for. + */ + pattern: string; + + /** + * Whether or not `pattern` should match multiple lines of text. + */ + isMultiline?: boolean; + + /** + * Whether or not `pattern` should be interpreted as a regular expression. + */ + isRegExp?: boolean; + + /** + * Whether or not the search should be case-sensitive. + */ + isCaseSensitive?: boolean; + + /** + * Whether or not to search for whole word matches only. + */ + isWordMatch?: boolean; + } + + /** + * A file glob pattern to match file paths against. + * TODO@roblou - merge this with the GlobPattern docs/definition in vscode.d.ts. + * @see [GlobPattern](#GlobPattern) + */ + export type GlobString = string; + + /** + * Options common to file and text search + */ + export interface SearchOptions { + /** + * The root folder to search within. + */ + folder: Uri; + + /** + * Files that match an `includes` glob pattern should be included in the search. + */ + includes: GlobString[]; + + /** + * Files that match an `excludes` glob pattern should be excluded from the search. + */ + excludes: GlobString[]; + + /** + * Whether external files that exclude files, like .gitignore, should be respected. + * See the vscode setting `"search.useIgnoreFiles"`. + */ + useIgnoreFiles: boolean; + + /** + * Whether symlinks should be followed while searching. + * See the vscode setting `"search.followSymlinks"`. + */ + followSymlinks: boolean; + + /** + * Whether global files that exclude files, like .gitignore, should be respected. + * See the vscode setting `"search.useGlobalIgnoreFiles"`. + */ + useGlobalIgnoreFiles: boolean; + + } + + /** + * Options to specify the size of the result text preview. + * These options don't affect the size of the match itself, just the amount of preview text. + */ + export interface TextSearchPreviewOptions { + /** + * The maximum number of lines in the preview. + * Only search providers that support multiline search will ever return more than one line in the match. + */ + matchLines: number; + + /** + * The maximum number of characters included per line. + */ + charsPerLine: number; + } + + /** + * Options that apply to text search. + */ + export interface TextSearchOptions extends SearchOptions { + /** + * The maximum number of results to be returned. + */ + maxResults: number; + + /** + * Options to specify the size of the result text preview. + */ + previewOptions?: TextSearchPreviewOptions; + + /** + * Exclude files larger than `maxFileSize` in bytes. + */ + maxFileSize?: number; + + /** + * Interpret files using this encoding. + * See the vscode setting `"files.encoding"` + */ + encoding?: string; + + /** + * Number of lines of context to include before each match. + */ + beforeContext?: number; + + /** + * Number of lines of context to include after each match. + */ + afterContext?: number; + } + + /** + * Information collected when text search is complete. + */ + export interface TextSearchComplete { + /** + * Whether the search hit the limit on the maximum number of search results. + * `maxResults` on [`TextSearchOptions`](#TextSearchOptions) specifies the max number of results. + * - If exactly that number of matches exist, this should be false. + * - If `maxResults` matches are returned and more exist, this should be true. + * - If search hits an internal limit which is less than `maxResults`, this should be true. + */ + limitHit?: boolean; + } + + /** + * The parameters of a query for file search. + */ + export interface FileSearchQuery { + /** + * The search pattern to match against file paths. + */ + pattern: string; + } + + /** + * Options that apply to file search. + */ + export interface FileSearchOptions extends SearchOptions { + /** + * The maximum number of results to be returned. + */ + maxResults?: number; + + /** + * A CancellationToken that represents the session for this search query. If the provider chooses to, this object can be used as the key for a cache, + * and searches with the same session object can search the same cache. When the token is cancelled, the session is complete and the cache can be cleared. + */ + session?: CancellationToken; + } + + /** + * Options that apply to requesting the file index. + */ + export interface FileIndexOptions extends SearchOptions { } + + /** + * A preview of the text result. + */ + export interface TextSearchMatchPreview { + /** + * The matching lines of text, or a portion of the matching line that contains the match. + */ + text: string; + + /** + * The Range within `text` corresponding to the text of the match. + * The number of matches must match the TextSearchMatch's range property. + */ + matches: Range | Range[]; + } + + /** + * A match from a text search + */ + export interface TextSearchMatch { + /** + * The uri for the matching document. + */ + uri: Uri; + + /** + * The range of the match within the document, or multiple ranges for multiple matches. + */ + ranges: Range | Range[]; + + /** + * A preview of the text match. + */ + preview: TextSearchMatchPreview; + } + + /** + * A line of context surrounding a TextSearchMatch. + */ + export interface TextSearchContext { + /** + * The uri for the matching document. + */ + uri: Uri; + + /** + * One line of text. + * previewOptions.charsPerLine applies to this + */ + text: string; + + /** + * The line number of this line of context. + */ + lineNumber: number; + } + + export type TextSearchResult = TextSearchMatch | TextSearchContext; + + /** + * A FileIndexProvider provides a list of files in the given folder. VS Code will filter that list for searching with quickopen or from other extensions. + * + * A FileIndexProvider is the simpler of two ways to implement file search in VS Code. Use a FileIndexProvider if you are able to provide a listing of all files + * in a folder, and want VS Code to filter them according to the user's search query. + * + * The FileIndexProvider will be invoked once when quickopen is opened, and VS Code will filter the returned list. It will also be invoked when + * `workspace.findFiles` is called. + * + * If a [`FileSearchProvider`](#FileSearchProvider) is registered for the scheme, that provider will be used instead. + */ + export interface FileIndexProvider { + /** + * Provide the set of files in the folder. + * @param options A set of options to consider while searching. + * @param token A cancellation token. + */ + provideFileIndex(options: FileIndexOptions, token: CancellationToken): ProviderResult; + } + + /** + * A FileSearchProvider provides search results for files in the given folder that match a query string. It can be invoked by quickopen or other extensions. + * + * A FileSearchProvider is the more powerful of two ways to implement file search in VS Code. Use a FileSearchProvider if you wish to search within a folder for + * all files that match the user's query. + * + * The FileSearchProvider will be invoked on every keypress in quickopen. When `workspace.findFiles` is called, it will be invoked with an empty query string, + * and in that case, every file in the folder should be returned. + * + * @see [FileIndexProvider](#FileIndexProvider) + */ + export interface FileSearchProvider { + /** + * Provide the set of files that match a certain file path pattern. + * @param query The parameters for this query. + * @param options A set of options to consider while searching files. + * @param progress A progress callback that must be invoked for all results. + * @param token A cancellation token. + */ + provideFileSearchResults(query: FileSearchQuery, options: FileSearchOptions, token: CancellationToken): ProviderResult; + } + + /** + * A TextSearchProvider provides search results for text results inside files in the workspace. + */ + export interface TextSearchProvider { + /** + * Provide results that match the given text pattern. + * @param query The parameters for this query. + * @param options A set of options to consider while searching. + * @param progress A progress callback that must be invoked for all results. + * @param token A cancellation token. + */ + provideTextSearchResults(query: TextSearchQuery, options: TextSearchOptions, progress: Progress, token: CancellationToken): ProviderResult; + } + + /** + * Options that can be set on a findTextInFiles search. + */ + export interface FindTextInFilesOptions { + /** + * A [glob pattern](#GlobPattern) that defines the files to search for. The glob pattern + * will be matched against the file paths of files relative to their workspace. Use a [relative pattern](#RelativePattern) + * to restrict the search results to a [workspace folder](#WorkspaceFolder). + */ + include?: GlobPattern; + + /** + * A [glob pattern](#GlobPattern) that defines files and folders to exclude. The glob pattern + * will be matched against the file paths of resulting matches relative to their workspace. When `undefined` only default excludes will + * apply, when `null` no excludes will apply. + */ + exclude?: GlobPattern | null; + + /** + * The maximum number of results to search for + */ + maxResults?: number; + + /** + * Whether external files that exclude files, like .gitignore, should be respected. + * See the vscode setting `"search.useIgnoreFiles"`. + */ + useIgnoreFiles?: boolean; + + /** + * Whether global files that exclude files, like .gitignore, should be respected. + * See the vscode setting `"search.useGlobalIgnoreFiles"`. + */ + useGlobalIgnoreFiles?: boolean; + + /** + * Whether symlinks should be followed while searching. + * See the vscode setting `"search.followSymlinks"`. + */ + followSymlinks?: boolean; + + /** + * Interpret files using this encoding. + * See the vscode setting `"files.encoding"` + */ + encoding?: string; + + /** + * Options to specify the size of the result text preview. + */ + previewOptions?: TextSearchPreviewOptions; + + /** + * Number of lines of context to include before each match. + */ + beforeContext?: number; + + /** + * Number of lines of context to include after each match. + */ + afterContext?: number; + } + + export namespace workspace { + /** + * DEPRECATED + */ + export function registerSearchProvider(): Disposable; + + /** + * Register a file index provider. + * + * Only one provider can be registered per scheme. + * + * @param scheme The provider will be invoked for workspace folders that have this file scheme. + * @param provider The provider. + * @return A [disposable](#Disposable) that unregisters this provider when being disposed. + */ + export function registerFileIndexProvider(scheme: string, provider: FileIndexProvider): Disposable; + + /** + * Register a search provider. + * + * Only one provider can be registered per scheme. + * + * @param scheme The provider will be invoked for workspace folders that have this file scheme. + * @param provider The provider. + * @return A [disposable](#Disposable) that unregisters this provider when being disposed. + */ + export function registerFileSearchProvider(scheme: string, provider: FileSearchProvider): Disposable; + + /** + * Register a text search provider. + * + * Only one provider can be registered per scheme. + * + * @param scheme The provider will be invoked for workspace folders that have this file scheme. + * @param provider The provider. + * @return A [disposable](#Disposable) that unregisters this provider when being disposed. + */ + export function registerTextSearchProvider(scheme: string, provider: TextSearchProvider): Disposable; + + /** + * Search text in files across all [workspace folders](#workspace.workspaceFolders) in the workspace. + * @param query The query parameters for the search - the search string, whether it's case-sensitive, or a regex, or matches whole words. + * @param callback A callback, called for each result + * @param token A token that can be used to signal cancellation to the underlying search engine. + * @return A thenable that resolves when the search is complete. + */ + export function findTextInFiles(query: TextSearchQuery, callback: (result: TextSearchResult) => void, token?: CancellationToken): Thenable; + + /** + * Search text in files across all [workspace folders](#workspace.workspaceFolders) in the workspace. + * @param query The query parameters for the search - the search string, whether it's case-sensitive, or a regex, or matches whole words. + * @param options An optional set of query options. Include and exclude patterns, maxResults, etc. + * @param callback A callback, called for each result + * @param token A token that can be used to signal cancellation to the underlying search engine. + * @return A thenable that resolves when the search is complete. + */ + export function findTextInFiles(query: TextSearchQuery, options: FindTextInFilesOptions, callback: (result: TextSearchResult) => void, token?: CancellationToken): Thenable; + } + + //#endregion + + //#region Joao: diff command + + /** + * The contiguous set of modified lines in a diff. + */ + export interface LineChange { + readonly originalStartLineNumber: number; + readonly originalEndLineNumber: number; + readonly modifiedStartLineNumber: number; + readonly modifiedEndLineNumber: number; + } + + export namespace commands { + + /** + * Registers a diff information command that can be invoked via a keyboard shortcut, + * a menu item, an action, or directly. + * + * Diff information commands are different from ordinary [commands](#commands.registerCommand) as + * they only execute when there is an active diff editor when the command is called, and the diff + * information has been computed. Also, the command handler of an editor command has access to + * the diff information. + * + * @param command A unique identifier for the command. + * @param callback A command handler function with access to the [diff information](#LineChange). + * @param thisArg The `this` context used when invoking the handler function. + * @return Disposable which unregisters this command on disposal. + */ + export function registerDiffInformationCommand(command: string, callback: (diff: LineChange[], ...args: any[]) => any, thisArg?: any): Disposable; + } + + //#endregion + + //#region Joh: decorations + + //todo@joh -> make class + export interface DecorationData { + letter?: string; + title?: string; + color?: ThemeColor; + priority?: number; + bubble?: boolean; + source?: string; // hacky... we should remove it and use equality under the hood + } + + export interface SourceControlResourceDecorations { + source?: string; + letter?: string; + color?: ThemeColor; + } + + export interface DecorationProvider { + onDidChangeDecorations: Event; + provideDecoration(uri: Uri, token: CancellationToken): ProviderResult; + } + + export namespace window { + export function registerDecorationProvider(provider: DecorationProvider): Disposable; + } + + //#endregion + + //#region André: debug + + // deprecated + + export interface DebugConfigurationProvider { + /** + * Deprecated, use DebugAdapterDescriptorFactory.provideDebugAdapter instead. + * @deprecated Use DebugAdapterDescriptorFactory.createDebugAdapterDescriptor instead + */ + debugAdapterExecutable?(folder: WorkspaceFolder | undefined, token?: CancellationToken): ProviderResult; + } + + //#endregion + + //#region Rob, Matt: logging + + /** + * The severity level of a log message + */ + export enum LogLevel { + Trace = 1, + Debug = 2, + Info = 3, + Warning = 4, + Error = 5, + Critical = 6, + Off = 7 + } + + export namespace env { + /** + * Current logging level. + */ + export const logLevel: LogLevel; + + /** + * An [event](#Event) that fires when the log level has changed. + */ + export const onDidChangeLogLevel: Event; + } + + //#endregion + + //#region Joao: SCM validation + + /** + * Represents the validation type of the Source Control input. + */ + export enum SourceControlInputBoxValidationType { + + /** + * Something not allowed by the rules of a language or other means. + */ + Error = 0, + + /** + * Something suspicious but allowed. + */ + Warning = 1, + + /** + * Something to inform about but not a problem. + */ + Information = 2 + } + + export interface SourceControlInputBoxValidation { + + /** + * The validation message to display. + */ + readonly message: string; + + /** + * The validation type. + */ + readonly type: SourceControlInputBoxValidationType; + } + + /** + * Represents the input box in the Source Control viewlet. + */ + export interface SourceControlInputBox { + + /** + * A validation function for the input box. It's possible to change + * the validation provider simply by setting this property to a different function. + */ + validateInput?(value: string, cursorPosition: number): ProviderResult; + } + + //#endregion + + //#region Joao: SCM selected provider + + export interface SourceControl { + + /** + * Whether the source control is selected. + */ + readonly selected: boolean; + + /** + * An event signaling when the selection state changes. + */ + readonly onDidChangeSelection: Event; + } + + //#endregion + + //#region Joao: SCM Input Box + + /** + * Represents the input box in the Source Control viewlet. + */ + export interface SourceControlInputBox { + + /** + * Controls whether the input box is visible (default is `true`). + */ + visible: boolean; + } + + //#endregion + + //#region Comments + /** + * Comments provider related APIs are still in early stages, they may be changed significantly during our API experiments. + */ + + interface CommentInfo { + /** + * All of the comment threads associated with the document. + */ + threads: CommentThread[]; + + /** + * The ranges of the document which support commenting. + */ + commentingRanges?: Range[]; + + /** + * If it's in draft mode or not + */ + inDraftMode?: boolean; + } + + export enum CommentThreadCollapsibleState { + /** + * Determines an item is collapsed + */ + Collapsed = 0, + /** + * Determines an item is expanded + */ + Expanded = 1 + } + + /** + * A collection of comments representing a conversation at a particular range in a document. + */ + interface CommentThread { + /** + * A unique identifier of the comment thread. + */ + threadId: string; + + /** + * The uri of the document the thread has been created on. + */ + resource: Uri; + + /** + * The range the comment thread is located within the document. The thread icon will be shown + * at the first line of the range. + */ + range: Range; + + /** + * The ordered comments of the thread. + */ + comments: Comment[]; + + /** + * Whether the thread should be collapsed or expanded when opening the document. Defaults to Collapsed. + */ + collapsibleState?: CommentThreadCollapsibleState; + } + + /** + * A comment is displayed within the editor or the Comments Panel, depending on how it is provided. + */ + interface Comment { + /** + * The id of the comment + */ + commentId: string; + + /** + * The text of the comment + */ + body: MarkdownString; + + /** + * The display name of the user who created the comment + */ + userName: string; + + /** + * The icon path for the user who created the comment + */ + userIconPath?: Uri; + + + /** + * @deprecated Use userIconPath instead. The avatar src of the user who created the comment + */ + gravatar?: string; + + /** + * Whether the current user has permission to edit the comment. + * + * This will be treated as false if the comment is provided by a `WorkspaceCommentProvider`, or + * if it is provided by a `DocumentCommentProvider` and no `editComment` method is given. + */ + canEdit?: boolean; + + /** + * Whether the current user has permission to delete the comment. + * + * This will be treated as false if the comment is provided by a `WorkspaceCommentProvider`, or + * if it is provided by a `DocumentCommentProvider` and no `deleteComment` method is given. + */ + canDelete?: boolean; + + /** + * The command to be executed if the comment is selected in the Comments Panel + */ + command?: Command; + + isDraft?: boolean; + } + + export interface CommentThreadChangedEvent { + /** + * Added comment threads. + */ + readonly added: CommentThread[]; + + /** + * Removed comment threads. + */ + readonly removed: CommentThread[]; + + /** + * Changed comment threads. + */ + readonly changed: CommentThread[]; + + /** + * Changed draft mode + */ + readonly inDraftMode: boolean; + } + + interface DocumentCommentProvider { + /** + * Provide the commenting ranges and comment threads for the given document. The comments are displayed within the editor. + */ + provideDocumentComments(document: TextDocument, token: CancellationToken): Promise; + + /** + * Called when a user adds a new comment thread in the document at the specified range, with body text. + */ + createNewCommentThread(document: TextDocument, range: Range, text: string, token: CancellationToken): Promise; + + /** + * Called when a user replies to a new comment thread in the document at the specified range, with body text. + */ + replyToCommentThread(document: TextDocument, range: Range, commentThread: CommentThread, text: string, token: CancellationToken): Promise; + + /** + * Called when a user edits the comment body to the be new text. + */ + editComment?(document: TextDocument, comment: Comment, text: string, token: CancellationToken): Promise; + + /** + * Called when a user deletes the comment. + */ + deleteComment?(document: TextDocument, comment: Comment, token: CancellationToken): Promise; + + startDraft?(document: TextDocument, token: CancellationToken): Promise; + deleteDraft?(document: TextDocument, token: CancellationToken): Promise; + finishDraft?(document: TextDocument, token: CancellationToken): Promise; + + startDraftLabel?: string; + deleteDraftLabel?: string; + finishDraftLabel?: string; + + /** + * Notify of updates to comment threads. + */ + onDidChangeCommentThreads: Event; + } + + interface WorkspaceCommentProvider { + /** + * Provide all comments for the workspace. Comments are shown within the comments panel. Selecting a comment + * from the panel runs the comment's command. + */ + provideWorkspaceComments(token: CancellationToken): Promise; + + /** + * Notify of updates to comment threads. + */ + onDidChangeCommentThreads: Event; + } + + namespace workspace { + export function registerDocumentCommentProvider(provider: DocumentCommentProvider): Disposable; + export function registerWorkspaceCommentProvider(provider: WorkspaceCommentProvider): Disposable; + } + //#endregion + + //#region Terminal + + export interface Terminal { + /** + * Fires when the terminal's pty slave pseudo-device is written to. In other words, this + * provides access to the raw data stream from the process running within the terminal, + * including VT sequences. + */ + onDidWriteData: Event; + } + + /** + * Represents the dimensions of a terminal. + */ + export interface TerminalDimensions { + /** + * The number of columns in the terminal. + */ + readonly columns: number; + + /** + * The number of rows in the terminal. + */ + readonly rows: number; + } + + /** + * Represents a terminal without a process where all interaction and output in the terminal is + * controlled by an extension. This is similar to an output window but has the same VT sequence + * compatility as the regular terminal. + * + * Note that an instance of [Terminal](#Terminal) will be created when a TerminalRenderer is + * created with all its APIs available for use by extensions. When using the Terminal object + * of a TerminalRenderer it acts just like normal only the extension that created the + * TerminalRenderer essentially acts as a process. For example when an + * [Terminal.onDidWriteData](#Terminal.onDidWriteData) listener is registered, that will fire + * when [TerminalRenderer.write](#TerminalRenderer.write) is called. Similarly when + * [Terminal.sendText](#Terminal.sendText) is triggered that will fire the + * [TerminalRenderer.onDidAcceptInput](#TerminalRenderer.onDidAcceptInput) event. + * + * **Example:** Create a terminal renderer, show it and write hello world in red + * ```typescript + * const renderer = window.createTerminalRenderer('foo'); + * renderer.terminal.then(t => t.show()); + * renderer.write('\x1b[31mHello world\x1b[0m'); + * ``` + */ + export interface TerminalRenderer { + /** + * The name of the terminal, this will appear in the terminal selector. + */ + name: string; + + /** + * The dimensions of the terminal, the rows and columns of the terminal can only be set to + * a value smaller than the maximum value, if this is undefined the terminal will auto fit + * to the maximum value [maximumDimensions](TerminalRenderer.maximumDimensions). + * + * **Example:** Override the dimensions of a TerminalRenderer to 20 columns and 10 rows + * ```typescript + * terminalRenderer.dimensions = { + * cols: 20, + * rows: 10 + * }; + * ``` + */ + dimensions: TerminalDimensions | undefined; + + /** + * The maximum dimensions of the terminal, this will be undefined immediately after a + * terminal renderer is created and also until the terminal becomes visible in the UI. + * Listen to [onDidChangeMaximumDimensions](TerminalRenderer.onDidChangeMaximumDimensions) + * to get notified when this value changes. + */ + readonly maximumDimensions: TerminalDimensions | undefined; + + /** + * The corressponding [Terminal](#Terminal) for this TerminalRenderer. + */ + readonly terminal: Terminal; + + /** + * Write text to the terminal. Unlike [Terminal.sendText](#Terminal.sendText) which sends + * text to the underlying _process_, this will write the text to the terminal itself. + * + * **Example:** Write red text to the terminal + * ```typescript + * terminalRenderer.write('\x1b[31mHello world\x1b[0m'); + * ``` + * + * **Example:** Move the cursor to the 10th row and 20th column and write an asterisk + * ```typescript + * terminalRenderer.write('\x1b[10;20H*'); + * ``` + * + * @param text The text to write. + */ + write(text: string): void; + + /** + * An event which fires on keystrokes in the terminal or when an extension calls + * [Terminal.sendText](#Terminal.sendText). Keystrokes are converted into their + * corresponding VT sequence representation. + * + * **Example:** Simulate interaction with the terminal from an outside extension or a + * workbench command such as `workbench.action.terminal.runSelectedText` + * ```typescript + * const terminalRenderer = window.createTerminalRenderer('test'); + * terminalRenderer.onDidAcceptInput(data => { + * cosole.log(data); // 'Hello world' + * }); + * terminalRenderer.terminal.then(t => t.sendText('Hello world')); + * ``` + */ + readonly onDidAcceptInput: Event; + + /** + * An event which fires when the [maximum dimensions](#TerminalRenderer.maimumDimensions) of + * the terminal renderer change. + */ + readonly onDidChangeMaximumDimensions: Event; + } + + export namespace window { + /** + * Create a [TerminalRenderer](#TerminalRenderer). + * + * @param name The name of the terminal renderer, this shows up in the terminal selector. + */ + export function createTerminalRenderer(name: string): TerminalRenderer; + } + + //#endregion + + //#region Joh -> exclusive document filters + + export interface DocumentFilter { + exclusive?: boolean; + } + + //#endregion + + //#region mjbvz,joh: https://github.com/Microsoft/vscode/issues/43768 + export interface FileRenameEvent { + readonly oldUri: Uri; + readonly newUri: Uri; + } + + export interface FileWillRenameEvent { + readonly oldUri: Uri; + readonly newUri: Uri; + waitUntil(thenable: Thenable): void; + } + + export namespace workspace { + export const onWillRenameFile: Event; + export const onDidRenameFile: Event; + } + //#endregion + + //#region Alex - OnEnter enhancement + export interface OnEnterRule { + /** + * This rule will only execute if the text above the this line matches this regular expression. + */ + oneLineAboveText?: RegExp; + } + //#endregion + + //#region Tree View + + export interface TreeView { + + /** + * An optional human-readable message that will be rendered in the view. + */ + message?: string | MarkdownString; + + } + + /** + * Label describing the [Tree item](#TreeItem) + */ + export interface TreeItemLabel { + + /** + * A human-readable string describing the [Tree item](#TreeItem). + */ + label: string; + + /** + * Ranges in the label to highlight. A range is defined as a tuple of two number where the + * first is the inclusive start index and the second the exclusive end index + */ + highlights?: [number, number][]; + + } + + export class TreeItem2 extends TreeItem { + /** + * Label describing this item. When `falsy`, it is derived from [resourceUri](#TreeItem.resourceUri). + */ + label?: string | TreeItemLabel | /* for compilation */ any; + + /** + * @param label Label describing this item + * @param collapsibleState [TreeItemCollapsibleState](#TreeItemCollapsibleState) of the tree item. Default is [TreeItemCollapsibleState.None](#TreeItemCollapsibleState.None) + */ + constructor(label: TreeItemLabel, collapsibleState?: TreeItemCollapsibleState); + } + //#endregion + + //#region SignatureHelpContext active paramters - mjbvz + export interface SignatureHelpContext { + /** + * The currently active [`SignatureHelp`](#SignatureHelp). + * + * Will have the [`SignatureHelp.activeSignature`] field updated based on user arrowing through sig help + */ + readonly activeSignatureHelp?: SignatureHelp; + } + //#endregion + + //#region CodeAction.isPreferred - mjbvz + export interface CodeAction { + /** + * If the action is a preferred action or fix to take. + * + * A quick fix should be marked preferred if it properly addresses the underlying error. + * A refactoring should be marked preferred if it is the most reasonable choice of actions to take. + */ + isPreferred?: boolean; + } + //#endregion + + + //#region Autofix - mjbvz + export namespace CodeActionKind { + /** + * Base kind for an auto fix source action: `source.autoFix`. + */ + export const SourceAutoFix: CodeActionKind; + } + //#endregion +} \ No newline at end of file diff --git a/extensions/html-language-features/package.json b/extensions/html-language-features/package.json index 812b6104c1b..ebcde114c3f 100644 --- a/extensions/html-language-features/package.json +++ b/extensions/html-language-features/package.json @@ -1,4 +1,5 @@ { + "enableProposedApi": true, "name": "html-language-features", "displayName": "%displayName%", "description": "%description%", diff --git a/extensions/html-language-features/server/src/htmlServerMain.ts b/extensions/html-language-features/server/src/htmlServerMain.ts index cec1d1da31e..06921362d85 100644 --- a/extensions/html-language-features/server/src/htmlServerMain.ts +++ b/extensions/html-language-features/server/src/htmlServerMain.ts @@ -480,6 +480,21 @@ connection.onFoldingRanges((params, token) => { }, null, `Error while computing folding regions for ${params.textDocument.uri}`, token); }); +connection.onRequest('$/selection', async (params) => { + const document = documents.get(params.textDocument.uri); + const position: Position = params.position; + + if (document) { + const htmlMode = languageModes.getMode('html'); + if (htmlMode && htmlMode.doSelection) { + return htmlMode.doSelection(document, position); + } + + console.log(position.line, position.character); + } + return Promise.resolve(null); +}); + // Listen on the connection connection.listen(); \ No newline at end of file diff --git a/extensions/html-language-features/server/src/modes/htmlMode.ts b/extensions/html-language-features/server/src/modes/htmlMode.ts index 65756c5ee14..e5b582663fb 100644 --- a/extensions/html-language-features/server/src/modes/htmlMode.ts +++ b/extensions/html-language-features/server/src/modes/htmlMode.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { getLanguageModelCache } from '../languageModelCache'; -import { LanguageService as HTMLLanguageService, HTMLDocument, DocumentContext, FormattingOptions, HTMLFormatConfiguration } from 'vscode-html-languageservice'; +import { LanguageService as HTMLLanguageService, HTMLDocument, DocumentContext, FormattingOptions, HTMLFormatConfiguration, Node } from 'vscode-html-languageservice'; import { TextDocument, Position, Range, CompletionItem, FoldingRange } from 'vscode-languageserver-types'; import { LanguageMode, Workspace } from './languageModes'; import { getPathCompletionParticipant } from './pathCompletion'; @@ -15,6 +15,21 @@ export function getHTMLMode(htmlLanguageService: HTMLLanguageService, workspace: getId() { return 'html'; }, + doSelection(document: TextDocument, position: Position): Range[] { + const htmlDocument = htmlDocuments.get(document); + let currNode = htmlDocument.findNodeAt(document.offsetAt(position)); + let getNodeRange = (n: Node) => { + return Range.create(document.positionAt(n.start), document.positionAt(n.end)); + }; + const result = [getNodeRange(currNode)]; + + while (currNode.parent) { + currNode = currNode.parent; + result.push(getNodeRange(currNode)); + } + + return result; + }, doComplete(document: TextDocument, position: Position, settings = workspace.settings) { let options = settings && settings.html && settings.html.suggest; let doAutoComplete = settings && settings.html && settings.html.autoClosingTags; diff --git a/extensions/html-language-features/server/src/modes/languageModes.ts b/extensions/html-language-features/server/src/modes/languageModes.ts index e0b073bdaef..63d295c014b 100644 --- a/extensions/html-language-features/server/src/modes/languageModes.ts +++ b/extensions/html-language-features/server/src/modes/languageModes.ts @@ -31,6 +31,7 @@ export interface Workspace { export interface LanguageMode { getId(): string; + doSelection?: (document: TextDocument, position: Position) => Range[]; doValidation?: (document: TextDocument, settings?: Settings) => Diagnostic[]; doComplete?: (document: TextDocument, position: Position, settings?: Settings) => CompletionList; doResolve?: (document: TextDocument, item: CompletionItem) => CompletionItem; From d1690ad706df71ca35b9212424bc112b4ba400f6 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Tue, 22 Jan 2019 11:25:41 -0800 Subject: [PATCH 021/274] Improve granularity --- .../server/src/modes/htmlMode.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/extensions/html-language-features/server/src/modes/htmlMode.ts b/extensions/html-language-features/server/src/modes/htmlMode.ts index e5b582663fb..00dc9313a37 100644 --- a/extensions/html-language-features/server/src/modes/htmlMode.ts +++ b/extensions/html-language-features/server/src/modes/htmlMode.ts @@ -18,14 +18,21 @@ export function getHTMLMode(htmlLanguageService: HTMLLanguageService, workspace: doSelection(document: TextDocument, position: Position): Range[] { const htmlDocument = htmlDocuments.get(document); let currNode = htmlDocument.findNodeAt(document.offsetAt(position)); - let getNodeRange = (n: Node) => { - return Range.create(document.positionAt(n.start), document.positionAt(n.end)); + let getNodeRanges = (n: Node) => { + if (n.startTagEnd && n.endTagStart && n.startTagEnd < n.endTagStart) { + return [ + Range.create(document.positionAt(n.startTagEnd), document.positionAt(n.endTagStart)), + Range.create(document.positionAt(n.start), document.positionAt(n.end)), + ]; + } + + return [Range.create(document.positionAt(n.start), document.positionAt(n.end))]; }; - const result = [getNodeRange(currNode)]; + const result = [...getNodeRanges(currNode)]; while (currNode.parent) { currNode = currNode.parent; - result.push(getNodeRange(currNode)); + getNodeRanges(currNode).forEach(r => result.push(r)); } return result; From d372fa0697992a4bc3b7af8b07737e17cea2d383 Mon Sep 17 00:00:00 2001 From: Phil Marshall Date: Mon, 21 Jan 2019 23:25:05 -0600 Subject: [PATCH 022/274] md extension should underline images used as link description --- .../src/features/documentLinkProvider.ts | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/extensions/markdown-language-features/src/features/documentLinkProvider.ts b/extensions/markdown-language-features/src/features/documentLinkProvider.ts index f4e1bd0a9d2..ded01382275 100644 --- a/extensions/markdown-language-features/src/features/documentLinkProvider.ts +++ b/extensions/markdown-language-features/src/features/documentLinkProvider.ts @@ -51,7 +51,7 @@ function matchAll( } export default class LinkProvider implements vscode.DocumentLinkProvider { - private readonly linkPattern = /(\[[^\]]*\]\(\s*)((([^\s\(\)]|\(\S*?\))+))\s*(".*?")?\)/g; + private readonly linkPattern = /(\[((!\[(.+)\]\()(.+)\)\]|[^\]]*\])\(\s*)((([^\s\(\)]|\(\S*?\))+))\s*(".*?")?\)/g; private readonly referenceLinkPattern = /(\[([^\]]+)\]\[\s*?)([^\s\]]*?)\]/g; private readonly definitionPattern = /^([\t ]*\[([^\]]+)\]:\s*)(\S+)/gm; @@ -74,7 +74,7 @@ export default class LinkProvider implements vscode.DocumentLinkProvider { const results: vscode.DocumentLink[] = []; for (const match of matchAll(this.linkPattern, text)) { const pre = match[1]; - const link = match[2]; + const link = match[6]; const offset = (match.index || 0) + pre.length; const linkStart = document.positionAt(offset); const linkEnd = document.positionAt(offset + link.length); @@ -85,6 +85,20 @@ export default class LinkProvider implements vscode.DocumentLinkProvider { } catch (e) { // noop } + if (match[5]) { + const imagePre = match[3]; + const imageLink = match[5]; + const imageOffset = (match.index || 0) + imagePre.length + 1; + const imageLinkStart = document.positionAt(imageOffset); + const imageLinkEnd = document.positionAt(imageOffset + imageLink.length); + try { + results.push(new vscode.DocumentLink( + new vscode.Range(imageLinkStart, imageLinkEnd), + normalizeLink(document, imageLink, base))); + } catch (e) { + // noop + } + } } return results; From b1de159292ff8ad57aa2190212abfda25e95f51e Mon Sep 17 00:00:00 2001 From: Phil Marshall Date: Tue, 22 Jan 2019 18:36:45 -0600 Subject: [PATCH 023/274] added test for markdown image link underline --- .../src/test/documentLinkProvider.test.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts b/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts index 5a1d0c87880..1d94141d76e 100644 --- a/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts +++ b/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts @@ -103,6 +103,14 @@ suite('markdown.DocumentLinkProvider', () => { assertRangeEqual(link1.range, new vscode.Range(0, 10, 0, 14)); assertRangeEqual(link2.range, new vscode.Range(0, 23, 0, 28)); }); + + test('should handle hyperlinked images', () => { + const links = getLinksForFile('[![alt text](image.jpg)](https://example.com)'); + assert.strictEqual(links.length, 2); + const [link1, link2] = links; + assertRangeEqual(link1.range, new vscode.Range(0,25,0,44)); + assertRangeEqual(link2.range, new vscode.Range(0,13,0,22)); + }); }); From beb8c6fb6183ed9e47f0495166707549aedafd59 Mon Sep 17 00:00:00 2001 From: Rudi Chen Date: Wed, 23 Jan 2019 00:02:42 -0800 Subject: [PATCH 024/274] Review: cast instead of creating new variable --- src/vs/editor/contrib/documentSymbols/outlineModel.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/contrib/documentSymbols/outlineModel.ts b/src/vs/editor/contrib/documentSymbols/outlineModel.ts index 673e8bc614b..01163baabe1 100644 --- a/src/vs/editor/contrib/documentSymbols/outlineModel.ts +++ b/src/vs/editor/contrib/documentSymbols/outlineModel.ts @@ -176,8 +176,6 @@ export class OutlineGroup extends TreeElement { } private _updateMarker(markers: IMarker[], item: OutlineElement): void { - let filteredMarkers: Array = markers; - item.marker = undefined; // find the proper start index to check for item/marker overlap. @@ -200,7 +198,7 @@ export class OutlineGroup extends TreeElement { // and store them in a 'private' array. let marker = markers[start]; myMarkers.push(marker); - filteredMarkers[start] = undefined; + (markers as Array)[start] = undefined; if (!myTopSev || marker.severity > myTopSev) { myTopSev = marker.severity; } @@ -221,7 +219,7 @@ export class OutlineGroup extends TreeElement { }; } - coalesceInPlace(filteredMarkers); + coalesceInPlace(markers); } } From 06e4e227e88fb3f7e448bc967ab7458e45323ecc Mon Sep 17 00:00:00 2001 From: Rudi Chen Date: Wed, 23 Jan 2019 00:18:02 -0800 Subject: [PATCH 025/274] strictNullCheck merge conflicts --- src/vs/workbench/browser/parts/editor/breadcrumbs.ts | 4 ++-- src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbs.ts b/src/vs/workbench/browser/parts/editor/breadcrumbs.ts index eba8d1508bc..43ae1ecc383 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbs.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbs.ts @@ -23,7 +23,7 @@ export interface IBreadcrumbsService { register(group: GroupIdentifier, widget: BreadcrumbsWidget): IDisposable; - getWidget(group: GroupIdentifier): BreadcrumbsWidget; + getWidget(group: GroupIdentifier): BreadcrumbsWidget | undefined; } @@ -43,7 +43,7 @@ export class BreadcrumbsService implements IBreadcrumbsService { }; } - getWidget(group: number): BreadcrumbsWidget { + getWidget(group: number): BreadcrumbsWidget | undefined { return this._map.get(group); } } diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts index 70dbea9d198..5edc694dd85 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts @@ -117,7 +117,7 @@ export class EditorBreadcrumbsModel { info.path.unshift(new FileElement(uriPrefix, info.path.length === 0 ? FileKind.FILE : FileKind.FOLDER)); let prevPathLength = uriPrefix.path.length; uriPrefix = dirname(uriPrefix); - if (uriPrefix.path.length === prevPathLength) { + if (!uriPrefix || uriPrefix.path.length === prevPathLength) { break; } } From 16d3d13c5b92bc8630aa33064a7b814ac360380c Mon Sep 17 00:00:00 2001 From: alexet Date: Wed, 23 Jan 2019 15:46:23 +0000 Subject: [PATCH 026/274] Remove pointless conjucntion in documentTracker --- extensions/merge-conflict/src/documentTracker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/merge-conflict/src/documentTracker.ts b/extensions/merge-conflict/src/documentTracker.ts index 97eeae6f87e..41be7a803e2 100644 --- a/extensions/merge-conflict/src/documentTracker.ts +++ b/extensions/merge-conflict/src/documentTracker.ts @@ -127,7 +127,7 @@ export default class DocumentMergeConflictTracker implements vscode.Disposable, } private getCacheKey(document: vscode.TextDocument): string | null { - if (document.uri && document.uri) { + if (document.uri) { return document.uri.toString(); } From 2a0a6ebad95e1483a81682e97d4ff91ae730edda Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 23 Jan 2019 16:48:14 +0100 Subject: [PATCH 027/274] debug: do not auto focus other threads / sessions while a session is still running fixes #65920 --- .../parts/debug/electron-browser/debugService.ts | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/vs/workbench/parts/debug/electron-browser/debugService.ts b/src/vs/workbench/parts/debug/electron-browser/debugService.ts index 8af829fa6e7..ce31f940835 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugService.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugService.ts @@ -489,17 +489,7 @@ export class DebugService implements IDebugService { } private registerSessionListeners(session: IDebugSession): void { - const sessionRunningScheduler = new RunOnceScheduler(() => { - // Do not immediatly focus another session or thread if a session is running - // Stepping in a session should preserve that session focused even if some continued events happen - if (session.state === State.Running && this.viewModel.focusedSession === session) { - this.focusStackFrame(undefined); - } - }, 200); this.toDispose.push(session.onDidChangeState(() => { - if (session.state === State.Running && this.viewModel.focusedSession === session) { - sessionRunningScheduler.schedule(); - } if (session === this.viewModel.focusedSession) { this.onStateChange(); } From 9cc66db3c6c868833d5f31a709d9c886f75f1932 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 23 Jan 2019 17:06:53 +0100 Subject: [PATCH 028/274] - Have only single installed extension - Exclude not supported extensions - Show remote badge --- .../extensions/browser/extensionsWidgets.ts | 135 ---------- .../parts/extensions/common/extensions.ts | 1 - .../electron-browser/extensionEditor.ts | 9 +- .../electron-browser/extensionsActions.ts | 239 +---------------- .../electron-browser/extensionsList.ts | 55 +--- .../electron-browser/extensionsViewlet.ts | 33 +-- .../electron-browser/extensionsWidgets.ts | 244 ++++++++++++++++++ .../media/EmptyStar.svg | 0 .../media/FullStarLight.svg | 0 .../media/HalfStarLight.svg | 0 .../media/extensionsViewlet.css | 25 +- .../media/extensionsWidgets.css | 8 + .../node/extensionsWorkbenchService.ts | 150 ++++------- .../extensionsWorkbenchService.test.ts | 8 +- 14 files changed, 358 insertions(+), 549 deletions(-) delete mode 100644 src/vs/workbench/parts/extensions/browser/extensionsWidgets.ts create mode 100644 src/vs/workbench/parts/extensions/electron-browser/extensionsWidgets.ts rename src/vs/workbench/parts/extensions/{browser => electron-browser}/media/EmptyStar.svg (100%) rename src/vs/workbench/parts/extensions/{browser => electron-browser}/media/FullStarLight.svg (100%) rename src/vs/workbench/parts/extensions/{browser => electron-browser}/media/HalfStarLight.svg (100%) rename src/vs/workbench/parts/extensions/{browser => electron-browser}/media/extensionsWidgets.css (89%) diff --git a/src/vs/workbench/parts/extensions/browser/extensionsWidgets.ts b/src/vs/workbench/parts/extensions/browser/extensionsWidgets.ts deleted file mode 100644 index b70f618d51a..00000000000 --- a/src/vs/workbench/parts/extensions/browser/extensionsWidgets.ts +++ /dev/null @@ -1,135 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import 'vs/css!./media/extensionsWidgets'; -import { Disposable } from 'vs/base/common/lifecycle'; -import { IExtension, IExtensionsWorkbenchService, IExtensionContainer } from '../common/extensions'; -import { append, $, addClass } from 'vs/base/browser/dom'; -import * as platform from 'vs/base/common/platform'; -import { localize } from 'vs/nls'; - -export abstract class ExtensionWidget extends Disposable implements IExtensionContainer { - private _extension: IExtension; - get extension(): IExtension { return this._extension; } - set extension(extension: IExtension) { this._extension = extension; this.update(); } - update(): void { this.render(); } - abstract render(): void; -} - -export class Label extends ExtensionWidget { - - constructor( - private element: HTMLElement, - private fn: (extension: IExtension) => string, - @IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService - ) { - super(); - this.render(); - } - - render(): void { - this.element.textContent = this.extension ? this.fn(this.extension) : ''; - } -} - -export class InstallCountWidget extends ExtensionWidget { - - constructor( - private container: HTMLElement, - private small: boolean, - @IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService - ) { - super(); - addClass(container, 'extension-install-count'); - this.render(); - } - - render(): void { - this.container.innerHTML = ''; - - if (!this.extension) { - return; - } - - const installCount = this.extension.installCount; - - if (installCount === undefined) { - return; - } - - let installLabel: string; - - if (this.small) { - if (installCount > 1000000) { - installLabel = `${Math.floor(installCount / 100000) / 10}M`; - } else if (installCount > 1000) { - installLabel = `${Math.floor(installCount / 1000)}K`; - } else { - installLabel = String(installCount); - } - } - else { - installLabel = installCount.toLocaleString(platform.locale); - } - - append(this.container, $('span.octicon.octicon-cloud-download')); - const count = append(this.container, $('span.count')); - count.textContent = installLabel; - } -} - -export class RatingsWidget extends ExtensionWidget { - - constructor( - private container: HTMLElement, - private small: boolean, - @IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService - ) { - super(); - addClass(container, 'extension-ratings'); - - if (this.small) { - addClass(container, 'small'); - } - - this.render(); - } - - render(): void { - this.container.innerHTML = ''; - - if (!this.extension) { - return; - } - - if (this.extension.rating === undefined) { - return; - } - - if (this.small && !this.extension.ratingCount) { - return; - } - - const rating = Math.round(this.extension.rating * 2) / 2; - - if (this.small) { - append(this.container, $('span.full.star')); - - const count = append(this.container, $('span.count')); - count.textContent = String(rating); - } else { - for (let i = 1; i <= 5; i++) { - if (rating >= i) { - append(this.container, $('span.full.star')); - } else if (rating >= i - 0.5) { - append(this.container, $('span.half.star')); - } else { - append(this.container, $('span.empty.star')); - } - } - } - this.container.title = this.extension.ratingCount > 1 ? localize('ratedByUsers', "Rated by {0} users", this.extension.ratingCount) : localize('ratedBySingleUser', "Rated by 1 user"); - } -} diff --git a/src/vs/workbench/parts/extensions/common/extensions.ts b/src/vs/workbench/parts/extensions/common/extensions.ts index 5c8339ab05a..803a1e6a512 100644 --- a/src/vs/workbench/parts/extensions/common/extensions.ts +++ b/src/vs/workbench/parts/extensions/common/extensions.ts @@ -60,7 +60,6 @@ export interface IExtension { getChangelog(token: CancellationToken): Promise; hasChangelog(): boolean; local?: ILocalExtension; - locals?: ILocalExtension[]; gallery?: IGalleryExtension; isMalicious: boolean; } diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts index c5333366afe..1e1fba41b8b 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts @@ -25,10 +25,10 @@ import { IExtensionManifest, IKeyBinding, IView, IViewContainer, ExtensionType } import { ResolvedKeybinding, KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { ExtensionsInput } from 'vs/workbench/parts/extensions/common/extensionsInput'; import { IExtensionsWorkbenchService, IExtensionsViewlet, VIEWLET_ID, IExtension, IExtensionDependencies, ExtensionContainers } from 'vs/workbench/parts/extensions/common/extensions'; -import { RatingsWidget, InstallCountWidget } from 'vs/workbench/parts/extensions/browser/extensionsWidgets'; +import { RatingsWidget, InstallCountWidget } from 'vs/workbench/parts/extensions/electron-browser/extensionsWidgets'; import { EditorOptions } from 'vs/workbench/common/editor'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; -import { CombinedInstallAction, UpdateAction, ExtensionEditorDropDownAction, ReloadAction, MaliciousStatusLabelAction, DisabledStatusLabelAction, IgnoreExtensionRecommendationAction, UndoIgnoreExtensionRecommendationAction, EnableDropDownAction, DisableDropDownAction } from 'vs/workbench/parts/extensions/electron-browser/extensionsActions'; +import { CombinedInstallAction, UpdateAction, ExtensionEditorDropDownAction, ReloadAction, MaliciousStatusLabelAction, IgnoreExtensionRecommendationAction, UndoIgnoreExtensionRecommendationAction, EnableDropDownAction, DisableDropDownAction } from 'vs/workbench/parts/extensions/electron-browser/extensionsActions'; import { WebviewElement } from 'vs/workbench/parts/webview/electron-browser/webviewElement'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; @@ -357,18 +357,17 @@ export class ExtensionEditor extends BaseEditor { const actions = [ reloadAction, this.instantiationService.createInstance(UpdateAction), - this.instantiationService.createInstance(EnableDropDownAction, runningExtensions), + this.instantiationService.createInstance(EnableDropDownAction), this.instantiationService.createInstance(DisableDropDownAction, runningExtensions), this.instantiationService.createInstance(CombinedInstallAction), this.instantiationService.createInstance(MaliciousStatusLabelAction, true), - this.instantiationService.createInstance(DisabledStatusLabelAction, runningExtensions) ]; const extensionContainers: ExtensionContainers = this.instantiationService.createInstance(ExtensionContainers, [...actions, ...widgets]); extensionContainers.extension = extension; this.extensionActionBar.clear(); this.extensionActionBar.push(actions, { icon: true, label: true }); - this.transientDisposables.push(...[...actions, extensionContainers]); + this.transientDisposables.push(...[...actions, ...widgets, extensionContainers]); this.setSubText(extension, reloadAction); this.content.innerHTML = ''; // Clear content before setting navbar actions. diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts index b26c648600a..29e89c2f2a8 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts @@ -16,9 +16,9 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; import { IExtension, ExtensionState, IExtensionsWorkbenchService, VIEWLET_ID, IExtensionsViewlet, AutoUpdateConfigurationKey, IExtensionContainer } from 'vs/workbench/parts/extensions/common/extensions'; import { ExtensionsConfigurationInitialContent } from 'vs/workbench/parts/extensions/common/extensionsFileTemplate'; -import { IExtensionEnablementService, IExtensionTipsService, EnablementState, ExtensionsLabel, IExtensionRecommendation, IGalleryExtension, IExtensionsConfigContent, IExtensionManagementServerService, IExtensionGalleryService, INSTALL_ERROR_MALICIOUS, INSTALL_ERROR_INCOMPATIBLE, IGalleryExtensionVersion } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionEnablementService, IExtensionTipsService, EnablementState, ExtensionsLabel, IExtensionRecommendation, IGalleryExtension, IExtensionsConfigContent, IExtensionGalleryService, INSTALL_ERROR_MALICIOUS, INSTALL_ERROR_INCOMPATIBLE, IGalleryExtensionVersion } from 'vs/platform/extensionManagement/common/extensionManagement'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; -import { isUIExtension, ExtensionIdentifier, ExtensionType } from 'vs/platform/extensions/common/extensions'; +import { ExtensionType } from 'vs/platform/extensions/common/extensions'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ShowViewletAction } from 'vs/workbench/browser/viewlet'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; @@ -54,9 +54,6 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { clipboard } from 'electron'; import { IPartService } from 'vs/workbench/services/part/common/partService'; import { alert } from 'vs/base/browser/ui/aria/aria'; -import { ILabelService } from 'vs/platform/label/common/label'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { coalesce } from 'vs/base/common/arrays'; const promptDownloadManually = (extension: IGalleryExtension | undefined, message: string, error: Error, instantiationService: IInstantiationService, notificationService: INotificationService, openerService: IOpenerService) => { @@ -515,7 +512,7 @@ export class ManageExtensionAction extends ExtensionDropDownAction { const groups: ExtensionAction[][] = []; groups.push([ this.instantiationService.createInstance(EnableGloballyAction), - this.instantiationService.createInstance(CombinedEnableForWorkspaceAction, runningExtensions) + this.instantiationService.createInstance(EnableForWorkspaceAction) ]); groups.push([ this.instantiationService.createInstance(DisableGloballyAction, runningExtensions), @@ -630,50 +627,6 @@ export class ExtensionInfoAction extends ExtensionAction { } } -export class CombinedEnableForWorkspaceAction extends ExtensionAction { - - static readonly ID = 'extensions.enableForWorkspace'; - static LABEL = localize('enableForWorkspaceAction', "Enable (Workspace)"); - - private enableForWorkspaceAction: EnableForWorkspaceAction; - private installInRemoteServerAction: InstallInRemoteServerAction; - - constructor(readonly runningExtensions: IExtensionDescription[], - @IInstantiationService instantiationService: IInstantiationService - ) { - super(CombinedEnableForWorkspaceAction.ID, CombinedEnableForWorkspaceAction.LABEL); - - this.enableForWorkspaceAction = instantiationService.createInstance(EnableForWorkspaceAction); - this.installInRemoteServerAction = instantiationService.createInstance(InstallInRemoteServerAction, runningExtensions); - - this.update(); - } - - update(): void { - this.enableForWorkspaceAction.extension = this.extension; - this.installInRemoteServerAction.extension = this.extension; - this.enableForWorkspaceAction.update(); - this.installInRemoteServerAction.update(); - this.enabled = this.installInRemoteServerAction.enabled || this.enableForWorkspaceAction.enabled; - } - - run(): Promise { - if (this.installInRemoteServerAction.enabled) { - return this.installInRemoteServerAction.run(); - } - if (this.enableForWorkspaceAction.enabled) { - return this.enableForWorkspaceAction.run(); - } - return Promise.resolve(); - } - - dispose(): void { - super.dispose(); - this.enableForWorkspaceAction.dispose(); - this.installInRemoteServerAction.dispose(); - } -} - export class EnableForWorkspaceAction extends ExtensionAction { static readonly ID = 'extensions.enableForWorkspace'; @@ -699,65 +652,6 @@ export class EnableForWorkspaceAction extends ExtensionAction { } } -export class InstallInRemoteServerAction extends ExtensionAction { - - static readonly ID = 'extensions.installInRemoteServerAction'; - static LABEL = localize('enableForWorkspaceAction', "Enable (Workspace)"); - - constructor(readonly runningExtensions: IExtensionDescription[], - @IExtensionsWorkbenchService extensionWorkbenchService: IExtensionsWorkbenchService, - @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, - @IConfigurationService private readonly configurationService: IConfigurationService, - @IStorageService private readonly storageService: IStorageService, - @ILabelService private readonly labelService: ILabelService, - @IDialogService private readonly dialogService: IDialogService, - @IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService, - @IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService - ) { - super(InstallInRemoteServerAction.ID, InstallInRemoteServerAction.LABEL); - this.update(); - } - - update(): void { - this.enabled = false; - if (this.extensionManagementServerService.remoteExtensionManagementServer - && this.extension && this.extension.locals && this.extension.locals.length > 0 - && !isUIExtension(this.extension.locals[0].manifest, this.configurationService) - && this.extension.state === ExtensionState.Installed) { - const installedInRemoteServer = this.extension.locals.some(local => { - const server = this.extensionManagementServerService.getExtensionManagementServer(local.location); - return !!server && server.authority === this.extensionManagementServerService.remoteExtensionManagementServer!.authority; - }); - if (!installedInRemoteServer) { - this.enabled = !this.runningExtensions.some(e => areSameExtensions({ id: e.identifier.value }, this.extension.identifier)); - } - } - } - - async run(): Promise { - if (!this.enabled) { - return Promise.resolve(); - } - if (this.storageService.getBoolean('askToInstallRemoteServerExtension', StorageScope.GLOBAL, true)) { - const message = localize('install extension', "Enabling the '{0}' extension will also install it in {1}. Would you like to continue?", this.extension.displayName, this.labelService.getHostLabel() || this.extensionManagementServerService.remoteExtensionManagementServer!.authority); - const response = await this.dialogService.confirm({ type: 'info', message, checkbox: { label: localize('do not ask me again', "Do not ask me again") } }); - if (!response || !response.confirmed) { - return Promise.resolve(); - } - if (response.checkboxChecked) { - this.storageService.store('askToInstallRemoteServerExtension', false, StorageScope.GLOBAL); - } - } - const galleryExtension = this.extension.gallery ? this.extension.gallery : await this.extensionGalleryService.getExtension(this.extension.local!.identifier); - if (galleryExtension) { - return this.extensionManagementServerService.remoteExtensionManagementServer!.extensionManagementService.installFromGallery(galleryExtension); - } else { - const zipLocation = await this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.zip(this.extension.local!); - return this.extensionManagementServerService.remoteExtensionManagementServer!.extensionManagementService.unzip(zipLocation, this.extension.type!); - } - } -} - export class EnableGloballyAction extends ExtensionAction { static readonly ID = 'extensions.enableGlobally'; @@ -765,9 +659,7 @@ export class EnableGloballyAction extends ExtensionAction { constructor( @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, - @IExtensionEnablementService private readonly extensionEnablementService: IExtensionEnablementService, - @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, - @IConfigurationService private readonly configurationService: IConfigurationService + @IExtensionEnablementService private readonly extensionEnablementService: IExtensionEnablementService ) { super(EnableGloballyAction.ID, EnableGloballyAction.LABEL); this.update(); @@ -775,15 +667,7 @@ export class EnableGloballyAction extends ExtensionAction { update(): void { this.enabled = false; - if (this.extension && this.extension.locals && this.extension.local) { - if (!isUIExtension(this.extension.local.manifest, this.configurationService) && this.extensionManagementServerService.remoteExtensionManagementServer) { - if (!this.extension.locals.some(local => { - const server = this.extensionManagementServerService.getExtensionManagementServer(local.location); - return !!server && server.authority === this.extensionManagementServerService.remoteExtensionManagementServer!.authority; - })) { - return; - } - } + if (this.extension && this.extension.local) { this.enabled = this.extension.state === ExtensionState.Installed && this.extension.enablementState === EnablementState.Disabled && this.extensionEnablementService.canChangeEnablement(this.extension.local); } } @@ -891,12 +775,11 @@ export abstract class ExtensionEditorDropDownAction extends ExtensionDropDownAct export class EnableDropDownAction extends ExtensionEditorDropDownAction { constructor( - runningExtensions: IExtensionDescription[], @IInstantiationService instantiationService: IInstantiationService ) { super('extensions.enable', localize('enableAction', "Enable"), [ instantiationService.createInstance(EnableGloballyAction), - instantiationService.createInstance(CombinedEnableForWorkspaceAction, runningExtensions) + instantiationService.createInstance(EnableForWorkspaceAction) ], instantiationService); } } @@ -1080,9 +963,7 @@ export class ReloadAction extends ExtensionAction { @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, @IWindowService private readonly windowService: IWindowService, @IExtensionService private readonly extensionService: IExtensionService, - @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, - @IExtensionEnablementService private readonly extensionEnablementService: IExtensionEnablementService, - @IConfigurationService private readonly configurationService: IConfigurationService + @IExtensionEnablementService private readonly extensionEnablementService: IExtensionEnablementService ) { super('extensions.reload', localize('reloadAction', "Reload"), ReloadAction.DisabledClass, false); this.throttler = new Throttler(); @@ -1136,33 +1017,14 @@ export class ReloadAction extends ExtensionAction { return; } } else { - const uiExtension = isUIExtension(installed.local.manifest, this.configurationService); if (!isDisabled) { - let enableReload = true; - if (this.extensionManagementServerService.remoteExtensionManagementServer && installed.locals) { - if (uiExtension) { - // Only UI extension from local server requires reload if it is not running on the server - enableReload = installed.locals.some(local => { - const server = this.extensionManagementServerService.getExtensionManagementServer(local.location); - return !!server && server.authority === this.extensionManagementServerService.localExtensionManagementServer.authority; - }); - } else { - enableReload = installed.locals.some(local => { - const server = this.extensionManagementServerService.getExtensionManagementServer(local.location); - return !!server && server.authority === this.extensionManagementServerService.remoteExtensionManagementServer!.authority; - }); - } - } - - if (enableReload === true) { - this.enabled = true; - if (!isEnabled) { - this.tooltip = localize('postInstallTooltip', "Please reload Visual Studio Code to complete the installation of this extension."); - } else { - this.tooltip = localize('postEnableTooltip', "Please reload Visual Studio Code to complete the enabling of this extension."); - } - return; + this.enabled = true; + if (!isEnabled) { + this.tooltip = localize('postInstallTooltip', "Please reload Visual Studio Code to complete the installation of this extension."); + } else { + this.tooltip = localize('postEnableTooltip', "Please reload Visual Studio Code to complete the enabling of this extension."); } + return; } } return; @@ -1661,44 +1523,6 @@ export class ChangeSortAction extends Action { } } -export class ChangeGroupAction extends Action { - - private query: Query; - private disposables: IDisposable[] = []; - - constructor( - id: string, - label: string, - onSearchChange: Event, - private groupBy: string, - @IViewletService private readonly viewletService: IViewletService - ) { - super(id, label, undefined, true); - - if (groupBy === undefined) { - throw new Error('bad arguments'); - } - - this.query = Query.parse(''); - onSearchChange(this.onSearchChange, this, this.disposables); - this.onSearchChange(''); - } - - private onSearchChange(value: string): void { - const query = Query.parse(value); - this.query = new Query(query.value, query.sortBy, this.groupBy || query.groupBy); - } - - run(): Promise { - return this.viewletService.openViewlet(VIEWLET_ID, true) - .then(viewlet => viewlet as IExtensionsViewlet) - .then(viewlet => { - viewlet.search(this.query.toString()); - viewlet.focus(); - }); - } -} - export class ConfigureRecommendedExtensionsCommandsContributor extends Disposable implements IWorkbenchContribution { private workspaceContextKey = new RawContextKey('workspaceRecommendations', true); @@ -2235,43 +2059,6 @@ export class MaliciousStatusLabelAction extends ExtensionAction { } } -export class DisabledStatusLabelAction extends ExtensionAction { - - private static readonly Class = 'disable-status'; - - constructor( - private runningExtensions: IExtensionDescription[], - @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, - @IConfigurationService private readonly configurationService: IConfigurationService, - @ILabelService private readonly labelService: ILabelService - ) { - super('extensions.install', localize('disabled', "Disabled"), `${DisabledStatusLabelAction.Class} hide`, false); - this.update(); - } - - update(): void { - this.class = `${DisabledStatusLabelAction.Class} hide`; - this.tooltip = ''; - if (this.extension && this.extension.local && !this.extension.isMalicious && !this.runningExtensions.some(e => ExtensionIdentifier.equals(e.identifier, this.extension.identifier.id))) { - if (this.extensionManagementServerService.remoteExtensionManagementServer && !isUIExtension(this.extension.local.manifest, this.configurationService) && this.extension.locals) { - const installedInRemoteServer = this.extension.locals.some(local => { - const server = this.extensionManagementServerService.getExtensionManagementServer(local.location); - return !!server && server.authority === this.extensionManagementServerService.remoteExtensionManagementServer!.authority; - }); - if (!installedInRemoteServer) { - this.class = `${DisabledStatusLabelAction.Class}`; - this.label = localize('disabled NonUI Extension', "Disabled for this Workspace because it is not installed in {0}.", this.labelService.getHostLabel() || this.extensionManagementServerService.remoteExtensionManagementServer.authority); - return; - } - } - } - } - - run(): Promise { - return Promise.resolve(null); - } -} - export class DisableAllAction extends Action { static readonly ID = 'workbench.extensions.action.disableAll'; diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts index e2323ca0895..fb69a556f6d 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { localize } from 'vs/nls'; import { append, $, addClass, removeClass, toggleClass } from 'vs/base/browser/dom'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { Action } from 'vs/base/common/actions'; @@ -14,12 +13,11 @@ import { IPagedRenderer } from 'vs/base/browser/ui/list/listPaging'; import { Event } from 'vs/base/common/event'; import { domEvent } from 'vs/base/browser/event'; import { IExtension, IExtensionsWorkbenchService, ExtensionContainers } from 'vs/workbench/parts/extensions/common/extensions'; -import { InstallAction, UpdateAction, ManageExtensionAction, ReloadAction, extensionButtonProminentBackground, extensionButtonProminentForeground, MaliciousStatusLabelAction, ExtensionActionItem } from 'vs/workbench/parts/extensions/electron-browser/extensionsActions'; +import { InstallAction, UpdateAction, ManageExtensionAction, ReloadAction, MaliciousStatusLabelAction, ExtensionActionItem } from 'vs/workbench/parts/extensions/electron-browser/extensionsActions'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; -import { Label, RatingsWidget, InstallCountWidget } from 'vs/workbench/parts/extensions/browser/extensionsWidgets'; +import { Label, RatingsWidget, InstallCountWidget, RecommendationWidget, RemoteBadgeWidget } from 'vs/workbench/parts/extensions/electron-browser/extensionsWidgets'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { IExtensionTipsService, IExtensionManagementServerService } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IExtensionManagementServerService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { INotificationService } from 'vs/platform/notification/common/notification'; export interface IExtensionsViewState { @@ -57,27 +55,17 @@ export class Renderer implements IPagedRenderer { @INotificationService private readonly notificationService: INotificationService, @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, @IExtensionService private readonly extensionService: IExtensionService, - @IExtensionTipsService private readonly extensionTipsService: IExtensionTipsService, - @IThemeService private readonly themeService: IThemeService, @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService ) { } get templateId() { return 'extension'; } renderTemplate(root: HTMLElement): ITemplateData { - const bookmark = append(root, $('span.bookmark')); - append(bookmark, $('span.octicon.octicon-star')); - const applyBookmarkStyle = (theme) => { - const bgColor = theme.getColor(extensionButtonProminentBackground); - const fgColor = theme.getColor(extensionButtonProminentForeground); - bookmark.style.borderTopColor = bgColor ? bgColor.toString() : 'transparent'; - bookmark.style.color = fgColor ? fgColor.toString() : 'white'; - }; - applyBookmarkStyle(this.themeService.getTheme()); - const bookmarkStyler = this.themeService.onThemeChange(applyBookmarkStyle.bind(this)); - + const recommendationWidget = this.instantiationService.createInstance(RecommendationWidget, root); const element = append(root, $('.extension')); - const icon = append(element, $('img.icon')); + const iconContainer = append(element, $('.icon-container')); + const icon = append(iconContainer, $('img.icon')); + const badgeWidget = this.instantiationService.createInstance(RemoteBadgeWidget, iconContainer); const details = append(element, $('.details')); const headerContainer = append(details, $('.header-container')); const header = append(headerContainer, $('.header')); @@ -100,6 +88,8 @@ export class Renderer implements IPagedRenderer { actionbar.onDidRun(({ error }) => error && this.notificationService.error(error)); const widgets = [ + recommendationWidget, + badgeWidget, this.instantiationService.createInstance(Label, version, (e: IExtension) => e.version), this.instantiationService.createInstance(InstallCountWidget, installCount, true), this.instantiationService.createInstance(RatingsWidget, ratings, true) @@ -114,7 +104,7 @@ export class Renderer implements IPagedRenderer { const extensionContainers: ExtensionContainers = this.instantiationService.createInstance(ExtensionContainers, [...actions, ...widgets]); actionbar.push(actions, actionOptions); - const disposables = [...actions, ...widgets, actionbar, bookmarkStyler, extensionContainers]; + const disposables = [...actions, ...widgets, actionbar, extensionContainers]; return { root, element, icon, name, installCount, ratings, author, description, disposables, actionbar, @@ -178,13 +168,6 @@ export class Renderer implements IPagedRenderer { data.icon.style.visibility = 'inherit'; } - this.updateRecommendationStatus(extension, data); - data.extensionDisposables.push(this.extensionTipsService.onRecommendationChange(change => { - if (areSameExtensions({ id: change.extensionId }, extension.identifier)) { - this.updateRecommendationStatus(extension, data); - } - })); - data.name.textContent = extension.displayName; data.author.textContent = extension.publisherDisplayName; data.description.textContent = extension.description; @@ -209,24 +192,6 @@ export class Renderer implements IPagedRenderer { }, this, data.extensionDisposables); } - private updateRecommendationStatus(extension: IExtension, data: ITemplateData) { - const extRecommendations = this.extensionTipsService.getAllRecommendationsWithReason(); - let ariaLabel = extension.displayName + '. '; - - if (!extRecommendations[extension.identifier.id.toLowerCase()]) { - removeClass(data.root, 'recommended'); - data.root.title = ''; - } else { - addClass(data.root, 'recommended'); - ariaLabel += extRecommendations[extension.identifier.id.toLowerCase()].reasonText + ' '; - data.root.title = extRecommendations[extension.identifier.id.toLowerCase()].reasonText; - } - - ariaLabel += localize('viewExtensionDetailsAria', "Press enter for extension details."); - data.root.setAttribute('aria-label', ariaLabel); - - } - disposeTemplate(data: ITemplateData): void { data.disposables = dispose(data.disposables); } diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts index b90011dc02d..352820e161a 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts @@ -22,7 +22,7 @@ import { IExtensionsWorkbenchService, IExtensionsViewlet, VIEWLET_ID, ExtensionS import { ShowEnabledExtensionsAction, ShowInstalledExtensionsAction, ShowRecommendedExtensionsAction, ShowPopularExtensionsAction, ShowDisabledExtensionsAction, ShowOutdatedExtensionsAction, ClearExtensionsInputAction, ChangeSortAction, UpdateAllAction, CheckForUpdatesAction, DisableAllAction, EnableAllAction, - EnableAutoUpdateAction, DisableAutoUpdateAction, ShowBuiltInExtensionsAction, InstallVSIXAction, ChangeGroupAction + EnableAutoUpdateAction, DisableAutoUpdateAction, ShowBuiltInExtensionsAction, InstallVSIXAction } from 'vs/workbench/parts/extensions/electron-browser/extensionsActions'; import { IExtensionManagementService, IExtensionManagementServerService, IExtensionManagementServer, EnablementState } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ExtensionsInput } from 'vs/workbench/parts/extensions/common/extensionsInput'; @@ -35,7 +35,7 @@ import { IActivityService, ProgressBadge, NumberBadge } from 'vs/workbench/servi import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ViewsRegistry, IViewDescriptor } from 'vs/workbench/common/views'; -import { ViewContainerViewlet, IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; +import { ViewContainerViewlet } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IContextKeyService, ContextKeyExpr, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey'; @@ -47,9 +47,6 @@ import { IWindowService } from 'vs/platform/windows/common/windows'; import { IPartService } from 'vs/workbench/services/part/common/partService'; import { IAddedViewDescriptorRef } from 'vs/workbench/browser/parts/views/views'; import { ViewletPanel } from 'vs/workbench/browser/parts/views/panelViewlet'; -import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { ExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/node/extensionsWorkbenchService'; -import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { Query } from 'vs/workbench/parts/extensions/common/extensionQuery'; import { SuggestEnabledInput, attachSuggestEnabledInputBoxStyler } from 'vs/workbench/parts/codeEditor/electron-browser/suggestEnabledInput'; import { alert } from 'vs/base/browser/ui/aria/aria'; @@ -279,7 +276,6 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio private extensionsBox: HTMLElement; private primaryActions: IAction[]; private secondaryActions: IAction[]; - private groupByServerAction: IAction; private disposables: IDisposable[] = []; constructor( @@ -297,8 +293,7 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio @IWorkspaceContextService contextService: IWorkspaceContextService, @IContextKeyService contextKeyService: IContextKeyService, @IContextMenuService contextMenuService: IContextMenuService, - @IExtensionService extensionService: IExtensionService, - @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService + @IExtensionService extensionService: IExtensionService ) { super(VIEWLET_ID, `${VIEWLET_ID}.state`, true, configurationService, partService, telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService); @@ -395,12 +390,6 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio getSecondaryActions(): IAction[] { if (!this.secondaryActions) { - if (!this.groupByServerAction) { - this.groupByServerAction = this.instantiationService.createInstance(ChangeGroupAction, 'extensions.group.servers', localize('group by servers', "Group By: Server"), this.onSearchChange, 'server'); - this.disposables.push(this.onSearchChange(value => { - this.groupByServerAction.enabled = !value || ExtensionsListView.isInstalledExtensionsQuery(value) || ExtensionsListView.isBuiltInExtensionsQuery(value); - })); - } this.secondaryActions = [ this.instantiationService.createInstance(ShowInstalledExtensionsAction, ShowInstalledExtensionsAction.ID, ShowInstalledExtensionsAction.LABEL), this.instantiationService.createInstance(ShowOutdatedExtensionsAction, ShowOutdatedExtensionsAction.ID, ShowOutdatedExtensionsAction.LABEL), @@ -414,7 +403,6 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio this.instantiationService.createInstance(ChangeSortAction, 'extensions.sort.rating', localize('sort by rating', "Sort By: Rating"), this.onSearchChange, 'rating'), this.instantiationService.createInstance(ChangeSortAction, 'extensions.sort.name', localize('sort by name', "Sort By: Name"), this.onSearchChange, 'name'), new Separator(), - ...(this.extensionManagementServerService.remoteExtensionManagementServer ? [this.groupByServerAction, new Separator()] : []), this.instantiationService.createInstance(CheckForUpdatesAction, CheckForUpdatesAction.ID, CheckForUpdatesAction.LABEL), ...(this.configurationService.getValue(AutoUpdateConfigurationKey) ? [this.instantiationService.createInstance(DisableAutoUpdateAction, DisableAutoUpdateAction.ID, DisableAutoUpdateAction.LABEL)] : [this.instantiationService.createInstance(UpdateAllAction, UpdateAllAction.ID, UpdateAllAction.LABEL), this.instantiationService.createInstance(EnableAutoUpdateAction, EnableAutoUpdateAction.ID, EnableAutoUpdateAction.LABEL)]), this.instantiationService.createInstance(InstallVSIXAction, InstallVSIXAction.ID, InstallVSIXAction.LABEL), @@ -489,21 +477,6 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio } } - protected createView(viewDescriptor: IViewDescriptor, options: IViewletViewOptions): ViewletPanel { - if (this.extensionManagementServerService.remoteExtensionManagementServer) { - const extensionManagementServer = viewDescriptor.id === `server.extensionsList.${this.extensionManagementServerService.localExtensionManagementServer.authority}` ? this.extensionManagementServerService.localExtensionManagementServer - : viewDescriptor.id === `server.extensionsList.${this.extensionManagementServerService.remoteExtensionManagementServer.authority}` ? this.extensionManagementServerService.remoteExtensionManagementServer : null; - if (extensionManagementServer) { - const servicesCollection: ServiceCollection = new ServiceCollection(); - servicesCollection.set(IExtensionManagementService, extensionManagementServer.extensionManagementService); - servicesCollection.set(IExtensionsWorkbenchService, new SyncDescriptor(ExtensionsWorkbenchService)); - const instantiationService = this.instantiationService.createChild(servicesCollection); - return instantiationService.createInstance(viewDescriptor.ctor, options, [extensionManagementServer]) as ViewletPanel; - } - } - return this.instantiationService.createInstance(viewDescriptor.ctor, options) as ViewletPanel; - } - private count(): number { return this.panels.reduce((count, view) => (view).count() + count, 0); } diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsWidgets.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsWidgets.ts new file mode 100644 index 00000000000..5c8b2439230 --- /dev/null +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsWidgets.ts @@ -0,0 +1,244 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'vs/css!./media/extensionsWidgets'; +import { Disposable, IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; +import { IExtension, IExtensionsWorkbenchService, IExtensionContainer } from '../common/extensions'; +import { append, $, addClass } from 'vs/base/browser/dom'; +import * as platform from 'vs/base/common/platform'; +import { localize } from 'vs/nls'; +import { IExtensionManagementServerService, IExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { ILabelService } from 'vs/platform/label/common/label'; +import { extensionButtonProminentBackground, extensionButtonProminentForeground } from 'vs/workbench/parts/extensions/electron-browser/extensionsActions'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { STATUS_BAR_HOST_NAME_BACKGROUND } from 'vs/workbench/parts/remote/electron-browser/remote.contribution'; + +export abstract class ExtensionWidget extends Disposable implements IExtensionContainer { + private _extension: IExtension; + get extension(): IExtension { return this._extension; } + set extension(extension: IExtension) { this._extension = extension; this.update(); } + update(): void { this.render(); } + abstract render(): void; +} + +export class Label extends ExtensionWidget { + + constructor( + private element: HTMLElement, + private fn: (extension: IExtension) => string, + @IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService + ) { + super(); + this.render(); + } + + render(): void { + this.element.textContent = this.extension ? this.fn(this.extension) : ''; + } +} + +export class InstallCountWidget extends ExtensionWidget { + + constructor( + private container: HTMLElement, + private small: boolean, + @IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService + ) { + super(); + addClass(container, 'extension-install-count'); + this.render(); + } + + render(): void { + this.container.innerHTML = ''; + + if (!this.extension) { + return; + } + + const installCount = this.extension.installCount; + + if (installCount === undefined) { + return; + } + + let installLabel: string; + + if (this.small) { + if (installCount > 1000000) { + installLabel = `${Math.floor(installCount / 100000) / 10}M`; + } else if (installCount > 1000) { + installLabel = `${Math.floor(installCount / 1000)}K`; + } else { + installLabel = String(installCount); + } + } + else { + installLabel = installCount.toLocaleString(platform.locale); + } + + append(this.container, $('span.octicon.octicon-cloud-download')); + const count = append(this.container, $('span.count')); + count.textContent = installLabel; + } +} + +export class RatingsWidget extends ExtensionWidget { + + constructor( + private container: HTMLElement, + private small: boolean + ) { + super(); + addClass(container, 'extension-ratings'); + + if (this.small) { + addClass(container, 'small'); + } + + this.render(); + } + + render(): void { + this.container.innerHTML = ''; + + if (!this.extension) { + return; + } + + if (this.extension.rating === undefined) { + return; + } + + if (this.small && !this.extension.ratingCount) { + return; + } + + const rating = Math.round(this.extension.rating * 2) / 2; + + if (this.small) { + append(this.container, $('span.full.star')); + + const count = append(this.container, $('span.count')); + count.textContent = String(rating); + } else { + for (let i = 1; i <= 5; i++) { + if (rating >= i) { + append(this.container, $('span.full.star')); + } else if (rating >= i - 0.5) { + append(this.container, $('span.half.star')); + } else { + append(this.container, $('span.empty.star')); + } + } + } + this.container.title = this.extension.ratingCount > 1 ? localize('ratedByUsers', "Rated by {0} users", this.extension.ratingCount) : localize('ratedBySingleUser', "Rated by 1 user"); + } +} + +export class RecommendationWidget extends ExtensionWidget { + + private element: HTMLElement; + private disposables: IDisposable[] = []; + + constructor( + private parent: HTMLElement, + @IThemeService private readonly themeService: IThemeService, + @IExtensionTipsService private readonly extensionTipsService: IExtensionTipsService + ) { + super(); + this.render(); + this._register(toDisposable(() => this.clear())); + } + + private clear(): void { + this.parent.title = ''; + this.parent.setAttribute('aria-label', this.extension ? localize('viewExtensionDetailsAria', "{0}. Press enter for extension details.", this.extension.displayName) : ''); + if (this.element) { + this.parent.removeChild(this.element); + } + this.element = null; + this.disposables = dispose(this.disposables); + } + + render(): void { + this.clear(); + if (!this.extension) { + return; + } + const updateRecommendationMarker = () => { + this.clear(); + const extRecommendations = this.extensionTipsService.getAllRecommendationsWithReason(); + if (extRecommendations[this.extension.identifier.id.toLowerCase()]) { + this.element = append(this.parent, $('div.bookmark')); + const recommendation = append(this.element, $('.recommendation')); + append(recommendation, $('span.octicon.octicon-star')); + const applyBookmarkStyle = (theme) => { + const bgColor = theme.getColor(extensionButtonProminentBackground); + const fgColor = theme.getColor(extensionButtonProminentForeground); + recommendation.style.borderTopColor = bgColor ? bgColor.toString() : 'transparent'; + recommendation.style.color = fgColor ? fgColor.toString() : 'white'; + }; + applyBookmarkStyle(this.themeService.getTheme()); + this.themeService.onThemeChange(applyBookmarkStyle, this, this.disposables); + this.parent.title = extRecommendations[this.extension.identifier.id.toLowerCase()].reasonText; + this.parent.setAttribute('aria-label', localize('viewRecommendedExtensionDetailsAria', "{0}. {1} Press enter for extension details.", this.extension.displayName, extRecommendations[this.extension.identifier.id.toLowerCase()].reasonText)); + } + }; + updateRecommendationMarker(); + this.extensionTipsService.onRecommendationChange(() => updateRecommendationMarker(), this, this.disposables); + } + +} + + +export class RemoteBadgeWidget extends ExtensionWidget { + + private element: HTMLElement | null; + private disposables: IDisposable[] = []; + + constructor( + private parent: HTMLElement, + @ILabelService private readonly labelService: ILabelService, + @IThemeService private readonly themeService: IThemeService, + @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, + ) { + super(); + this.render(); + this._register(toDisposable(() => this.clear())); + } + + private clear(): void { + if (this.element) { + this.parent.removeChild(this.element); + } + this.element = null; + this.disposables = dispose(this.disposables); + } + + render(): void { + this.clear(); + if (!this.extension || !this.extension.local) { + return; + } + const server = this.extensionManagementServerService.getExtensionManagementServer(this.extension.local.location); + if (server === this.extensionManagementServerService.remoteExtensionManagementServer) { + this.element = append(this.parent, $('div.extension-remote-badge')); + append(this.element, $('span.octicon.octicon-file-symlink-directory')); + + const applyBadgeStyle = (theme) => { + const bgColor = this.themeService.getTheme().getColor(STATUS_BAR_HOST_NAME_BACKGROUND); + this.element.style.backgroundColor = bgColor ? bgColor.toString() : ''; + }; + applyBadgeStyle(this.themeService.getTheme()); + this.themeService.onThemeChange(applyBadgeStyle, this, this.disposables); + + const updateTitle = () => this.element.title = this.labelService.getHostLabel(); + this.labelService.onDidChangeFormatters(() => updateTitle(), this, this.disposables); + updateTitle(); + } + } + +} diff --git a/src/vs/workbench/parts/extensions/browser/media/EmptyStar.svg b/src/vs/workbench/parts/extensions/electron-browser/media/EmptyStar.svg similarity index 100% rename from src/vs/workbench/parts/extensions/browser/media/EmptyStar.svg rename to src/vs/workbench/parts/extensions/electron-browser/media/EmptyStar.svg diff --git a/src/vs/workbench/parts/extensions/browser/media/FullStarLight.svg b/src/vs/workbench/parts/extensions/electron-browser/media/FullStarLight.svg similarity index 100% rename from src/vs/workbench/parts/extensions/browser/media/FullStarLight.svg rename to src/vs/workbench/parts/extensions/electron-browser/media/FullStarLight.svg diff --git a/src/vs/workbench/parts/extensions/browser/media/HalfStarLight.svg b/src/vs/workbench/parts/extensions/electron-browser/media/HalfStarLight.svg similarity index 100% rename from src/vs/workbench/parts/extensions/browser/media/HalfStarLight.svg rename to src/vs/workbench/parts/extensions/electron-browser/media/HalfStarLight.svg diff --git a/src/vs/workbench/parts/extensions/electron-browser/media/extensionsViewlet.css b/src/vs/workbench/parts/extensions/electron-browser/media/extensionsViewlet.css index e8e11338689..648b9c762a8 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/media/extensionsViewlet.css +++ b/src/vs/workbench/parts/extensions/electron-browser/media/extensionsViewlet.css @@ -57,19 +57,18 @@ } .extensions-viewlet > .extensions .monaco-list-row > .bookmark { - display: none; -} - -.extensions-viewlet > .extensions .monaco-list-row.recommended > .bookmark { display: inline-block; height: 20px; width: 20px; +} + +.extensions-viewlet > .extensions .monaco-list-row > .bookmark > .recommendation { border-right: 20px solid transparent; border-top: 20px solid; box-sizing: border-box; } -.extensions-viewlet > .extensions .monaco-list-row > .bookmark > .octicon { +.extensions-viewlet > .extensions .monaco-list-row > .bookmark > .recommendation > .octicon { position: absolute; top: 1px; left: 1px; @@ -91,7 +90,11 @@ background: url('loading.svg') center center no-repeat; } -.extensions-viewlet > .extensions .extension > .icon { +.extensions-viewlet > .extensions .monaco-list-row > .extension > .icon-container { + position: relative; +} + +.extensions-viewlet > .extensions .extension > .icon-container > .icon { width: 42px; height: 42px; padding: 10px 14px 10px 0; @@ -99,6 +102,12 @@ object-fit: contain; } +.extensions-viewlet > .extensions .monaco-list-row > .extension > .icon-container > .extension-remote-badge { + position: absolute; + right: 5px; + bottom: 5px; +} + .extensions-viewlet.narrow > .extensions .extension > .icon { display: none; } @@ -204,8 +213,8 @@ .vs .extensions-viewlet > .extensions .monaco-list-row.disabled > .bookmark, .vs-dark .extensions-viewlet > .extensions .monaco-list-row.disabled > .bookmark, -.vs .extensions-viewlet > .extensions .monaco-list-row.disabled > .extension > .icon, -.vs-dark .extensions-viewlet > .extensions .monaco-list-row.disabled > .extension > .icon, +.vs .extensions-viewlet > .extensions .monaco-list-row.disabled > .extension > .icon-container > .icon, +.vs-dark .extensions-viewlet > .extensions .monaco-list-row.disabled > .extension > .icon-container > .icon, .vs .extensions-viewlet > .extensions .monaco-list-row.disabled > .extension > .details > .header-container, .vs-dark .extensions-viewlet > .extensions .monaco-list-row.disabled > .extension > .details > .header-container, .vs .extensions-viewlet > .extensions .monaco-list-row.disabled > .extension > .details > .description, diff --git a/src/vs/workbench/parts/extensions/browser/media/extensionsWidgets.css b/src/vs/workbench/parts/extensions/electron-browser/media/extensionsWidgets.css similarity index 89% rename from src/vs/workbench/parts/extensions/browser/media/extensionsWidgets.css rename to src/vs/workbench/parts/extensions/electron-browser/media/extensionsWidgets.css index 7ddd1d4c880..7146eb87f31 100644 --- a/src/vs/workbench/parts/extensions/browser/media/extensionsWidgets.css +++ b/src/vs/workbench/parts/extensions/electron-browser/media/extensionsWidgets.css @@ -46,4 +46,12 @@ .extension-ratings.small > .count { margin-left: 2px; +} + +.extension-remote-badge { + width: 22px; + height: 22px; + line-height: 22px; + border-radius: 20px; + text-align: center; } \ No newline at end of file diff --git a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts index d3b7b2723d3..22c75922e8b 100644 --- a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts +++ b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts @@ -30,14 +30,11 @@ import product from 'vs/platform/node/product'; import { ILogService } from 'vs/platform/log/common/log'; import { IProgressService2, ProgressLocation } from 'vs/platform/progress/common/progress'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { groupBy } from 'vs/base/common/collections'; -import { Schemas } from 'vs/base/common/network'; import * as resources from 'vs/base/common/resources'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IFileService } from 'vs/platform/files/common/files'; -import { IExtensionManifest, ExtensionType, ExtensionIdentifierWithVersion, IExtension as IPlatformExtension } from 'vs/platform/extensions/common/extensions'; +import { IExtensionManifest, ExtensionType, ExtensionIdentifierWithVersion, IExtension as IPlatformExtension, isUIExtension } from 'vs/platform/extensions/common/extensions'; interface IExtensionStateProvider { (extension: Extension): T; @@ -45,13 +42,12 @@ interface IExtensionStateProvider { class Extension implements IExtension { - public get local(): ILocalExtension { return this.locals[0]; } public enablementState: EnablementState = EnablementState.Enabled; constructor( private galleryService: IExtensionGalleryService, private stateProvider: IExtensionStateProvider, - public locals: ILocalExtension[], + public local: ILocalExtension | undefined, public gallery: IGalleryExtension | undefined, private telemetryService: ITelemetryService, private logService: ILogService, @@ -383,7 +379,6 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, @IWindowService private readonly windowService: IWindowService, @ILogService private readonly logService: ILogService, @IProgressService2 private readonly progressService: IProgressService2, - @IExtensionService private readonly runtimeExtensionService: IExtensionService, @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, @IStorageService private readonly storageService: IStorageService, @IFileService private readonly fileService: IFileService @@ -430,23 +425,20 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, queryLocal(): Promise { return this.extensionService.getInstalled() - .then(installed => this.getDistinctInstalledExtensions(installed) - .then(distinctInstalled => { - const installedById = index(this.installed, e => e.identifier.id); - const groupById = groupBy(installed, i => i.identifier.id); - this.installed = distinctInstalled.map(local => { - const locals = groupById[local.identifier.id]; - locals.splice(locals.indexOf(local), 1); - locals.splice(0, 0, local); - const extension = installedById[local.identifier.id] || new Extension(this.galleryService, this.stateProvider, locals, undefined, this.telemetryService, this.logService, this.fileService); - extension.locals = locals; - extension.enablementState = this.extensionEnablementService.getEnablementState(local); - return extension; - }); + .then(installed => { + if (this.extensionManagementServerService.remoteExtensionManagementServer) { + installed = installed.filter(installed => this.belongsToWindow(installed)); + } + const installedById = index(this.installed, e => e.identifier.id); + this.installed = installed.map(local => { + const extension = installedById[local.identifier.id] || new Extension(this.galleryService, this.stateProvider, local, undefined, this.telemetryService, this.logService, this.fileService); + extension.enablementState = this.extensionEnablementService.getEnablementState(local); + return extension; + }); - this._onChange.fire(undefined); - return this.local; - })); + this._onChange.fire(undefined); + return this.local; + }); } queryGallery(options: IQueryOptions = {}): Promise> { @@ -491,52 +483,19 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, return Promise.resolve(this.editorService.openEditor(this.instantiationService.createInstance(ExtensionsInput, extension), undefined, sideByside ? SIDE_GROUP : ACTIVE_GROUP)); } - private getDistinctInstalledExtensions(allInstalled: ILocalExtension[]): Promise { - if (!this.hasDuplicates(allInstalled)) { - return Promise.resolve(allInstalled); + private belongsToWindow(extension: ILocalExtension): boolean { + if (!this.extensionManagementServerService.remoteExtensionManagementServer) { + return true; } - return Promise.all([this.runtimeExtensionService.getExtensions(), this.extensionEnablementService.getDisabledExtensions()]) - .then(([runtimeExtensions, disabledExtensionIdentifiers]) => { - const groups = groupBy(allInstalled, (extension: ILocalExtension) => { - const isDisabled = disabledExtensionIdentifiers.some(identifier => areSameExtensions(identifier, extension.identifier)); - if (isDisabled) { - return extension.location.scheme === Schemas.file ? 'disabled:primary' : 'disabled:secondary'; - } else { - return 'enabled'; - } - }); - const enabled: ILocalExtension[] = []; - const notRunningExtensions: ILocalExtension[] = []; - const seenExtensions: { [id: string]: boolean } = Object.create({}); - for (const extension of (groups['enabled'] || [])) { - if (runtimeExtensions.some(r => r.extensionLocation.toString() === extension.location.toString())) { - enabled.push(extension); - seenExtensions[extension.identifier.id] = true; - } else { - notRunningExtensions.push(extension); - } - } - for (const extension of notRunningExtensions) { - if (!seenExtensions[extension.identifier.id]) { - enabled.push(extension); - seenExtensions[extension.identifier.id] = true; - } - } - const primaryDisabled = groups['disabled:primary'] || []; - const secondaryDisabled = (groups['disabled:secondary'] || []).filter(disabled => { - return primaryDisabled.every(p => !areSameExtensions(p.identifier, disabled.identifier)); - }); - return [...enabled, ...primaryDisabled, ...secondaryDisabled]; - }); - } - - private hasDuplicates(extensions: ILocalExtension[]): boolean { - const seen: { [key: string]: boolean; } = Object.create(null); - for (const i of extensions) { - if (seen[i.identifier.id]) { + const extensionManagementServer = this.extensionManagementServerService.getExtensionManagementServer(extension.location); + if (isUIExtension(extension.manifest, this.configurationService)) { + if (this.extensionManagementServerService.localExtensionManagementServer === extensionManagementServer) { + return true; + } + } else { + if (this.extensionManagementServerService.remoteExtensionManagementServer === extensionManagementServer) { return true; } - seen[i.identifier.id] = true; } return false; } @@ -554,7 +513,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, this.syncLocalWithGalleryExtension(result, gallery); } } else { - result = new Extension(this.galleryService, this.stateProvider, [], gallery, this.telemetryService, this.logService, this.fileService); + result = new Extension(this.galleryService, this.stateProvider, undefined, gallery, this.telemetryService, this.logService, this.fileService); } if (maliciousExtensionSet.has(result.identifier.id)) { @@ -581,13 +540,15 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, private syncLocalWithGalleryExtension(extension: Extension, gallery: IGalleryExtension) { // Sync the local extension with gallery extension if local extension doesnot has metadata - Promise.all(extension.locals.map(local => local.metadata ? Promise.resolve(local) : this.extensionService.updateMetadata(local, { id: gallery.identifier.uuid, publisherDisplayName: gallery.publisherDisplayName, publisherId: gallery.publisherId }))) - .then(locals => { - extension.locals = locals; - extension.gallery = gallery; - this._onChange.fire(extension); - this.eventuallyAutoUpdateExtensions(); - }); + if (extension.local) { + (extension.local.metadata ? Promise.resolve(extension.local) : this.extensionService.updateMetadata(extension.local, { id: gallery.identifier.uuid, publisherDisplayName: gallery.publisherDisplayName, publisherId: gallery.publisherId })) + .then(local => { + extension.local = local; + extension.gallery = gallery; + this._onChange.fire(extension); + this.eventuallyAutoUpdateExtensions(); + }); + } } checkForUpdates(): Promise { @@ -699,9 +660,9 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, } const ext = extension as Extension; - const toUninstall: ILocalExtension[] = ext.locals.length ? ext.locals : this.installed.filter(e => areSameExtensions(e.identifier, extension.identifier))[0].locals; + const toUninstall: ILocalExtension = ext.local ? ext.local : this.installed.filter(e => areSameExtensions(e.identifier, extension.identifier))[0].local; - if (!toUninstall || !toUninstall.length) { + if (!toUninstall) { return Promise.reject(new Error('Missing local')); } @@ -709,8 +670,8 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, return this.progressService.withProgress({ location: ProgressLocation.Extensions, title: nls.localize('uninstallingExtension', 'Uninstalling extension....'), - source: `${toUninstall[0].identifier.id}` - }, () => Promise.all(toUninstall.map(local => this.extensionService.uninstall(local))).then(() => undefined)); + source: `${toUninstall.identifier.id}` + }, () => this.extensionService.uninstall(toUninstall).then(() => undefined)); } installVersion(extension: IExtension, version: string): Promise { @@ -744,16 +705,16 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, } const ext = extension as Extension; - const toReinstall: ILocalExtension[] = ext.locals.length ? ext.locals : this.installed.filter(e => areSameExtensions(e.identifier, extension.identifier))[0].locals; + const toReinstall: ILocalExtension = ext.local ? ext.local : this.installed.filter(e => areSameExtensions(e.identifier, extension.identifier))[0].local; - if (!toReinstall || !toReinstall.length) { + if (!toReinstall) { return Promise.reject(new Error('Missing local')); } return this.progressService.withProgress({ location: ProgressLocation.Extensions, - source: `${toReinstall[0].identifier.id}` - }, () => Promise.all(toReinstall.map(local => this.extensionService.reinstallFromGallery(local))).then(() => undefined)); + source: `${toReinstall.identifier.id}` + }, () => this.extensionService.reinstallFromGallery(toReinstall).then(() => undefined)); } private installWithProgress(installTask: () => Promise, extensionName?: string): Promise { @@ -913,7 +874,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, let extension = this.installed.filter(e => areSameExtensions(e.identifier, gallery.identifier))[0]; if (!extension) { - extension = new Extension(this.galleryService, this.stateProvider, [], gallery, this.telemetryService, this.logService, this.fileService); + extension = new Extension(this.galleryService, this.stateProvider, undefined, gallery, this.telemetryService, this.logService, this.fileService); } this.installing.push(extension); @@ -924,29 +885,22 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, private onDidInstallExtension(event: DidInstallExtensionEvent): void { const { local, zipPath, error, gallery } = event; const installingExtension = gallery ? this.installing.filter(e => areSameExtensions(e.identifier, gallery.identifier))[0] : null; - let extension: Extension | undefined = installingExtension ? installingExtension : zipPath ? new Extension(this.galleryService, this.stateProvider, local ? [local] : [], undefined, this.telemetryService, this.logService, this.fileService) : undefined; + this.installing = installingExtension ? this.installing.filter(e => e !== installingExtension) : this.installing; + + if (local && !this.belongsToWindow(local)) { + return; + } + + let extension: Extension | undefined = installingExtension ? installingExtension : zipPath ? new Extension(this.galleryService, this.stateProvider, local, undefined, this.telemetryService, this.logService, this.fileService) : undefined; if (extension) { - this.installing = installingExtension ? this.installing.filter(e => e !== installingExtension) : this.installing; if (local) { const installed = this.installed.filter(e => areSameExtensions(e.identifier, extension!.identifier))[0]; if (installed) { extension = installed; - const newServer = this.extensionManagementServerService.getExtensionManagementServer(local.location); - const existingLocal = newServer && installed.locals.filter(l => { - const server = this.extensionManagementServerService.getExtensionManagementServer(l.location); - return server && server.authority === newServer.authority; - })[0]; - if (existingLocal) { - const locals = [...installed.locals]; - locals.splice(installed.locals.indexOf(existingLocal), 1, local); - installed.locals = locals; - } else { - installed.locals = [...installed.locals, local]; - } } else { - extension.locals = [local]; this.installed.push(extension); } + extension.local = local; } } this._onChange.fire(error ? undefined : extension); 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 38a85d5b399..2abd6bb2a40 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 @@ -12,7 +12,7 @@ import { IExtensionsWorkbenchService, ExtensionState, AutoCheckUpdatesConfigurat import { ExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/node/extensionsWorkbenchService'; import { IExtensionManagementService, IExtensionGalleryService, IExtensionEnablementService, IExtensionTipsService, ILocalExtension, IGalleryExtension, - DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IGalleryExtensionAssets, IExtensionIdentifier, EnablementState, InstallOperation + DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IGalleryExtensionAssets, IExtensionIdentifier, EnablementState, InstallOperation, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/platform/extensionManagement/common/extensionManagement'; import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; @@ -37,6 +37,9 @@ import { URLService } from 'vs/platform/url/common/urlService'; import { URI } from 'vs/base/common/uri'; import { CancellationToken } from 'vs/base/common/cancellation'; import { ExtensionType } from 'vs/platform/extensions/common/extensions'; +import { ExtensionManagementServerService } from 'vs/workbench/services/extensions/node/extensionManagementServerService'; +import { IRemoteAgentService } from 'vs/workbench/services/remote/node/remoteAgentService'; +import { RemoteAgentService } from 'vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl'; suite('ExtensionsWorkbenchServiceTest', () => { @@ -73,6 +76,9 @@ suite('ExtensionsWorkbenchServiceTest', () => { } }); + instantiationService.stub(IRemoteAgentService, RemoteAgentService); + instantiationService.stub(IExtensionManagementServerService, instantiationService.createInstance(ExtensionManagementServerService, { authority: 'vscode-local', extensionManagementService: instantiationService.get(IExtensionManagementService), label: 'local' })); + instantiationService.stub(IExtensionManagementService, ExtensionManagementService); instantiationService.stub(IExtensionManagementService, 'onInstallExtension', installEvent.event); instantiationService.stub(IExtensionManagementService, 'onDidInstallExtension', didInstallEvent.event); From 799cab9e1d0d2e5fceff792228a837cf49bb517f Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 23 Jan 2019 17:17:39 +0100 Subject: [PATCH 029/274] fix null checks --- .../node/extensionsWorkbenchService.ts | 42 ++++++++----------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts index 22c75922e8b..4aee8a6eac0 100644 --- a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts +++ b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts @@ -59,7 +59,7 @@ class Extension implements IExtension { } get name(): string { - return this.gallery ? this.gallery.name : this.local.manifest.name; + return this.gallery ? this.gallery.name : this.local!.manifest.name; } get displayName(): string { @@ -67,22 +67,22 @@ class Extension implements IExtension { return this.gallery.displayName || this.gallery.name; } - return this.local.manifest.displayName || this.local.manifest.name; + return this.local!.manifest.displayName || this.local!.manifest.name; } get identifier(): IExtensionIdentifier { if (this.gallery) { return this.gallery.identifier; } - return this.local.identifier; + return this.local!.identifier; } get uuid(): string | undefined { - return this.gallery ? this.gallery.identifier.uuid : this.local.identifier.uuid; + return this.gallery ? this.gallery.identifier.uuid : this.local!.identifier.uuid; } get publisher(): string { - return this.gallery ? this.gallery.publisher : this.local.manifest.publisher; + return this.gallery ? this.gallery.publisher : this.local!.manifest.publisher; } get publisherDisplayName(): string { @@ -90,11 +90,11 @@ class Extension implements IExtension { return this.gallery.publisherDisplayName || this.gallery.publisher; } - if (this.local.metadata && this.local.metadata.publisherDisplayName) { - return this.local.metadata.publisherDisplayName; + if (this.local!.metadata && this.local!.metadata.publisherDisplayName) { + return this.local!.metadata.publisherDisplayName; } - return this.local.manifest.publisher; + return this.local!.manifest.publisher; } get version(): string { @@ -102,11 +102,11 @@ class Extension implements IExtension { } get latestVersion(): string { - return this.gallery ? this.gallery.version : this.local.manifest.version; + return this.gallery ? this.gallery.version : this.local!.manifest.version; } get description(): string { - return this.gallery ? this.gallery.description : this.local.manifest.description || ''; + return this.gallery ? this.gallery.description : this.local!.manifest.description || ''; } get url(): string | undefined { @@ -141,7 +141,7 @@ class Extension implements IExtension { } private get defaultIconUrl(): string { - if (this.type === ExtensionType.System) { + if (this.type === ExtensionType.System && this.local) { if (this.local.manifest && this.local.manifest.contributes) { if (Array.isArray(this.local.manifest.contributes.themes) && this.local.manifest.contributes.themes.length) { return require.toUrl('../electron-browser/media/theme-icon.png'); @@ -190,7 +190,7 @@ class Extension implements IExtension { if (gallery) { return getGalleryExtensionTelemetryData(gallery); } else { - return getLocalExtensionTelemetryData(local); + return getLocalExtensionTelemetryData(local!); } } @@ -211,7 +211,7 @@ class Extension implements IExtension { return Promise.resolve(null); } - return Promise.resolve(this.local.manifest); + return Promise.resolve(this.local!.manifest); } hasReadme(): boolean { @@ -655,12 +655,8 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, } uninstall(extension: IExtension): Promise { - if (!(extension instanceof Extension)) { - return Promise.resolve(); - } - - const ext = extension as Extension; - const toUninstall: ILocalExtension = ext.local ? ext.local : this.installed.filter(e => areSameExtensions(e.identifier, extension.identifier))[0].local; + const ext = extension.local ? extension : this.installed.filter(e => areSameExtensions(e.identifier, extension.identifier))[0]; + const toUninstall: ILocalExtension | null = ext && ext.local ? ext.local : null; if (!toUninstall) { return Promise.reject(new Error('Missing local')); @@ -700,12 +696,8 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, } reinstall(extension: IExtension): Promise { - if (!(extension instanceof Extension)) { - return Promise.resolve(); - } - - const ext = extension as Extension; - const toReinstall: ILocalExtension = ext.local ? ext.local : this.installed.filter(e => areSameExtensions(e.identifier, extension.identifier))[0].local; + const ext = extension.local ? extension : this.installed.filter(e => areSameExtensions(e.identifier, extension.identifier))[0]; + const toReinstall: ILocalExtension | null = ext && ext.local ? ext.local : null; if (!toReinstall) { return Promise.reject(new Error('Missing local')); From 649422cc83f42a1739a528b95daf9da7a6b69c45 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 23 Jan 2019 17:30:20 +0100 Subject: [PATCH 030/274] fix compilations --- .../extensions/electron-browser/extensionsWidgets.ts | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsWidgets.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsWidgets.ts index 5c8b2439230..ee256cb75e8 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsWidgets.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsWidgets.ts @@ -13,7 +13,6 @@ import { IExtensionManagementServerService, IExtensionTipsService } from 'vs/pla import { ILabelService } from 'vs/platform/label/common/label'; import { extensionButtonProminentBackground, extensionButtonProminentForeground } from 'vs/workbench/parts/extensions/electron-browser/extensionsActions'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { STATUS_BAR_HOST_NAME_BACKGROUND } from 'vs/workbench/parts/remote/electron-browser/remote.contribution'; export abstract class ExtensionWidget extends Disposable implements IExtensionContainer { private _extension: IExtension; @@ -202,7 +201,6 @@ export class RemoteBadgeWidget extends ExtensionWidget { constructor( private parent: HTMLElement, @ILabelService private readonly labelService: ILabelService, - @IThemeService private readonly themeService: IThemeService, @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, ) { super(); @@ -228,12 +226,7 @@ export class RemoteBadgeWidget extends ExtensionWidget { this.element = append(this.parent, $('div.extension-remote-badge')); append(this.element, $('span.octicon.octicon-file-symlink-directory')); - const applyBadgeStyle = (theme) => { - const bgColor = this.themeService.getTheme().getColor(STATUS_BAR_HOST_NAME_BACKGROUND); - this.element.style.backgroundColor = bgColor ? bgColor.toString() : ''; - }; - applyBadgeStyle(this.themeService.getTheme()); - this.themeService.onThemeChange(applyBadgeStyle, this, this.disposables); + this.element.style.backgroundColor = 'rgb(56, 138, 52)'; const updateTitle = () => this.element.title = this.labelService.getHostLabel(); this.labelService.onDidChangeFormatters(() => updateTitle(), this, this.disposables); From 471a3ff8519b1006da0c3c6ac4e5b0828f9a986b Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 23 Jan 2019 17:33:48 +0100 Subject: [PATCH 031/274] fix warning --- src/vs/workbench/parts/debug/electron-browser/debugService.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/parts/debug/electron-browser/debugService.ts b/src/vs/workbench/parts/debug/electron-browser/debugService.ts index ce31f940835..c52576fa0a4 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugService.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugService.ts @@ -45,7 +45,6 @@ import { DebugSession } from 'vs/workbench/parts/debug/electron-browser/debugSes import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { IDebugService, State, IDebugSession, CONTEXT_DEBUG_TYPE, CONTEXT_DEBUG_STATE, CONTEXT_IN_DEBUG_MODE, IThread, IDebugConfiguration, VIEWLET_ID, REPL_ID, IConfig, ILaunch, IViewModel, IConfigurationManager, IDebugModel, IEnablement, IBreakpoint, IBreakpointData, ICompound, IGlobalConfig, IStackFrame, AdapterEndEvent, getStateLabel } from 'vs/workbench/parts/debug/common/debug'; import { isExtensionHostDebugging } from 'vs/workbench/parts/debug/common/debugUtils'; -import { RunOnceScheduler } from 'vs/base/common/async'; import { isErrorWithActions, createErrorWithActions } from 'vs/base/common/errorsWithActions'; const DEBUG_BREAKPOINTS_KEY = 'debug.breakpoint'; From 0cb91006c13ba092f5b88eedcf2c3680e26f7ad2 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 23 Jan 2019 17:49:42 +0100 Subject: [PATCH 032/274] Use theme styling --- .../extensions/electron-browser/extensionsWidgets.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsWidgets.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsWidgets.ts index ee256cb75e8..821ba4d90ae 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsWidgets.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsWidgets.ts @@ -13,6 +13,7 @@ import { IExtensionManagementServerService, IExtensionTipsService } from 'vs/pla import { ILabelService } from 'vs/platform/label/common/label'; import { extensionButtonProminentBackground, extensionButtonProminentForeground } from 'vs/workbench/parts/extensions/electron-browser/extensionsActions'; import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { STATUS_BAR_PROMINENT_ITEM_BACKGROUND } from 'vs/workbench/common/theme'; export abstract class ExtensionWidget extends Disposable implements IExtensionContainer { private _extension: IExtension; @@ -201,6 +202,7 @@ export class RemoteBadgeWidget extends ExtensionWidget { constructor( private parent: HTMLElement, @ILabelService private readonly labelService: ILabelService, + @IThemeService private readonly themeService: IThemeService, @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, ) { super(); @@ -226,7 +228,12 @@ export class RemoteBadgeWidget extends ExtensionWidget { this.element = append(this.parent, $('div.extension-remote-badge')); append(this.element, $('span.octicon.octicon-file-symlink-directory')); - this.element.style.backgroundColor = 'rgb(56, 138, 52)'; + const applyBadgeStyle = () => { + const bgColor = this.themeService.getTheme().getColor(STATUS_BAR_PROMINENT_ITEM_BACKGROUND); + this.element.style.backgroundColor = bgColor ? bgColor.toString() : ''; + }; + applyBadgeStyle(); + this.themeService.onThemeChange(applyBadgeStyle, this, this.disposables); const updateTitle = () => this.element.title = this.labelService.getHostLabel(); this.labelService.onDidChangeFormatters(() => updateTitle(), this, this.disposables); From 946e3e2374c7f1a67fbfa394612b308d6b746c5b Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 23 Jan 2019 18:48:08 +0100 Subject: [PATCH 033/274] Fix #66735 --- .../electron-browser/extensionsActions.ts | 11 ++++++----- .../electron-browser/extensionsActions.test.ts | 17 ++++++++++------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts index 29e89c2f2a8..013105be402 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts @@ -6,7 +6,7 @@ import 'vs/css!./media/extensionActions'; import { localize } from 'vs/nls'; import { IAction, Action } from 'vs/base/common/actions'; -import { Throttler } from 'vs/base/common/async'; +import { ThrottledDelayer } from 'vs/base/common/async'; import * as DOM from 'vs/base/browser/dom'; import * as paths from 'vs/base/common/paths'; import { Event } from 'vs/base/common/event'; @@ -956,7 +956,8 @@ export class ReloadAction extends ExtensionAction { private static readonly EnabledClass = 'extension-action reload'; private static readonly DisabledClass = `${ReloadAction.EnabledClass} disabled`; - private throttler: Throttler; + // Use delayer to wait for more updates + private throttler: ThrottledDelayer; private disposables: IDisposable[] = []; constructor( @@ -966,13 +967,13 @@ export class ReloadAction extends ExtensionAction { @IExtensionEnablementService private readonly extensionEnablementService: IExtensionEnablementService ) { super('extensions.reload', localize('reloadAction', "Reload"), ReloadAction.DisabledClass, false); - this.throttler = new Throttler(); + this.throttler = new ThrottledDelayer(50); this.extensionService.onDidChangeExtensions(this.update, this, this.disposables); this.update(); } - update(): void { - this.throttler.queue(() => { + update(): Promise { + return this.throttler.trigger(() => { this.enabled = false; this.tooltip = ''; if (!this.extension) { diff --git a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsActions.test.ts b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsActions.test.ts index 2a48e3dfcb9..281958141b7 100644 --- a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsActions.test.ts +++ b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsActions.test.ts @@ -698,7 +698,7 @@ suite('ExtensionsActions Test', () => { }); test('Test EnableAction when there is no extension', () => { - const testObject: ExtensionsActions.EnableDropDownAction = instantiationService.createInstance(ExtensionsActions.EnableDropDownAction, []); + const testObject: ExtensionsActions.EnableDropDownAction = instantiationService.createInstance(ExtensionsActions.EnableDropDownAction); assert.ok(!testObject.enabled); }); @@ -709,7 +709,7 @@ suite('ExtensionsActions Test', () => { return instantiationService.get(IExtensionsWorkbenchService).queryLocal() .then(extensions => { - const testObject: ExtensionsActions.EnableDropDownAction = instantiationService.createInstance(ExtensionsActions.EnableDropDownAction, []); + const testObject: ExtensionsActions.EnableDropDownAction = instantiationService.createInstance(ExtensionsActions.EnableDropDownAction); testObject.extension = extensions[0]; assert.ok(!testObject.enabled); }); @@ -723,7 +723,7 @@ suite('ExtensionsActions Test', () => { return instantiationService.get(IExtensionsWorkbenchService).queryLocal() .then(extensions => { - const testObject: ExtensionsActions.EnableDropDownAction = instantiationService.createInstance(ExtensionsActions.EnableDropDownAction, []); + const testObject: ExtensionsActions.EnableDropDownAction = instantiationService.createInstance(ExtensionsActions.EnableDropDownAction); testObject.extension = extensions[0]; assert.ok(testObject.enabled); }); @@ -738,7 +738,7 @@ suite('ExtensionsActions Test', () => { return instantiationService.get(IExtensionsWorkbenchService).queryLocal() .then(extensions => { - const testObject: ExtensionsActions.EnableDropDownAction = instantiationService.createInstance(ExtensionsActions.EnableDropDownAction, []); + const testObject: ExtensionsActions.EnableDropDownAction = instantiationService.createInstance(ExtensionsActions.EnableDropDownAction); testObject.extension = extensions[0]; assert.ok(testObject.enabled); }); @@ -751,7 +751,7 @@ suite('ExtensionsActions Test', () => { return instantiationService.get(IExtensionsWorkbenchService).queryGallery() .then(page => { - const testObject: ExtensionsActions.EnableDropDownAction = instantiationService.createInstance(ExtensionsActions.EnableDropDownAction, []); + const testObject: ExtensionsActions.EnableDropDownAction = instantiationService.createInstance(ExtensionsActions.EnableDropDownAction); testObject.extension = page.firstPage[0]; assert.ok(!testObject.enabled); }); @@ -763,7 +763,7 @@ suite('ExtensionsActions Test', () => { return instantiationService.get(IExtensionsWorkbenchService).queryGallery() .then(page => { - const testObject: ExtensionsActions.EnableDropDownAction = instantiationService.createInstance(ExtensionsActions.EnableDropDownAction, []); + const testObject: ExtensionsActions.EnableDropDownAction = instantiationService.createInstance(ExtensionsActions.EnableDropDownAction); testObject.extension = page.firstPage[0]; instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update()); @@ -778,7 +778,7 @@ suite('ExtensionsActions Test', () => { return instantiationService.get(IExtensionsWorkbenchService).queryLocal() .then(extensions => { - const testObject: ExtensionsActions.EnableDropDownAction = instantiationService.createInstance(ExtensionsActions.EnableDropDownAction, []); + const testObject: ExtensionsActions.EnableDropDownAction = instantiationService.createInstance(ExtensionsActions.EnableDropDownAction); testObject.extension = extensions[0]; uninstallEvent.fire(local.identifier); assert.ok(!testObject.enabled); @@ -1214,6 +1214,7 @@ suite('ExtensionsActions Test', () => { return workbenchService.queryLocal().then(extensions => { testObject.extension = extensions[0]; return workbenchService.setEnablement(extensions[0], EnablementState.Disabled) + .then(() => testObject.update()) .then(() => { assert.ok(testObject.enabled); assert.equal('Please reload Visual Studio Code to complete the disabling of this extension.', testObject.tooltip); @@ -1250,6 +1251,7 @@ suite('ExtensionsActions Test', () => { .then(extensions => { testObject.extension = extensions[0]; return workbenchService.setEnablement(extensions[0], EnablementState.Enabled) + .then(() => testObject.update()) .then(() => { assert.ok(testObject.enabled); assert.equal('Please reload Visual Studio Code to complete the enabling of this extension.', testObject.tooltip); @@ -1294,6 +1296,7 @@ suite('ExtensionsActions Test', () => { installEvent.fire({ identifier: gallery.identifier, gallery }); didInstallEvent.fire({ identifier: gallery.identifier, gallery, operation: InstallOperation.Install, local: aLocalExtension('a', gallery, gallery) }); return workbenchService.setEnablement(extensions[0], EnablementState.Enabled) + .then(() => testObject.update()) .then(() => { assert.ok(testObject.enabled); assert.equal('Please reload Visual Studio Code to complete the enabling of this extension.', testObject.tooltip); From ad73b8fa10489a4bfc4d967bc840e4863e0fb692 Mon Sep 17 00:00:00 2001 From: Chris Patterson Date: Wed, 23 Jan 2019 14:08:03 -0500 Subject: [PATCH 034/274] Update build badge url Updating build badge url to latest format. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d5bebc76f27..b08b3fc38f0 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Visual Studio Code - Open Source -[![Build Status](https://vscode.visualstudio.com/_apis/public/build/definitions/a4cdce18-a05c-4bb8-9476-5d07e63bfd76/1/badge?branchName=master)](https://aka.ms/vscode-builds) +[![Build Status](https://dev.azure.com/vscode/VSCode/_apis/build/status/VS%20Code?branchName=master)](https://aka.ms/vscode-builds) [![Feature Requests](https://img.shields.io/github/issues/Microsoft/vscode/feature-request.svg)](https://github.com/Microsoft/vscode/issues?q=is%3Aopen+is%3Aissue+label%3Afeature-request+sort%3Areactions-%2B1-desc) [![Bugs](https://img.shields.io/github/issues/Microsoft/vscode/bug.svg)](https://github.com/Microsoft/vscode/issues?utf8=✓&q=is%3Aissue+is%3Aopen+label%3Abug) [![Gitter](https://img.shields.io/badge/chat-on%20gitter-yellow.svg)](https://gitter.im/Microsoft/vscode) From ceb8018399cdaa9ec5c2fca73476be4725da6d1f Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 23 Jan 2019 20:09:53 +0100 Subject: [PATCH 035/274] better fix for session focus switching Fixes #65920 --- .../parts/debug/electron-browser/debugService.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/vs/workbench/parts/debug/electron-browser/debugService.ts b/src/vs/workbench/parts/debug/electron-browser/debugService.ts index c52576fa0a4..8e930b91103 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugService.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugService.ts @@ -46,6 +46,7 @@ import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { IDebugService, State, IDebugSession, CONTEXT_DEBUG_TYPE, CONTEXT_DEBUG_STATE, CONTEXT_IN_DEBUG_MODE, IThread, IDebugConfiguration, VIEWLET_ID, REPL_ID, IConfig, ILaunch, IViewModel, IConfigurationManager, IDebugModel, IEnablement, IBreakpoint, IBreakpointData, ICompound, IGlobalConfig, IStackFrame, AdapterEndEvent, getStateLabel } from 'vs/workbench/parts/debug/common/debug'; import { isExtensionHostDebugging } from 'vs/workbench/parts/debug/common/debugUtils'; import { isErrorWithActions, createErrorWithActions } from 'vs/base/common/errorsWithActions'; +import { RunOnceScheduler } from 'vs/base/common/async'; const DEBUG_BREAKPOINTS_KEY = 'debug.breakpoint'; const DEBUG_BREAKPOINTS_ACTIVATED_KEY = 'debug.breakpointactivated'; @@ -488,7 +489,16 @@ export class DebugService implements IDebugService { } private registerSessionListeners(session: IDebugSession): void { + const sessionRunningScheduler = new RunOnceScheduler(() => { + // Do not immediatly defocus the stack frame if the session is running + if (session.state === State.Running && this.viewModel.focusedSession === session) { + this.viewModel.setFocus(undefined, this.viewModel.focusedThread, session, false); + } + }, 200); this.toDispose.push(session.onDidChangeState(() => { + if (session.state === State.Running && this.viewModel.focusedSession === session) { + sessionRunningScheduler.schedule(); + } if (session === this.viewModel.focusedSession) { this.onStateChange(); } From c43b4376171c984ed5b1256f3e5448e765b4a94b Mon Sep 17 00:00:00 2001 From: Thomas Ladd Date: Wed, 23 Jan 2019 13:46:38 -0600 Subject: [PATCH 036/274] Make context parameter optional for IStandaloneDiffEditor and IStandaloneCodeEditor addCommand Fix #67011 --- src/vs/editor/standalone/browser/standaloneCodeEditor.ts | 8 ++++---- src/vs/monaco.d.ts | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/vs/editor/standalone/browser/standaloneCodeEditor.ts b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts index 715c24c363b..71cf8c51599 100644 --- a/src/vs/editor/standalone/browser/standaloneCodeEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts @@ -122,13 +122,13 @@ export interface IDiffEditorConstructionOptions extends IDiffEditorOptions { } export interface IStandaloneCodeEditor extends ICodeEditor { - addCommand(keybinding: number, handler: ICommandHandler, context: string): string | null; + addCommand(keybinding: number, handler: ICommandHandler, context?: string): string | null; createContextKey(key: string, defaultValue: T): IContextKey; addAction(descriptor: IActionDescriptor): IDisposable; } export interface IStandaloneDiffEditor extends IDiffEditor { - addCommand(keybinding: number, handler: ICommandHandler, context: string): string | null; + addCommand(keybinding: number, handler: ICommandHandler, context?: string): string | null; createContextKey(key: string, defaultValue: T): IContextKey; addAction(descriptor: IActionDescriptor): IDisposable; @@ -182,7 +182,7 @@ export class StandaloneCodeEditor extends CodeEditorWidget implements IStandalon createAriaDomNode(); } - public addCommand(keybinding: number, handler: ICommandHandler, context: string): string | null { + public addCommand(keybinding: number, handler: ICommandHandler, context?: string): string | null { if (!this._standaloneKeybindingService) { console.warn('Cannot add command because the editor is configured with an unrecognized KeybindingService'); return null; @@ -409,7 +409,7 @@ export class StandaloneDiffEditor extends DiffEditorWidget implements IStandalon return super.getModifiedEditor(); } - public addCommand(keybinding: number, handler: ICommandHandler, context: string): string | null { + public addCommand(keybinding: number, handler: ICommandHandler, context?: string): string | null { return this.getModifiedEditor().addCommand(keybinding, handler, context); } diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 937ec711364..35793ae2e20 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -1071,13 +1071,13 @@ declare namespace monaco.editor { } export interface IStandaloneCodeEditor extends ICodeEditor { - addCommand(keybinding: number, handler: ICommandHandler, context: string): string | null; + addCommand(keybinding: number, handler: ICommandHandler, context?: string): string | null; createContextKey(key: string, defaultValue: T): IContextKey; addAction(descriptor: IActionDescriptor): IDisposable; } export interface IStandaloneDiffEditor extends IDiffEditor { - addCommand(keybinding: number, handler: ICommandHandler, context: string): string | null; + addCommand(keybinding: number, handler: ICommandHandler, context?: string): string | null; createContextKey(key: string, defaultValue: T): IContextKey; addAction(descriptor: IActionDescriptor): IDisposable; getOriginalEditor(): IStandaloneCodeEditor; From c447fc0de34daacb2e806ce2d5a4272ab46d59ef Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 23 Jan 2019 11:54:25 -0800 Subject: [PATCH 037/274] Fix terminal screen reader support when set to auto Part of #59794 --- .../terminal/electron-browser/terminalInstance.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts index 3b51fc9f5d8..32385809cd9 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as browser from 'vs/base/browser/browser'; import * as lifecycle from 'vs/base/common/lifecycle'; import * as nls from 'vs/nls'; import * as platform from 'vs/base/common/platform'; @@ -31,7 +32,6 @@ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService import { ansiColorIdentifiers, TERMINAL_BACKGROUND_COLOR, TERMINAL_FOREGROUND_COLOR, TERMINAL_CURSOR_FOREGROUND_COLOR, TERMINAL_CURSOR_BACKGROUND_COLOR, TERMINAL_SELECTION_BACKGROUND_COLOR } from 'vs/workbench/parts/terminal/common/terminalColorRegistry'; import { PANEL_BACKGROUND } from 'vs/workbench/common/theme'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; -import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { INotificationService, Severity, IPromptChoice } from 'vs/platform/notification/common/notification'; import { ILogService } from 'vs/platform/log/common/log'; import { TerminalCommandTracker } from 'vs/workbench/parts/terminal/node/terminalCommandTracker'; @@ -399,7 +399,6 @@ export class TerminalInstance implements ITerminalInstance { Terminal.strings.promptLabel = nls.localize('terminal.integrated.a11yPromptLabel', 'Terminal input'); Terminal.strings.tooMuchOutput = nls.localize('terminal.integrated.a11yTooMuchOutput', 'Too much output to announce, navigate to rows manually to read'); } - const accessibilitySupport = this._configurationService.getValue('editor').accessibilitySupport; const font = this._configHelper.getFont(undefined, true); const config = this._configHelper.config; this._xterm = new Terminal({ @@ -413,7 +412,7 @@ export class TerminalInstance implements ITerminalInstance { letterSpacing: font.letterSpacing, lineHeight: font.lineHeight, bellStyle: config.enableBell ? 'sound' : 'none', - screenReaderMode: accessibilitySupport === 'on', + screenReaderMode: this._isScreenReaderOptimized(), macOptionIsMeta: config.macOptionIsMeta, macOptionClickForcesSelection: config.macOptionClickForcesSelection, rightClickSelectsWord: config.rightClickBehavior === 'selectWord', @@ -450,6 +449,12 @@ export class TerminalInstance implements ITerminalInstance { this._disposables.push(this._themeService.onThemeChange(theme => this._updateTheme(theme))); } + private _isScreenReaderOptimized(): boolean { + const detected = browser.getAccessibilitySupport() === platform.AccessibilitySupport.Enabled; + const config = this._configurationService.getValue('editor.accessibilitySupport'); + return config === 'on' || (config === 'auto' && detected); + } + public reattachToElement(container: HTMLElement): void { if (!this._wrapperElement) { throw new Error('The terminal instance has not been attached to a container yet'); @@ -1110,8 +1115,7 @@ export class TerminalInstance implements ITerminalInstance { } public updateAccessibilitySupport(): void { - const value = this._configurationService.getValue('editor.accessibilitySupport'); - this._xterm.setOption('screenReaderMode', value === 'on'); + this._xterm.setOption('screenReaderMode', this._isScreenReaderOptimized()); } private _setCursorBlink(blink: boolean): void { From d3c14cf0af9dab84ad68df74659ed0cdd9b83f57 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Wed, 23 Jan 2019 12:11:29 -0800 Subject: [PATCH 038/274] Move logic to service --- .../client/src/htmlMain.ts | 2 +- .../server/src/htmlServerMain.ts | 4 +--- .../server/src/modes/htmlMode.ts | 23 ++----------------- 3 files changed, 4 insertions(+), 25 deletions(-) diff --git a/extensions/html-language-features/client/src/htmlMain.ts b/extensions/html-language-features/client/src/htmlMain.ts index 40825b84131..b9d3978b304 100644 --- a/extensions/html-language-features/client/src/htmlMain.ts +++ b/extensions/html-language-features/client/src/htmlMain.ts @@ -89,7 +89,7 @@ export function activate(context: ExtensionContext) { languages.registerSelectionRangeProvider('html', { async provideSelectionRanges(document: TextDocument, position: Position): Promise { const textDocument = TextDocumentIdentifier.create(document.uri.toString()); - const rawRanges: Range[] = await client.sendRequest('$/selection', { textDocument, position }); + const rawRanges: Range[] = await client.sendRequest('$/textDocument/selectionRange', { textDocument, position }); return rawRanges.map(r => { const actualRange = new Range(new Position(r.start.line, r.start.character), new Position(r.end.line, r.end.character)); diff --git a/extensions/html-language-features/server/src/htmlServerMain.ts b/extensions/html-language-features/server/src/htmlServerMain.ts index 06921362d85..2ccb26c3f60 100644 --- a/extensions/html-language-features/server/src/htmlServerMain.ts +++ b/extensions/html-language-features/server/src/htmlServerMain.ts @@ -480,7 +480,7 @@ connection.onFoldingRanges((params, token) => { }, null, `Error while computing folding regions for ${params.textDocument.uri}`, token); }); -connection.onRequest('$/selection', async (params) => { +connection.onRequest('$/textDocument/selectionRange', async (params) => { const document = documents.get(params.textDocument.uri); const position: Position = params.position; @@ -489,8 +489,6 @@ connection.onRequest('$/selection', async (params) => { if (htmlMode && htmlMode.doSelection) { return htmlMode.doSelection(document, position); } - - console.log(position.line, position.character); } return Promise.resolve(null); }); diff --git a/extensions/html-language-features/server/src/modes/htmlMode.ts b/extensions/html-language-features/server/src/modes/htmlMode.ts index 00dc9313a37..00a690f56eb 100644 --- a/extensions/html-language-features/server/src/modes/htmlMode.ts +++ b/extensions/html-language-features/server/src/modes/htmlMode.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { getLanguageModelCache } from '../languageModelCache'; -import { LanguageService as HTMLLanguageService, HTMLDocument, DocumentContext, FormattingOptions, HTMLFormatConfiguration, Node } from 'vscode-html-languageservice'; +import { LanguageService as HTMLLanguageService, HTMLDocument, DocumentContext, FormattingOptions, HTMLFormatConfiguration } from 'vscode-html-languageservice'; import { TextDocument, Position, Range, CompletionItem, FoldingRange } from 'vscode-languageserver-types'; import { LanguageMode, Workspace } from './languageModes'; import { getPathCompletionParticipant } from './pathCompletion'; @@ -16,26 +16,7 @@ export function getHTMLMode(htmlLanguageService: HTMLLanguageService, workspace: return 'html'; }, doSelection(document: TextDocument, position: Position): Range[] { - const htmlDocument = htmlDocuments.get(document); - let currNode = htmlDocument.findNodeAt(document.offsetAt(position)); - let getNodeRanges = (n: Node) => { - if (n.startTagEnd && n.endTagStart && n.startTagEnd < n.endTagStart) { - return [ - Range.create(document.positionAt(n.startTagEnd), document.positionAt(n.endTagStart)), - Range.create(document.positionAt(n.start), document.positionAt(n.end)), - ]; - } - - return [Range.create(document.positionAt(n.start), document.positionAt(n.end))]; - }; - const result = [...getNodeRanges(currNode)]; - - while (currNode.parent) { - currNode = currNode.parent; - getNodeRanges(currNode).forEach(r => result.push(r)); - } - - return result; + return htmlLanguageService.getSelectionRanges(document, position); }, doComplete(document: TextDocument, position: Position, settings = workspace.settings) { let options = settings && settings.html && settings.html.suggest; From c47d3d4d46d09f1358d6ef5b5163bc048e46e9bd Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Wed, 23 Jan 2019 12:15:20 -0800 Subject: [PATCH 039/274] Update service --- extensions/html-language-features/server/package.json | 2 +- extensions/html-language-features/server/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/html-language-features/server/package.json b/extensions/html-language-features/server/package.json index 77ceb8edc5d..a1c09e1aa2d 100644 --- a/extensions/html-language-features/server/package.json +++ b/extensions/html-language-features/server/package.json @@ -10,7 +10,7 @@ "main": "./out/htmlServerMain", "dependencies": { "vscode-css-languageservice": "^3.0.13-next.6", - "vscode-html-languageservice": "^2.1.11-next.3", + "vscode-html-languageservice": "^2.1.11-next.4", "vscode-languageserver": "^5.1.0", "vscode-languageserver-types": "^3.13.0", "vscode-nls": "^4.0.0", diff --git a/extensions/html-language-features/server/yarn.lock b/extensions/html-language-features/server/yarn.lock index 3bb00545094..a1ed0ca3f33 100644 --- a/extensions/html-language-features/server/yarn.lock +++ b/extensions/html-language-features/server/yarn.lock @@ -237,10 +237,10 @@ vscode-css-languageservice@^3.0.13-next.6: vscode-languageserver-types "^3.13.0" vscode-nls "^4.0.0" -vscode-html-languageservice@^2.1.11-next.3: - version "2.1.11-next.3" - resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-2.1.11-next.3.tgz#f8eaee042c161f47fb71850c558428d0d91c881f" - integrity sha512-621+f1nbRvMgLyvW1Aa9shZ9r9qBIXMi4fF8o0voooHjIggrIbstUpYmaQIFRNill3b9HCNdluTZnAQlQGU6Ew== +vscode-html-languageservice@^2.1.11-next.4: + version "2.1.11-next.4" + resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-2.1.11-next.4.tgz#bcfe68ca17300d7f845889be3cb947af95dab409" + integrity sha512-dQOcdqLhMZuAWhSoe/AqqvVo2klkWpCHwp14dWvgrAfUyKM7l0e0kk9lbf8kt1Kn2J9ukDTySIcOkbHaVSwcHA== dependencies: vscode-languageserver-types "^3.13.0" vscode-nls "^4.0.0" From ba41bffa99d959491e1b20e2531e237d73b11d8e Mon Sep 17 00:00:00 2001 From: Segev Finer Date: Wed, 23 Jan 2019 23:39:04 +0200 Subject: [PATCH 040/274] Print a message before installing dependencies in npm post install Should make it easier to see what it's doing and debugging failures. --- build/npm/postinstall.js | 1 + 1 file changed, 1 insertion(+) diff --git a/build/npm/postinstall.js b/build/npm/postinstall.js index ea2d2d9a2dc..34a8e28ff1a 100644 --- a/build/npm/postinstall.js +++ b/build/npm/postinstall.js @@ -17,6 +17,7 @@ function yarnInstall(location, opts) { opts.cwd = location; opts.stdio = 'inherit'; + console.log('Installing dependencies in \'%s\'.', location); const result = cp.spawnSync(yarn, ['install'], opts); if (result.error || result.status !== 0) { From c45d91d9c147f05066c41db13eb28b3ad3cacd44 Mon Sep 17 00:00:00 2001 From: SteVen Batten <6561887+sbatten@users.noreply.github.com> Date: Wed, 23 Jan 2019 14:16:37 -0800 Subject: [PATCH 041/274] Workbench Grid Layout Behind flag (#63066) * partially functioning grid view * lots of toggling behavior and editor restore * dimension changes * restore layout and fix titlebar * fix view dimensions on toggle/move * increase timeout for debug config * undo unnecessary changes to CSS * put grid layout behind setting * fix strict null checks * address code comments from @bpasero * changing key name --- .vscode/launch.json | 3 +- src/vs/base/browser/ui/grid/grid.ts | 1 + src/vs/workbench/browser/media/part.css | 30 +- .../parts/activitybar/activitybarPart.ts | 38 +- .../activitybar/media/activityaction.css | 20 +- .../activitybar/media/activitybarpart.css | 8 +- .../workbench/browser/parts/compositePart.ts | 11 +- .../browser/parts/editor/editorGroupView.ts | 8 +- .../browser/parts/editor/editorPart.ts | 31 +- .../parts/editor/media/breadcrumbscontrol.css | 10 +- .../parts/editor/media/editorgroupview.css | 40 +- .../parts/editor/media/notabstitlecontrol.css | 30 +- .../parts/editor/media/tabstitlecontrol.css | 138 +++---- .../parts/editor/media/titlecontrol.css | 40 +- .../browser/parts/editor/tabsTitleControl.ts | 42 +- .../browser/parts/media/compositepart.css | 6 +- .../browser/parts/panel/media/panelpart.css | 34 +- .../browser/parts/panel/panelPart.ts | 64 ++- .../parts/sidebar/media/sidebarpart.css | 4 +- .../browser/parts/sidebar/sidebarPart.ts | 39 +- .../parts/statusbar/media/statusbarpart.css | 30 +- .../browser/parts/statusbar/statusbarPart.ts | 54 ++- .../parts/titlebar/media/titlebarpart.css | 54 +-- .../browser/parts/titlebar/titlebarPart.ts | 83 ++-- .../electron-browser/main.contribution.ts | 6 + .../electron-browser/media/workbench.css | 2 +- .../workbench/electron-browser/workbench.ts | 368 ++++++++++++++++-- .../browser/media/debug.contribution.css | 4 +- .../debug/browser/media/debugViewlet.css | 10 +- .../debug/browser/statusbarColorProvider.ts | 4 +- .../electron-browser/media/extensions.css | 2 +- .../electron-browser/feedbackStatusbarItem.ts | 2 +- .../media/explorerviewlet.css | 2 +- .../relauncher.contribution.ts | 9 +- .../scm/electron-browser/media/scmViewlet.css | 2 +- .../media/search.contribution.css | 2 +- .../watermark/electron-browser/watermark.css | 30 +- .../page/electron-browser/welcomePage.css | 92 ++--- .../page/electron-browser/welcomePage.ts | 22 +- .../electron-browser/walkThroughPart.css | 64 +-- .../electron-browser/walkThroughPart.ts | 22 +- .../editor/test/browser/editorService.test.ts | 13 +- .../test/browser/editorGroupsService.test.ts | 3 +- .../browser/media/progressService2.css | 4 +- 44 files changed, 978 insertions(+), 503 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index e217c585b42..0cde845dc24 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -123,7 +123,8 @@ "request": "launch", "name": "Launch VS Code", "windows": { - "runtimeExecutable": "${workspaceFolder}/scripts/code.bat" + "runtimeExecutable": "${workspaceFolder}/scripts/code.bat", + "timeout": 20000 }, "osx": { "runtimeExecutable": "${workspaceFolder}/scripts/code.sh" diff --git a/src/vs/base/browser/ui/grid/grid.ts b/src/vs/base/browser/ui/grid/grid.ts index 336329a05e2..39bbdeb9181 100644 --- a/src/vs/base/browser/ui/grid/grid.ts +++ b/src/vs/base/browser/ui/grid/grid.ts @@ -186,6 +186,7 @@ export interface IGridStyles extends IGridViewStyles { } export interface IGridOptions { styles?: IGridStyles; + proportionalLayout?: boolean; } export class Grid implements IDisposable { diff --git a/src/vs/workbench/browser/media/part.css b/src/vs/workbench/browser/media/part.css index fc87a52eaf3..32c3fe76eae 100644 --- a/src/vs/workbench/browser/media/part.css +++ b/src/vs/workbench/browser/media/part.css @@ -3,34 +3,34 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-workbench > .part > .title { +.monaco-workbench .part > .title { display: none; /* Parts have to opt in to show title area */ } -.monaco-workbench > .part > .title { +.monaco-workbench .part > .title { height: 35px; display: flex; box-sizing: border-box; overflow: hidden; } -.monaco-workbench > .part > .title { +.monaco-workbench .part > .title { padding-left: 8px; padding-right: 8px; } -.monaco-workbench > .part > .title > .title-label { +.monaco-workbench .part > .title > .title-label { line-height: 35px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } -.monaco-workbench > .part > .title > .title-label { +.monaco-workbench .part > .title > .title-label { padding-left: 12px; } -.monaco-workbench > .part > .title > .title-label h2 { +.monaco-workbench .part > .title > .title-label h2 { font-size: 11px; cursor: default; font-weight: normal; @@ -41,19 +41,19 @@ text-overflow: ellipsis; } -.monaco-workbench > .part > .title > .title-label a { +.monaco-workbench .part > .title > .title-label a { text-decoration: none; font-size: 13px; cursor: default; } -.monaco-workbench > .part > .title > .title-actions { +.monaco-workbench .part > .title > .title-actions { height: 35px; flex: 1; padding-left: 5px; } -.monaco-workbench > .part > .title > .title-actions .action-label { +.monaco-workbench .part > .title > .title-actions .action-label { display: block; height: 35px; line-height: 35px; @@ -63,16 +63,16 @@ background-repeat: no-repeat; } -.monaco-workbench > .part > .title > .title-actions .action-label .label { +.monaco-workbench .part > .title > .title-actions .action-label .label { display: none; } -.monaco-workbench > .part > .content { +.monaco-workbench .part > .content { font-size: 13px; } -.monaco-workbench > .part > .content > .monaco-progress-container, -.monaco-workbench > .part.editor > .content .monaco-progress-container { +.monaco-workbench .part > .content > .monaco-progress-container, +.monaco-workbench .part.editor > .content .monaco-progress-container { position: absolute; left: 0; top: 33px; /* at the bottom of the 35px height title container */ @@ -80,7 +80,7 @@ height: 2px; } -.monaco-workbench > .part > .content > .monaco-progress-container .progress-bit, -.monaco-workbench > .part.editor > .content .monaco-progress-container .progress-bit { +.monaco-workbench .part > .content > .monaco-progress-container .progress-bit, +.monaco-workbench .part.editor > .content .monaco-progress-container .progress-bit { height: 2px; } diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts index 404646c23f5..ec3397b9066 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts @@ -6,6 +6,7 @@ import 'vs/css!./media/activitybarpart'; import * as nls from 'vs/nls'; import { illegalArgument } from 'vs/base/common/errors'; +import { Emitter } from 'vs/base/common/event'; import { ActionsOrientation, ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { GlobalActivityExtensions, IGlobalActivityRegistry } from 'vs/workbench/common/activity'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -31,6 +32,7 @@ import { IViewsService, IViewContainersRegistry, Extensions as ViewContainerExte import { IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IViewlet } from 'vs/workbench/common/viewlet'; import { isUndefinedOrNull } from 'vs/base/common/types'; +import { ISerializableView } from 'vs/base/browser/ui/grid/grid'; const SCM_VIEWLET_ID = 'workbench.view.scm'; @@ -43,7 +45,7 @@ interface ICachedViewlet { views?: { when: string }[]; } -export class ActivitybarPart extends Part { +export class ActivitybarPart extends Part implements ISerializableView { private static readonly ACTION_HEIGHT = 50; private static readonly PINNED_VIEWLETS = 'workbench.activity.pinnedViewlets'; @@ -57,6 +59,15 @@ export class ActivitybarPart extends Part { private compositeBar: CompositeBar; private compositeActions: { [compositeId: string]: { activityAction: ViewletActivityAction, pinnedAction: ToggleCompositePinnedAction } } = Object.create(null); + element: HTMLElement; + minimumWidth: number = 50; + maximumWidth: number = 50; + minimumHeight: number = 0; + maximumHeight: number = Number.POSITIVE_INFINITY; + + private _onDidChange = new Emitter<{ width: number; height: number; }>(); + readonly onDidChange = this._onDidChange.event; + constructor( id: string, @IViewletService private readonly viewletService: IViewletService, @@ -180,6 +191,7 @@ export class ActivitybarPart extends Part { } createContentArea(parent: HTMLElement): HTMLElement { + this.element = parent; const content = document.createElement('div'); addClass(content, 'content'); parent.appendChild(content); @@ -338,13 +350,19 @@ export class ActivitybarPart extends Part { .map(v => v.id); } - layout(dimension: Dimension): Dimension[] { + layout(dimension: Dimension): Dimension[]; + layout(width: number, height: number): void; + layout(dim1: Dimension | number, dim2?: number): Dimension[] | void { if (!this.partService.isVisible(Parts.ACTIVITYBAR_PART)) { - return [dimension]; + if (dim1 instanceof Dimension) { + return [dim1]; + } + + return; } // Pass to super - const sizes = super.layout(dimension); + const sizes = super.layout(dim1 instanceof Dimension ? dim1 : new Dimension(dim1, dim2)); this.dimension = sizes[1]; @@ -353,9 +371,11 @@ export class ActivitybarPart extends Part { // adjust height for global actions showing availableHeight -= (this.globalActionBar.items.length * ActivitybarPart.ACTION_HEIGHT); } - this.compositeBar.layout(new Dimension(dimension.width, availableHeight)); + this.compositeBar.layout(new Dimension(dim1 instanceof Dimension ? dim1.width : dim1, availableHeight)); - return sizes; + if (dim1 instanceof Dimension) { + return sizes; + } } private onDidStorageChange(e: IWorkspaceStorageChangeEvent): void { @@ -469,4 +489,10 @@ export class ActivitybarPart extends Part { const viewContainerRegistry = Registry.as(ViewContainerExtensions.ViewContainersRegistry); return viewContainerRegistry.get(viewletId); } + + toJSON(): object { + return { + type: Parts.ACTIVITYBAR_PART + }; + } } diff --git a/src/vs/workbench/browser/parts/activitybar/media/activityaction.css b/src/vs/workbench/browser/parts/activitybar/media/activityaction.css index 3f07bfcb0d0..362ea4a70c8 100644 --- a/src/vs/workbench/browser/parts/activitybar/media/activityaction.css +++ b/src/vs/workbench/browser/parts/activitybar/media/activityaction.css @@ -3,13 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item { +.monaco-workbench .activitybar > .content .monaco-action-bar .action-item { display: block; position: relative; padding: 5px 0; } -.monaco-workbench > .activitybar > .content .monaco-action-bar .action-label { +.monaco-workbench .activitybar > .content .monaco-action-bar .action-label { display: flex; overflow: hidden; height: 40px; @@ -20,7 +20,7 @@ font-size: 15px; } -.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item:focus:before { +.monaco-workbench .activitybar > .content .monaco-action-bar .action-item:focus:before { content: ""; position: absolute; top: 9px; @@ -29,19 +29,19 @@ border-left: 2px solid; } -.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item.clicked:focus:before { +.monaco-workbench .activitybar > .content .monaco-action-bar .action-item.clicked:focus:before { border-left: none !important; /* no focus feedback when using mouse */ } -.monaco-workbench > .activitybar.left > .content .monaco-action-bar .action-item:focus:before { +.monaco-workbench .activitybar.left > .content .monaco-action-bar .action-item:focus:before { left: 1px; } -.monaco-workbench > .activitybar.right > .content .monaco-action-bar .action-item:focus:before { +.monaco-workbench .activitybar.right > .content .monaco-action-bar .action-item:focus:before { right: 1px; } -.monaco-workbench > .activitybar > .content .monaco-action-bar .badge { +.monaco-workbench .activitybar > .content .monaco-action-bar .badge { position: absolute; top: 5px; left: 0; @@ -50,7 +50,7 @@ height: 40px; } -.monaco-workbench > .activitybar > .content .monaco-action-bar .badge .badge-content { +.monaco-workbench .activitybar > .content .monaco-action-bar .badge .badge-content { position: absolute; top: 20px; right: 8px; @@ -65,13 +65,13 @@ /* Right aligned */ -.monaco-workbench > .activitybar.right > .content .monaco-action-bar .action-label { +.monaco-workbench .activitybar.right > .content .monaco-action-bar .action-label { margin-left: 0; padding: 0 50px 0 0; background-position: calc(100% - 9px) center; } -.monaco-workbench > .activitybar.right > .content .monaco-action-bar .badge { +.monaco-workbench .activitybar.right > .content .monaco-action-bar .badge { left: auto; right: 0; } \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css b/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css index 5a92b2e1f52..3b72852b0fc 100644 --- a/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css +++ b/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css @@ -3,23 +3,23 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-workbench > .part.activitybar { +.monaco-workbench .part.activitybar { width: 50px; } -.monaco-workbench > .activitybar > .content { +.monaco-workbench .activitybar > .content { height: 100%; display: flex; flex-direction: column; justify-content: space-between; } -.monaco-workbench > .activitybar > .content .monaco-action-bar { +.monaco-workbench .activitybar > .content .monaco-action-bar { text-align: left; background-color: inherit; } -.monaco-workbench > .activitybar .action-item:focus { +.monaco-workbench .activitybar .action-item:focus { outline: 0 !important; /* activity bar indicates focus custom */ } diff --git a/src/vs/workbench/browser/parts/compositePart.ts b/src/vs/workbench/browser/parts/compositePart.ts index 1865d8b1d70..8064787e489 100644 --- a/src/vs/workbench/browser/parts/compositePart.ts +++ b/src/vs/workbench/browser/parts/compositePart.ts @@ -464,10 +464,11 @@ export abstract class CompositePart extends Part { return AnchorAlignment.RIGHT; } - layout(dimension: Dimension): Dimension[] { - + layout(dimension: Dimension): Dimension[]; + layout(width: number, height: number): void; + layout(dim1: Dimension | number, dim2?: number): Dimension[] | void { // Pass to super - const sizes = super.layout(dimension); + const sizes = super.layout(dim1 instanceof Dimension ? dim1 : new Dimension(dim1, dim2!)); // Pass Contentsize to composite this.contentAreaSize = sizes[1]; @@ -475,7 +476,9 @@ export abstract class CompositePart extends Part { this.activeComposite.layout(this.contentAreaSize); } - return sizes; + if (dim1 instanceof Dimension) { + return sizes; + } } protected removeComposite(compositeId: string): boolean { diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index 9bb84cf7d49..3a5fdebc6a3 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -1460,7 +1460,7 @@ registerThemingParticipant((theme, collector, environment) => { // Letterpress const letterpress = `resources/letterpress${theme.type === 'dark' ? '-dark' : theme.type === 'hc' ? '-hc' : ''}.svg`; collector.addRule(` - .monaco-workbench > .part.editor > .content .editor-group-container.empty .editor-group-letterpress { + .monaco-workbench .part.editor > .content .editor-group-container.empty .editor-group-letterpress { background-image: url('${URI.file(join(environment.appRoot, letterpress)).toString()}') } `); @@ -1469,20 +1469,20 @@ registerThemingParticipant((theme, collector, environment) => { const focusedEmptyGroupBorder = theme.getColor(EDITOR_GROUP_FOCUSED_EMPTY_BORDER); if (focusedEmptyGroupBorder) { collector.addRule(` - .monaco-workbench > .part.editor > .content:not(.empty) .editor-group-container.empty.active:focus { + .monaco-workbench .part.editor > .content:not(.empty) .editor-group-container.empty.active:focus { outline-width: 1px; outline-color: ${focusedEmptyGroupBorder}; outline-offset: -2px; outline-style: solid; } - .monaco-workbench > .part.editor > .content.empty .editor-group-container.empty.active:focus { + .monaco-workbench .part.editor > .content.empty .editor-group-container.empty.active:focus { outline: none; /* never show outline for empty group if it is the last */ } `); } else { collector.addRule(` - .monaco-workbench > .part.editor > .content .editor-group-container.empty.active:focus { + .monaco-workbench .part.editor > .content .editor-group-container.empty.active:focus { outline: none; /* disable focus outline unless active empty group border is defined */ } `); diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index adfe1690863..7a1fbaf6c46 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -11,7 +11,7 @@ import { Event, Emitter, Relay } from 'vs/base/common/event'; import { contrastBorder, editorBackground } from 'vs/platform/theme/common/colorRegistry'; import { GroupDirection, IAddGroupOptions, GroupsArrangement, GroupOrientation, IMergeGroupOptions, MergeGroupMode, ICopyEditorOptions, GroupsOrder, GroupChangeKind, GroupLocation, IFindGroupScope, EditorGroupLayout, GroupLayoutArgument } from 'vs/workbench/services/group/common/editorGroupsService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { Direction, SerializableGrid, Sizing, ISerializedGrid, Orientation, GridBranchNode, isGridBranchNode, GridNode, createSerializedGrid, Grid } from 'vs/base/browser/ui/grid/grid'; +import { Direction, SerializableGrid, Sizing, ISerializedGrid, Orientation, GridBranchNode, isGridBranchNode, GridNode, createSerializedGrid, Grid, ISerializableView } from 'vs/base/browser/ui/grid/grid'; import { GroupIdentifier, IWorkbenchEditorConfiguration } from 'vs/workbench/common/editor'; import { values } from 'vs/base/common/map'; import { EDITOR_GROUP_BORDER, EDITOR_PANE_BACKGROUND } from 'vs/workbench/common/theme'; @@ -28,8 +28,9 @@ import { EditorDropTarget } from 'vs/workbench/browser/parts/editor/editorDropTa import { localize } from 'vs/nls'; import { Color } from 'vs/base/common/color'; import { CenteredViewLayout } from 'vs/base/browser/ui/centered/centeredViewLayout'; -import { IView, orthogonal } from 'vs/base/browser/ui/grid/gridview'; +import { IView, orthogonal, LayoutPriority } from 'vs/base/browser/ui/grid/gridview'; import { onUnexpectedError } from 'vs/base/common/errors'; +import { Parts } from 'vs/workbench/services/part/common/partService'; interface IEditorPartUIState { serializedGrid: ISerializedGrid; @@ -79,7 +80,7 @@ class GridWidgetView implements IView { } } -export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditorGroupsAccessor { +export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditorGroupsAccessor, ISerializableView { _serviceBrand: any; @@ -132,6 +133,13 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor private _whenRestored: Promise; private whenRestoredResolve: () => void; + element: HTMLElement; + + private _onDidChange = new Emitter<{ width: number; height: number; }>(); + readonly onDidChange = this._onDidChange.event; + + priority: LayoutPriority = LayoutPriority.High; + constructor( id: string, private restorePreviousState: boolean, @@ -779,6 +787,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor createContentArea(parent: HTMLElement): HTMLElement { // Container + this.element = parent; this.container = document.createElement('div'); addClass(this.container, 'content'); parent.appendChild(this.container); @@ -946,12 +955,16 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor return this.groupViews.size === 1 && this._activeGroup.isEmpty(); } - layout(dimension: Dimension): Dimension[] { - const sizes = super.layout(dimension); + layout(dimension: Dimension): Dimension[]; + layout(width: number, height: number): void; + layout(dim1: Dimension | number, dim2?: number): Dimension[] | void { + const sizes = super.layout(dim1 instanceof Dimension ? dim1 : new Dimension(dim1, dim2)); this.doLayout(sizes[1]); - return sizes; + if (dim1 instanceof Dimension) { + return sizes; + } } private doLayout(dimension: Dimension): void { @@ -1007,4 +1020,10 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor } //#endregion + + toJSON(): object { + return { + type: Parts.EDITOR_PART + }; + } } diff --git a/src/vs/workbench/browser/parts/editor/media/breadcrumbscontrol.css b/src/vs/workbench/browser/parts/editor/media/breadcrumbscontrol.css index 2a5ce2936fe..d3850e29307 100644 --- a/src/vs/workbench/browser/parts/editor/media/breadcrumbscontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/breadcrumbscontrol.css @@ -3,17 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-workbench>.part.editor>.content .editor-group-container .breadcrumbs-control.hidden { +.monaco-workbench .part.editor>.content .editor-group-container .breadcrumbs-control.hidden { display: none; } -.monaco-workbench>.part.editor>.content .editor-group-container .breadcrumbs-control .monaco-breadcrumb-item.selected .monaco-icon-label, -.monaco-workbench>.part.editor>.content .editor-group-container .breadcrumbs-control .monaco-breadcrumb-item.focused .monaco-icon-label { +.monaco-workbench .part.editor>.content .editor-group-container .breadcrumbs-control .monaco-breadcrumb-item.selected .monaco-icon-label, +.monaco-workbench .part.editor>.content .editor-group-container .breadcrumbs-control .monaco-breadcrumb-item.focused .monaco-icon-label { text-decoration-line: underline; } -.monaco-workbench>.part.editor>.content .editor-group-container .breadcrumbs-control .monaco-breadcrumb-item.selected .hint-more, -.monaco-workbench>.part.editor>.content .editor-group-container .breadcrumbs-control .monaco-breadcrumb-item.focused .hint-more { +.monaco-workbench .part.editor>.content .editor-group-container .breadcrumbs-control .monaco-breadcrumb-item.selected .hint-more, +.monaco-workbench .part.editor>.content .editor-group-container .breadcrumbs-control .monaco-breadcrumb-item.focused .hint-more { text-decoration-line: underline; } diff --git a/src/vs/workbench/browser/parts/editor/media/editorgroupview.css b/src/vs/workbench/browser/parts/editor/media/editorgroupview.css index bd0c879f1c1..f372df090e5 100644 --- a/src/vs/workbench/browser/parts/editor/media/editorgroupview.css +++ b/src/vs/workbench/browser/parts/editor/media/editorgroupview.css @@ -5,26 +5,26 @@ /* Container */ -.monaco-workbench > .part.editor > .content .editor-group-container { +.monaco-workbench .part.editor > .content .editor-group-container { height: 100%; } -.monaco-workbench > .part.editor > .content .editor-group-container.empty { +.monaco-workbench .part.editor > .content .editor-group-container.empty { opacity: 0.5; /* dimmed to indicate inactive state */ } -.monaco-workbench > .part.editor > .content .editor-group-container.empty.active, -.monaco-workbench > .part.editor > .content .editor-group-container.empty.dragged-over { +.monaco-workbench .part.editor > .content .editor-group-container.empty.active, +.monaco-workbench .part.editor > .content .editor-group-container.empty.dragged-over { opacity: 1; /* indicate active/dragged-over group through undimmed state */ } /* Letterpress */ -.monaco-workbench > .part.editor > .content .editor-group-container > .editor-group-letterpress { +.monaco-workbench .part.editor > .content .editor-group-container > .editor-group-letterpress { display: none; /* only visible when empty */ } -.monaco-workbench > .part.editor > .content .editor-group-container.empty > .editor-group-letterpress { +.monaco-workbench .part.editor > .content .editor-group-container.empty > .editor-group-letterpress { display: block; margin: auto; width: 100%; @@ -35,25 +35,25 @@ background-size: 70% 70%; } -.monaco-workbench > .part.editor > .content.empty .editor-group-container.empty > .editor-group-letterpress { +.monaco-workbench .part.editor > .content.empty .editor-group-container.empty > .editor-group-letterpress { background-size: 100% 100%; /* larger for empty editor part */ height: 100%; /* no toolbar in this case */ } /* Title */ -.monaco-workbench > .part.editor > .content .editor-group-container > .title { +.monaco-workbench .part.editor > .content .editor-group-container > .title { position: relative; box-sizing: border-box; overflow: hidden; } -.monaco-workbench > .part.editor > .content .editor-group-container > .title:not(.tabs) { +.monaco-workbench .part.editor > .content .editor-group-container > .title:not(.tabs) { display: flex; /* when tabs are not shown, use flex layout */ flex-wrap: nowrap; } -.monaco-workbench > .part.editor > .content .editor-group-container > .title.title-border-bottom::after { +.monaco-workbench .part.editor > .content .editor-group-container > .title.title-border-bottom::after { content: ''; position: absolute; bottom: 0; @@ -65,21 +65,21 @@ height: 1px; } -.monaco-workbench > .part.editor > .content .editor-group-container.empty > .title { +.monaco-workbench .part.editor > .content .editor-group-container.empty > .title { display: none; } /* Toolbar */ -.monaco-workbench > .part.editor > .content .editor-group-container > .editor-group-container-toolbar { +.monaco-workbench .part.editor > .content .editor-group-container > .editor-group-container-toolbar { display: none; } -.monaco-workbench > .part.editor > .content:not(.empty) .editor-group-container.empty > .editor-group-container-toolbar { +.monaco-workbench .part.editor > .content:not(.empty) .editor-group-container.empty > .editor-group-container-toolbar { display: block; } -.monaco-workbench > .part.editor > .content .editor-group-container > .editor-group-container-toolbar .action-label { +.monaco-workbench .part.editor > .content .editor-group-container > .editor-group-container-toolbar .action-label { display: block; height: 35px; line-height: 35px; @@ -89,26 +89,26 @@ background-repeat: no-repeat; } -.vs .monaco-workbench > .part.editor > .content .editor-group-container > .editor-group-container-toolbar .close-editor-group { +.vs .monaco-workbench .part.editor > .content .editor-group-container > .editor-group-container-toolbar .close-editor-group { background-image: url('close-big.svg'); } -.vs-dark .monaco-workbench > .part.editor > .content .editor-group-container > .editor-group-container-toolbar .close-editor-group, -.hc-black .monaco-workbench > .part.editor > .content .editor-group-container > .editor-group-container-toolbar .close-editor-group { +.vs-dark .monaco-workbench .part.editor > .content .editor-group-container > .editor-group-container-toolbar .close-editor-group, +.hc-black .monaco-workbench .part.editor > .content .editor-group-container > .editor-group-container-toolbar .close-editor-group { background-image: url('close-big-inverse.svg'); } /* Editor */ -.monaco-workbench > .part.editor > .content .editor-group-container.empty > .editor-container { +.monaco-workbench .part.editor > .content .editor-group-container.empty > .editor-container { display: none; } -.monaco-workbench > .part.editor > .content .editor-group-container > .editor-container > .editor-instance { +.monaco-workbench .part.editor > .content .editor-group-container > .editor-container > .editor-instance { height: 100%; } -.monaco-workbench > .part.editor > .content .grid-view-container { +.monaco-workbench .part.editor > .content .grid-view-container { width: 100%; height: 100%; } diff --git a/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css b/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css index 28760657fba..6b309377039 100644 --- a/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css @@ -5,7 +5,7 @@ /* Title Label */ -.monaco-workbench > .part.editor > .content .editor-group-container > .title > .label-container { +.monaco-workbench .part.editor > .content .editor-group-container > .title > .label-container { height: 35px; display: flex; justify-content: flex-start; @@ -14,7 +14,7 @@ flex: auto; } -.monaco-workbench > .part.editor > .content .editor-group-container > .title .title-label { +.monaco-workbench .part.editor > .content .editor-group-container > .title .title-label { line-height: 35px; overflow: hidden; text-overflow: ellipsis; @@ -22,31 +22,31 @@ padding-left: 20px; } -.monaco-workbench > .part.editor > .content .editor-group-container > .title.breadcrumbs .no-tabs.title-label { +.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .no-tabs.title-label { flex: none; } -.monaco-workbench > .part.editor > .content .editor-group-container > .title .monaco-icon-label::before { +.monaco-workbench .part.editor > .content .editor-group-container > .title .monaco-icon-label::before { height: 35px; /* tweak the icon size of the editor labels when icons are enabled */ } /* Breadcrumbs */ -.monaco-workbench > .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control { +.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control { flex: 1 50%; overflow: hidden; margin-left: .45em; } -.monaco-workbench > .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item { +.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item { font-size: 0.9em; } -.monaco-workbench > .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control.preview .monaco-breadcrumb-item { +.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control.preview .monaco-breadcrumb-item { font-style: italic; } -.monaco-workbench > .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item::before { +.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item::before { content: '/'; opacity: 1; height: inherit; @@ -58,31 +58,31 @@ content: '\\'; } -.monaco-workbench > .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item.root_folder::before, -.monaco-workbench > .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item.root_folder + .monaco-breadcrumb-item::before, -.monaco-workbench > .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control.relative-path .monaco-breadcrumb-item:nth-child(2)::before { +.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item.root_folder::before, +.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item.root_folder + .monaco-breadcrumb-item::before, +.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control.relative-path .monaco-breadcrumb-item:nth-child(2)::before { /* workspace folder, item following workspace folder, or relative path -> hide first seperator */ display: none; } -.monaco-workbench > .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item.root_folder::after { +.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item.root_folder::after { /* use dot separator for workspace folder */ content: '\00a0•\00a0'; padding: 0px; } -.monaco-workbench > .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item:last-child { +.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item:last-child { padding-right: 4px; /* does not have trailing separator*/ } /* Title Actions */ -.monaco-workbench > .part.editor > .content .editor-group-container > .title .title-actions { +.monaco-workbench .part.editor > .content .editor-group-container > .title .title-actions { display: flex; flex: initial; opacity: 0.5; height: 35px; } -.monaco-workbench > .part.editor > .content .editor-group-container.active > .title .title-actions { +.monaco-workbench .part.editor > .content .editor-group-container.active > .title .title-actions { opacity: 1; } diff --git a/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css b/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css index aa867509495..1e413fdae98 100644 --- a/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css @@ -5,37 +5,37 @@ /* Title Container */ -.monaco-workbench > .part.editor > .content .editor-group-container > .title.tabs > .tabs-and-actions-container { +.monaco-workbench .part.editor > .content .editor-group-container > .title.tabs > .tabs-and-actions-container { display: flex; } -.monaco-workbench > .part.editor > .content .editor-group-container > .title.tabs > .tabs-and-actions-container > .monaco-scrollable-element { +.monaco-workbench .part.editor > .content .editor-group-container > .title.tabs > .tabs-and-actions-container > .monaco-scrollable-element { flex: 1; } -.monaco-workbench > .part.editor > .content .editor-group-container > .title.tabs > .tabs-and-actions-container > .monaco-scrollable-element .scrollbar { +.monaco-workbench .part.editor > .content .editor-group-container > .title.tabs > .tabs-and-actions-container > .monaco-scrollable-element .scrollbar { z-index: 3; /* on top of tabs */ cursor: default; } /* Tabs Container */ -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container { display: flex; height: 35px; } -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container.scroll { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container.scroll { overflow: scroll !important; } -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container::-webkit-scrollbar { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container::-webkit-scrollbar { display: none; } /* Tab */ -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab { position: relative; display: flex; white-space: nowrap; @@ -45,45 +45,45 @@ padding-left: 10px; } -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.has-icon-theme.close-button-right, -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.has-icon-theme.close-button-off { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.has-icon-theme.close-button-right, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.has-icon-theme.close-button-off { padding-left: 5px; /* reduce padding when we show icons and are in shrinking mode and tab close button is not left */ } -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fit { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fit { width: 120px; min-width: fit-content; } -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink { min-width: 60px; flex-basis: 0; /* all tabs are even */ flex-grow: 1; /* all tabs grow even */ max-width: fit-content; } -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.close-button-left::after, -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.close-button-off::after { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.close-button-left::after, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.close-button-off::after { content: ''; display: flex; flex: 0; width: 5px; /* Reserve space to hide tab fade when close button is left or off (fixes https://github.com/Microsoft/vscode/issues/45728) */ } -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.close-button-left { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.close-button-left { min-width: 80px; /* make more room for close button when it shows to the left */ padding-right: 5px; /* we need less room when sizing is shrink */ } -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dragged { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dragged { will-change: transform; /* forces tab to be drawn on a separate layer (fixes https://github.com/Microsoft/vscode/issues/18733) */ } -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dragged-over div { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dragged-over div { pointer-events: none; /* prevents cursor flickering (fixes https://github.com/Microsoft/vscode/issues/38753) */ } -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-left { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-left { flex-direction: row-reverse; padding-left: 0; padding-right: 10px; @@ -91,14 +91,14 @@ /* Tab border top/bottom */ -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab > .tab-border-top-container, -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab > .tab-border-bottom-container { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab > .tab-border-top-container, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab > .tab-border-bottom-container { display: none; /* hidden by default until a color is provided (see below) */ } -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active.tab-border-top > .tab-border-top-container, -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active.tab-border-bottom > .tab-border-bottom-container, -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty-border-top > .tab-border-top-container { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active.tab-border-top > .tab-border-top-container, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active.tab-border-bottom > .tab-border-bottom-container, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty-border-top > .tab-border-top-container { display: block; position: absolute; left: 0; @@ -107,19 +107,19 @@ width: 100%; } -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active.tab-border-top > .tab-border-top-container { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active.tab-border-top > .tab-border-top-container { top: 0; height: 1px; background-color: var(--tab-border-top-color); } -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active.tab-border-bottom > .tab-border-bottom-container { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active.tab-border-bottom > .tab-border-bottom-container { bottom: 0; height: 1px; background-color: var(--tab-border-bottom-color); } -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty-border-top > .tab-border-top-container { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty-border-top > .tab-border-top-container { top: 0; height: 2px; background-color: var(--tab-dirty-border-top-color); @@ -127,16 +127,16 @@ /* Tab Label */ -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab .tab-label { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab .tab-label { margin-top: auto; margin-bottom: auto; } -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink .tab-label { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink .tab-label { position: relative; } -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink > .tab-label::after { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink > .tab-label::after { content: ''; position: absolute; right: 0; @@ -146,66 +146,66 @@ padding: 0; } -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink:focus > .tab-label::after { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink:focus > .tab-label::after { opacity: 0; /* when tab has the focus this shade breaks the tab border (fixes https://github.com/Microsoft/vscode/issues/57819) */ } -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fit .monaco-icon-label, -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fit .monaco-icon-label > .monaco-icon-label-description-container { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fit .monaco-icon-label, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fit .monaco-icon-label > .monaco-icon-label-description-container { overflow: visible; /* fixes https://github.com/Microsoft/vscode/issues/20182 */ } -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink > .monaco-icon-label > .monaco-icon-label-description-container { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink > .monaco-icon-label > .monaco-icon-label-description-container { text-overflow: clip; } -.hc-black .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink > .monaco-icon-label > .monaco-icon-label-description-container { +.hc-black .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink > .monaco-icon-label > .monaco-icon-label-description-container { text-overflow: ellipsis; } -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab .monaco-icon-label::before { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab .monaco-icon-label::before { height: 16px; /* tweak the icon size of the editor labels when icons are enabled */ } /* Tab Close */ -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab > .tab-close { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab > .tab-close { margin-top: auto; margin-bottom: auto; width: 28px; } -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-right.sizing-shrink > .tab-close { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-right.sizing-shrink > .tab-close { flex: 0; overflow: hidden; /* let the close button be pushed out of view when sizing is set to shrink to make more room... */ } -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty.close-button-right.sizing-shrink > .tab-close, -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-right.sizing-shrink:hover > .tab-close, -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-right.sizing-shrink > .tab-close:focus-within { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty.close-button-right.sizing-shrink > .tab-close, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-right.sizing-shrink:hover > .tab-close, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-right.sizing-shrink > .tab-close:focus-within { overflow: visible; /* ...but still show the close button on hover, focus and when dirty */ } -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-off > .tab-close { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-off > .tab-close { display: none; /* hide the close action bar when we are configured to hide it */ } -.monaco-workbench > .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab.active > .tab-close .action-label, /* always show it for active tab */ -.monaco-workbench > .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab > .tab-close .action-label:focus, /* always show it on focus */ -.monaco-workbench > .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab:hover > .tab-close .action-label, /* always show it on hover */ -.monaco-workbench > .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab.active:hover > .tab-close .action-label, /* always show it on hover */ -.monaco-workbench > .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab.dirty > .tab-close .action-label { /* always show it for dirty tabs */ +.monaco-workbench .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab.active > .tab-close .action-label, /* always show it for active tab */ +.monaco-workbench .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab > .tab-close .action-label:focus, /* always show it on focus */ +.monaco-workbench .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab:hover > .tab-close .action-label, /* always show it on hover */ +.monaco-workbench .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab.active:hover > .tab-close .action-label, /* always show it on hover */ +.monaco-workbench .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab.dirty > .tab-close .action-label { /* always show it for dirty tabs */ opacity: 1; } -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active > .tab-close .action-label, /* show dimmed for inactive group */ -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active:hover > .tab-close .action-label, /* show dimmed for inactive group */ -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty > .tab-close .action-label, /* show dimmed for inactive group */ -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab:hover > .tab-close .action-label { /* show dimmed for inactive group */ +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active > .tab-close .action-label, /* show dimmed for inactive group */ +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active:hover > .tab-close .action-label, /* show dimmed for inactive group */ +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty > .tab-close .action-label, /* show dimmed for inactive group */ +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab:hover > .tab-close .action-label { /* show dimmed for inactive group */ opacity: 0.5; } -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab > .tab-close .action-label { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab > .tab-close .action-label { opacity: 0; display: block; height: 16px; @@ -216,53 +216,53 @@ margin-right: 0.5em; } -.vs .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty .close-editor-action { +.vs .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty .close-editor-action { background: url('close-dirty.svg') center center no-repeat; } -.vs-dark .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty .close-editor-action, -.hc-black .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty .close-editor-action { +.vs-dark .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty .close-editor-action, +.hc-black .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty .close-editor-action { background: url('close-dirty-inverse.svg') center center no-repeat; } -.vs .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty .close-editor-action:hover { +.vs .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty .close-editor-action:hover { background: url('close.svg') center center no-repeat; } -.vs-dark .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty .close-editor-action:hover, -.hc-black .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty .close-editor-action:hover { +.vs-dark .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty .close-editor-action:hover, +.hc-black .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty .close-editor-action:hover { background: url('close-inverse.svg') center center no-repeat; } /* No Tab Close Button */ -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-off { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-off { padding-right: 10px; /* give a little bit more room if close button is off */ } -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.close-button-off { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.close-button-off { padding-right: 5px; /* we need less room when sizing is shrink */ } -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-off.dirty:not(.dirty-border-top) { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-off.dirty:not(.dirty-border-top) { background-repeat: no-repeat; background-position-y: center; background-position-x: calc(100% - 6px); /* to the right of the tab label */ padding-right: 28px; /* make room for dirty indication when we are running without close button */ } -.vs .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-off.dirty:not(.dirty-border-top) { +.vs .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-off.dirty:not(.dirty-border-top) { background-image: url('close-dirty.svg'); } -.vs-dark .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-off.dirty:not(.dirty-border-top), -.hc-black .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-off.dirty { +.vs-dark .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-off.dirty:not(.dirty-border-top), +.hc-black .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-off.dirty { background-image: url('close-dirty-inverse.svg'); } /* Editor Actions */ -.monaco-workbench > .part.editor > .content .editor-group-container > .title .editor-actions { +.monaco-workbench .part.editor > .content .editor-group-container > .title .editor-actions { cursor: default; flex: initial; padding-left: 4px; @@ -271,30 +271,30 @@ /* Breadcrumbs */ -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control { flex: 1 100%; height: 22px; cursor: default; } -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control .monaco-icon-label { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control .monaco-icon-label { height: 22px; line-height: 22px; } -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control .monaco-icon-label::before { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control .monaco-icon-label::before { height: 22px; /* tweak the icon size of the editor labels when icons are enabled */ } -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item { max-width: 80%; } -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item::before { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item::before { min-width: 16px; height: 22px; } -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item:last-child { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item:last-child { padding-right: 8px; } diff --git a/src/vs/workbench/browser/parts/editor/media/titlecontrol.css b/src/vs/workbench/browser/parts/editor/media/titlecontrol.css index 41e78a153f2..42358c52102 100644 --- a/src/vs/workbench/browser/parts/editor/media/titlecontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/titlecontrol.css @@ -5,31 +5,31 @@ /* Editor Label */ -.monaco-workbench > .part.editor > .content .editor-group-container > .title .title-label, -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab .tab-label { +.monaco-workbench .part.editor > .content .editor-group-container > .title .title-label, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab .tab-label { white-space: nowrap; flex: 1; } -.monaco-workbench > .part.editor > .content .editor-group-container > .title .title-label a, -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab .tab-label a { +.monaco-workbench .part.editor > .content .editor-group-container > .title .title-label a, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab .tab-label a { text-decoration: none; font-size: 13px; } -.monaco-workbench > .part.editor > .content .editor-group-container > .title .monaco-icon-label::before, -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab .monaco-icon-label::before, -.monaco-workbench > .part.editor > .content .editor-group-container > .title .title-label a, -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab .tab-label a, -.monaco-workbench > .part.editor > .content .editor-group-container > .title .title-label h2, -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab .tab-label span { +.monaco-workbench .part.editor > .content .editor-group-container > .title .monaco-icon-label::before, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab .monaco-icon-label::before, +.monaco-workbench .part.editor > .content .editor-group-container > .title .title-label a, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab .tab-label a, +.monaco-workbench .part.editor > .content .editor-group-container > .title .title-label h2, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab .tab-label span { cursor: pointer; } /* Title Actions */ -.monaco-workbench > .part.editor > .content .editor-group-container > .title .title-actions .action-label, -.monaco-workbench > .part.editor > .content .editor-group-container > .title .editor-actions .action-label { +.monaco-workbench .part.editor > .content .editor-group-container > .title .title-actions .action-label, +.monaco-workbench .part.editor > .content .editor-group-container > .title .editor-actions .action-label { display: block; height: 35px; line-height: 35px; @@ -39,29 +39,29 @@ background-repeat: no-repeat; } -.hc-black .monaco-workbench > .part.editor > .content .editor-group-container > .title .title-actions .action-label, -.hc-black .monaco-workbench > .part.editor > .content .editor-group-container > .title .editor-actions .action-label { +.hc-black .monaco-workbench .part.editor > .content .editor-group-container > .title .title-actions .action-label, +.hc-black .monaco-workbench .part.editor > .content .editor-group-container > .title .editor-actions .action-label { line-height: initial; } -.monaco-workbench > .part.editor > .content .editor-group-container > .title .editor-actions .action-label .label, -.monaco-workbench > .part.editor > .content .editor-group-container > .title .title-actions .action-label .label { +.monaco-workbench .part.editor > .content .editor-group-container > .title .editor-actions .action-label .label, +.monaco-workbench .part.editor > .content .editor-group-container > .title .title-actions .action-label .label { display: none; } /* Drag Cursor */ -.monaco-workbench > .part.editor > .content .editor-group-container > .title { +.monaco-workbench .part.editor > .content .editor-group-container > .title { cursor: -webkit-grab; } /* Actions */ -.monaco-workbench > .part.editor > .content .editor-group-container > .title .close-editor-action { +.monaco-workbench .part.editor > .content .editor-group-container > .title .close-editor-action { background: url('close.svg') center center no-repeat; } -.vs-dark .monaco-workbench > .part.editor > .content .editor-group-container > .title .close-editor-action, -.hc-black .monaco-workbench > .part.editor > .content .editor-group-container > .title .close-editor-action { +.vs-dark .monaco-workbench .part.editor > .content .editor-group-container > .title .close-editor-action, +.hc-black .monaco-workbench .part.editor > .content .editor-group-container > .title .close-editor-action { background: url('close-inverse.svg') center center no-repeat; } diff --git a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts index 3f531ff7b4d..5f957c221a6 100644 --- a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts @@ -1128,21 +1128,21 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const activeContrastBorderColor = theme.getColor(activeContrastBorder); if (activeContrastBorderColor) { collector.addRule(` - .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active, - .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active:hover { + .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active, + .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active:hover { outline: 1px solid; outline-offset: -5px; } - .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab:hover { + .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab:hover { outline: 1px dashed; outline-offset: -5px; } - .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active > .tab-close .action-label, - .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active:hover > .tab-close .action-label, - .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty > .tab-close .action-label, - .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab:hover > .tab-close .action-label { + .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active > .tab-close .action-label, + .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active:hover > .tab-close .action-label, + .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty > .tab-close .action-label, + .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab:hover > .tab-close .action-label { opacity: 1 !important; } `); @@ -1152,7 +1152,7 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const tabHoverBackground = theme.getColor(TAB_HOVER_BACKGROUND); if (tabHoverBackground) { collector.addRule(` - .monaco-workbench > .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab:hover { + .monaco-workbench .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab:hover { background-color: ${tabHoverBackground} !important; } `); @@ -1161,7 +1161,7 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const tabUnfocusedHoverBackground = theme.getColor(TAB_UNFOCUSED_HOVER_BACKGROUND); if (tabUnfocusedHoverBackground) { collector.addRule(` - .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab:hover { + .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab:hover { background-color: ${tabUnfocusedHoverBackground} !important; } `); @@ -1171,7 +1171,7 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const tabHoverBorder = theme.getColor(TAB_HOVER_BORDER); if (tabHoverBorder) { collector.addRule(` - .monaco-workbench > .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab:hover { + .monaco-workbench .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab:hover { box-shadow: ${tabHoverBorder} 0 -1px inset !important; } `); @@ -1180,7 +1180,7 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const tabUnfocusedHoverBorder = theme.getColor(TAB_UNFOCUSED_HOVER_BORDER); if (tabUnfocusedHoverBorder) { collector.addRule(` - .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab:hover { + .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab:hover { box-shadow: ${tabUnfocusedHoverBorder} 0 -1px inset !important; } `); @@ -1208,12 +1208,12 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const adjustedColor = tabHoverBackground.flatten(adjustedTabBackground); const adjustedColorDrag = tabHoverBackground.flatten(adjustedTabDragBackground); collector.addRule(` - .monaco-workbench > .part.editor > .content:not(.dragged-over) .editor-group-container.active > .title .tabs-container > .tab.sizing-shrink:not(.dragged):hover > .tab-label::after { + .monaco-workbench .part.editor > .content:not(.dragged-over) .editor-group-container.active > .title .tabs-container > .tab.sizing-shrink:not(.dragged):hover > .tab-label::after { background: linear-gradient(to left, ${adjustedColor}, transparent) !important; } - .monaco-workbench > .part.editor > .content.dragged-over .editor-group-container.active > .title .tabs-container > .tab.sizing-shrink:not(.dragged):hover > .tab-label::after { + .monaco-workbench .part.editor > .content.dragged-over .editor-group-container.active > .title .tabs-container > .tab.sizing-shrink:not(.dragged):hover > .tab-label::after { background: linear-gradient(to left, ${adjustedColorDrag}, transparent) !important; } `); @@ -1224,11 +1224,11 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const adjustedColor = tabUnfocusedHoverBackground.flatten(adjustedTabBackground); const adjustedColorDrag = tabUnfocusedHoverBackground.flatten(adjustedTabDragBackground); collector.addRule(` - .monaco-workbench > .part.editor > .content:not(.dragged-over) .editor-group-container > .title .tabs-container > .tab.sizing-shrink:not(.dragged):hover > .tab-label::after { + .monaco-workbench .part.editor > .content:not(.dragged-over) .editor-group-container > .title .tabs-container > .tab.sizing-shrink:not(.dragged):hover > .tab-label::after { background: linear-gradient(to left, ${adjustedColor}, transparent) !important; } - .monaco-workbench > .part.editor > .content.dragged-over .editor-group-container > .title .tabs-container > .tab.sizing-shrink:not(.dragged):hover > .tab-label::after { + .monaco-workbench .part.editor > .content.dragged-over .editor-group-container > .title .tabs-container > .tab.sizing-shrink:not(.dragged):hover > .tab-label::after { background: linear-gradient(to left, ${adjustedColorDrag}, transparent) !important; } `); @@ -1238,8 +1238,8 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { if (editorDragAndDropBackground && adjustedTabDragBackground) { const adjustedColorDrag = editorDragAndDropBackground.flatten(adjustedTabDragBackground); collector.addRule(` - .monaco-workbench > .part.editor > .content.dragged-over .editor-group-container.active > .title .tabs-container > .tab.sizing-shrink.dragged-over:not(.active):not(.dragged) > .tab-label::after, - .monaco-workbench > .part.editor > .content.dragged-over .editor-group-container:not(.active) > .title .tabs-container > .tab.sizing-shrink.dragged-over:not(.dragged) > .tab-label::after { + .monaco-workbench .part.editor > .content.dragged-over .editor-group-container.active > .title .tabs-container > .tab.sizing-shrink.dragged-over:not(.active):not(.dragged) > .tab-label::after, + .monaco-workbench .part.editor > .content.dragged-over .editor-group-container:not(.active) > .title .tabs-container > .tab.sizing-shrink.dragged-over:not(.dragged) > .tab-label::after { background: linear-gradient(to left, ${adjustedColorDrag}, transparent) !important; } `); @@ -1251,11 +1251,11 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const adjustedColor = tabActiveBackground.flatten(adjustedTabBackground); const adjustedColorDrag = tabActiveBackground.flatten(adjustedTabDragBackground); collector.addRule(` - .monaco-workbench > .part.editor > .content:not(.dragged-over) .editor-group-container > .title .tabs-container > .tab.sizing-shrink.active:not(.dragged) > .tab-label::after { + .monaco-workbench .part.editor > .content:not(.dragged-over) .editor-group-container > .title .tabs-container > .tab.sizing-shrink.active:not(.dragged) > .tab-label::after { background: linear-gradient(to left, ${adjustedColor}, transparent); } - .monaco-workbench > .part.editor > .content.dragged-over .editor-group-container > .title .tabs-container > .tab.sizing-shrink.active:not(.dragged) > .tab-label::after { + .monaco-workbench .part.editor > .content.dragged-over .editor-group-container > .title .tabs-container > .tab.sizing-shrink.active:not(.dragged) > .tab-label::after { background: linear-gradient(to left, ${adjustedColorDrag}, transparent); } `); @@ -1267,11 +1267,11 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const adjustedColor = tabInactiveBackground.flatten(adjustedTabBackground); const adjustedColorDrag = tabInactiveBackground.flatten(adjustedTabDragBackground); collector.addRule(` - .monaco-workbench > .part.editor > .content:not(.dragged-over) .editor-group-container > .title .tabs-container > .tab.sizing-shrink:not(.dragged) > .tab-label::after { + .monaco-workbench .part.editor > .content:not(.dragged-over) .editor-group-container > .title .tabs-container > .tab.sizing-shrink:not(.dragged) > .tab-label::after { background: linear-gradient(to left, ${adjustedColor}, transparent); } - .monaco-workbench > .part.editor > .content.dragged-over .editor-group-container > .title .tabs-container > .tab.sizing-shrink:not(.dragged) > .tab-label::after { + .monaco-workbench .part.editor > .content.dragged-over .editor-group-container > .title .tabs-container > .tab.sizing-shrink:not(.dragged) > .tab-label::after { background: linear-gradient(to left, ${adjustedColorDrag}, transparent); } `); diff --git a/src/vs/workbench/browser/parts/media/compositepart.css b/src/vs/workbench/browser/parts/media/compositepart.css index 75895952fec..fde7cdb706c 100644 --- a/src/vs/workbench/browser/parts/media/compositepart.css +++ b/src/vs/workbench/browser/parts/media/compositepart.css @@ -3,15 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-workbench > .part > .content > .composite { +.monaco-workbench .part > .content > .composite { height: 100%; } -.monaco-workbench > .part > .composite.title { +.monaco-workbench .part > .composite.title { display: flex; } -.monaco-workbench > .part > .composite.title > .title-actions { +.monaco-workbench .part > .composite.title > .title-actions { flex: 1; padding-left: 5px; } \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/panel/media/panelpart.css b/src/vs/workbench/browser/parts/panel/media/panelpart.css index 18cc419df90..ce309151e69 100644 --- a/src/vs/workbench/browser/parts/panel/media/panelpart.css +++ b/src/vs/workbench/browser/parts/panel/media/panelpart.css @@ -3,16 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-workbench.nopanel > .part.panel { +.monaco-workbench.nopanel .part.panel { display: none !important; visibility: hidden !important; } -.monaco-workbench > .part.panel { +.monaco-workbench .part.panel { z-index: initial; } -.monaco-workbench > .part.panel .title { +.monaco-workbench .part.panel .title { padding-right: 0px; height: 35px; display: flex; @@ -20,23 +20,23 @@ justify-content: space-between; } -.monaco-workbench > .part.panel.bottom .title { +.monaco-workbench .part.panel.bottom .title { border-top-width: 1px; border-top-style: solid; } -.monaco-workbench > .part.panel.right { +.monaco-workbench .part.panel.right { border-left-width: 1px; border-left-style: solid; } -.monaco-workbench > .part.panel > .title > .title-actions .monaco-action-bar .action-item .action-label { +.monaco-workbench .part.panel > .title > .title-actions .monaco-action-bar .action-item .action-label { outline-offset: -2px; } /** Panel Switcher */ -.monaco-workbench > .part.panel > .title > .panel-switcher-container.composite-bar > .monaco-action-bar .action-label.toggle-more { +.monaco-workbench .part.panel > .title > .panel-switcher-container.composite-bar > .monaco-action-bar .action-label.toggle-more { background-image: url('ellipsis.svg'); display: block; height: 28px; @@ -48,16 +48,16 @@ background-position: center center; } -.monaco-workbench > .part.panel > .title > .panel-switcher-container > .monaco-action-bar { +.monaco-workbench .part.panel > .title > .panel-switcher-container > .monaco-action-bar { line-height: 27px; /* matches panel titles in settings */ height: 35px; } -.monaco-workbench > .part.panel > .title > .panel-switcher-container > .monaco-action-bar .action-item:first-child { +.monaco-workbench .part.panel > .title > .panel-switcher-container > .monaco-action-bar .action-item:first-child { padding-left: 12px; } -.monaco-workbench > .part.panel > .title > .panel-switcher-container > .monaco-action-bar .action-item { +.monaco-workbench .part.panel > .title > .panel-switcher-container > .monaco-action-bar .action-item { text-transform: uppercase; padding-left: 10px; padding-right: 10px; @@ -67,24 +67,24 @@ display: flex; } -.monaco-workbench > .part.panel > .title > .panel-switcher-container > .monaco-action-bar .action-item .action-label{ +.monaco-workbench .part.panel > .title > .panel-switcher-container > .monaco-action-bar .action-item .action-label{ margin-right: 0; } -.monaco-workbench > .part.panel > .title > .panel-switcher-container > .monaco-action-bar .action-item:last-child { +.monaco-workbench .part.panel > .title > .panel-switcher-container > .monaco-action-bar .action-item:last-child { padding-right: 10px; } -.monaco-workbench > .part.panel > .title > .panel-switcher-container > .monaco-action-bar .action-item.checked .action-label { +.monaco-workbench .part.panel > .title > .panel-switcher-container > .monaco-action-bar .action-item.checked .action-label { border-bottom: 1px solid; margin-right: 0; } -.monaco-workbench > .part.panel > .title > .panel-switcher-container > .monaco-action-bar .badge { +.monaco-workbench .part.panel > .title > .panel-switcher-container > .monaco-action-bar .badge { margin-left: 8px; } -.monaco-workbench > .part.panel > .title > .panel-switcher-container > .monaco-action-bar .badge .badge-content { +.monaco-workbench .part.panel > .title > .panel-switcher-container > .monaco-action-bar .badge .badge-content { padding: 0.3em 0.5em; border-radius: 1em; font-weight: normal; @@ -152,7 +152,7 @@ background: url('close-inverse.svg') center center no-repeat; } -.vs-dark .monaco-workbench > .part.panel > .title > .panel-switcher-container.composite-bar > .monaco-action-bar .action-label.toggle-more, -.hc-black .monaco-workbench > .part.panel > .title > .panel-switcher-container.composite-bar > .monaco-action-bar .action-label.toggle-more { +.vs-dark .monaco-workbench .part.panel > .title > .panel-switcher-container.composite-bar > .monaco-action-bar .action-label.toggle-more, +.hc-black .monaco-workbench .part.panel > .title > .panel-switcher-container.composite-bar > .monaco-action-bar .action-label.toggle-more { background-image: url('ellipsis-inverse.svg'); } diff --git a/src/vs/workbench/browser/parts/panel/panelPart.ts b/src/vs/workbench/browser/parts/panel/panelPart.ts index 26a6b426ed8..68bc0f955e6 100644 --- a/src/vs/workbench/browser/parts/panel/panelPart.ts +++ b/src/vs/workbench/browser/parts/panel/panelPart.ts @@ -5,7 +5,7 @@ import 'vs/css!./media/panelpart'; import { IAction } from 'vs/base/common/actions'; -import { Event } from 'vs/base/common/event'; +import { Event, Emitter } from 'vs/base/common/event'; import { Registry } from 'vs/platform/registry/common/platform'; import { ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar'; import { IPanel } from 'vs/workbench/common/panel'; @@ -32,6 +32,7 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import { RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { isUndefinedOrNull } from 'vs/base/common/types'; import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { ISerializableView } from 'vs/base/browser/ui/grid/grid'; export const ActivePanelContext = new RawContextKey('activePanel', ''); export const PanelFocusContext = new RawContextKey('panelFocus', false); @@ -43,7 +44,7 @@ interface ICachedPanel { visible: boolean; } -export class PanelPart extends CompositePart implements IPanelService { +export class PanelPart extends CompositePart implements IPanelService, ISerializableView { static readonly activePanelSettingsKey = 'workbench.panelpart.activepanelid'; @@ -59,6 +60,16 @@ export class PanelPart extends CompositePart implements IPanelService { private compositeActions: { [compositeId: string]: { activityAction: PanelActivityAction, pinnedAction: ToggleCompositePinnedAction } } = Object.create(null); private dimension: Dimension; + element: HTMLElement; + minimumWidth: number = 300; + maximumWidth: number = Number.POSITIVE_INFINITY; + minimumHeight: number = 77; + maximumHeight: number = Number.POSITIVE_INFINITY; + snapSize: number = 50; + + private _onDidChange = new Emitter<{ width: number; height: number; }>(); + readonly onDidChange = this._onDidChange.event; + constructor( id: string, @INotificationService notificationService: INotificationService, @@ -129,6 +140,8 @@ export class PanelPart extends CompositePart implements IPanelService { } create(parent: HTMLElement): void { + this.element = parent; + super.create(parent); const focusTracker = trackFocus(parent); @@ -271,21 +284,32 @@ export class PanelPart extends CompositePart implements IPanelService { }; } - layout(dimension: Dimension): Dimension[] { + layout(dimension: Dimension): Dimension[]; + layout(width: number, height: number): void; + layout(dim1: Dimension | number, dim2?: number): Dimension[] | void { if (!this.partService.isVisible(Parts.PANEL_PART)) { - return [dimension]; + if (dim1 instanceof Dimension) { + return [dim1]; + } + + return; } + const { width, height } = dim1 instanceof Dimension ? dim1 : { width: dim1, height: dim2 }; + if (this.partService.getPanelPosition() === Position.RIGHT) { // Take into account the 1px border when layouting - this.dimension = new Dimension(dimension.width - 1, dimension.height); + this.dimension = new Dimension(width - 1, height); } else { - this.dimension = dimension; + this.dimension = new Dimension(width, height); } - const sizes = super.layout(this.dimension); + + const sizes = super.layout(this.dimension.width, this.dimension.height); this.layoutCompositeBar(); - return sizes; + if (dim1 instanceof Dimension) { + return sizes; + } } private layoutCompositeBar(): void { @@ -408,6 +432,12 @@ export class PanelPart extends CompositePart implements IPanelService { private setStoredCachedViewletsValue(value: string): void { this.storageService.store(PanelPart.PINNED_PANELS, value, StorageScope.GLOBAL); } + + toJSON(): object { + return { + type: Parts.PANEL_PART + }; + } } registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { @@ -418,9 +448,9 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const panelBackground = theme.getColor(PANEL_BACKGROUND); if (panelBackground && panelBackground !== theme.getColor(editorBackground)) { collector.addRule(` - .monaco-workbench > .part.panel > .content .monaco-editor, - .monaco-workbench > .part.panel > .content .monaco-editor .margin, - .monaco-workbench > .part.panel > .content .monaco-editor .monaco-editor-background { + .monaco-workbench .part.panel > .content .monaco-editor, + .monaco-workbench .part.panel > .content .monaco-editor .margin, + .monaco-workbench .part.panel > .content .monaco-editor .monaco-editor-background { background-color: ${panelBackground}; } `); @@ -431,7 +461,7 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const titleActiveBorder = theme.getColor(PANEL_ACTIVE_TITLE_BORDER); if (titleActive || titleActiveBorder) { collector.addRule(` - .monaco-workbench > .part.panel > .title > .panel-switcher-container > .monaco-action-bar .action-item:hover .action-label { + .monaco-workbench .part.panel > .title > .panel-switcher-container > .monaco-action-bar .action-item:hover .action-label { color: ${titleActive} !important; border-bottom-color: ${titleActiveBorder} !important; } @@ -442,14 +472,14 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const focusBorderColor = theme.getColor(focusBorder); if (focusBorderColor) { collector.addRule(` - .monaco-workbench > .part.panel > .title > .panel-switcher-container > .monaco-action-bar .action-item:focus .action-label { + .monaco-workbench .part.panel > .title > .panel-switcher-container > .monaco-action-bar .action-item:focus .action-label { color: ${titleActive} !important; border-bottom-color: ${focusBorderColor} !important; border-bottom: 1px solid; } `); collector.addRule(` - .monaco-workbench > .part.panel > .title > .panel-switcher-container > .monaco-action-bar .action-item:focus { + .monaco-workbench .part.panel > .title > .panel-switcher-container > .monaco-action-bar .action-item:focus { outline: none; } `); @@ -461,8 +491,8 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const outline = theme.getColor(activeContrastBorder); collector.addRule(` - .monaco-workbench > .part.panel > .title > .panel-switcher-container > .monaco-action-bar .action-item.checked .action-label, - .monaco-workbench > .part.panel > .title > .panel-switcher-container > .monaco-action-bar .action-item .action-label:hover { + .monaco-workbench .part.panel > .title > .panel-switcher-container > .monaco-action-bar .action-item.checked .action-label, + .monaco-workbench .part.panel > .title > .panel-switcher-container > .monaco-action-bar .action-item .action-label:hover { outline-color: ${outline}; outline-width: 1px; outline-style: solid; @@ -471,7 +501,7 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { outline-offset: 1px; } - .monaco-workbench > .part.panel > .title > .panel-switcher-container > .monaco-action-bar .action-item:not(.checked) .action-label:hover { + .monaco-workbench .part.panel > .title > .panel-switcher-container > .monaco-action-bar .action-item:not(.checked) .action-label:hover { outline-style: dashed; } `); diff --git a/src/vs/workbench/browser/parts/sidebar/media/sidebarpart.css b/src/vs/workbench/browser/parts/sidebar/media/sidebarpart.css index 9a5f5bd6e00..eb324691b34 100644 --- a/src/vs/workbench/browser/parts/sidebar/media/sidebarpart.css +++ b/src/vs/workbench/browser/parts/sidebar/media/sidebarpart.css @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-workbench > .sidebar > .content { +.monaco-workbench .sidebar > .content { overflow: hidden; } @@ -12,7 +12,7 @@ visibility: hidden !important; } -.monaco-workbench > .sidebar > .title > .title-label h2 { +.monaco-workbench .sidebar > .title > .title-label h2 { text-transform: uppercase; } diff --git a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts index f357916019f..d95a8d716a4 100644 --- a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts +++ b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts @@ -25,16 +25,17 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { contrastBorder } from 'vs/platform/theme/common/colorRegistry'; import { SIDE_BAR_TITLE_FOREGROUND, SIDE_BAR_BACKGROUND, SIDE_BAR_FOREGROUND, SIDE_BAR_BORDER } from 'vs/workbench/common/theme'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { Dimension, EventType, addDisposableListener, trackFocus } from 'vs/base/browser/dom'; +import { EventType, addDisposableListener, trackFocus, Dimension } from 'vs/base/browser/dom'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { ISerializableView } from 'vs/base/browser/ui/grid/grid'; export const SidebarFocusContext = new RawContextKey('sideBarFocus', false); export const ActiveViewletContext = new RawContextKey('activeViewlet', ''); -export class SidebarPart extends CompositePart implements IViewletService { +export class SidebarPart extends CompositePart implements ISerializableView, IViewletService { _serviceBrand: any; static readonly activeViewletSettingsKey = 'workbench.sidebar.activeviewletid'; @@ -45,6 +46,16 @@ export class SidebarPart extends CompositePart implements IViewletServi private blockOpeningViewlet: boolean; private _onDidViewletDeregister = this._register(new Emitter()); + element: HTMLElement; + minimumWidth: number = 170; + maximumWidth: number = Number.POSITIVE_INFINITY; + minimumHeight: number = 0; + maximumHeight: number = Number.POSITIVE_INFINITY; + snapSize: number = 50; + + private _onDidChange = new Emitter<{ width: number; height: number; }>(); + readonly onDidChange = this._onDidChange.event; + constructor( id: string, @INotificationService notificationService: INotificationService, @@ -111,6 +122,8 @@ export class SidebarPart extends CompositePart implements IViewletServi } create(parent: HTMLElement): void { + this.element = parent; + super.create(parent); const focusTracker = trackFocus(parent); @@ -152,12 +165,22 @@ export class SidebarPart extends CompositePart implements IViewletServi container.style.borderLeftColor = !isPositionLeft ? borderColor : null; } - layout(dimension: Dimension): Dimension[] { + layout(dimension: Dimension): Dimension[]; + layout(width: number, height: number): void; + layout(dim1: Dimension | number, dim2?: number): Dimension[] | void { if (!this.partService.isVisible(Parts.SIDEBAR_PART)) { - return [dimension]; + if (dim1 instanceof Dimension) { + return [dim1]; + } + + return; } - return super.layout(dimension); + if (dim1 instanceof Dimension) { + return super.layout(dim1); + } + + super.layout(dim1, dim2!); } // Viewlet service @@ -237,6 +260,12 @@ export class SidebarPart extends CompositePart implements IViewletServi } } } + + toJSON(): object { + return { + type: Parts.SIDEBAR_PART + }; + } } class FocusSideBarAction extends Action { diff --git a/src/vs/workbench/browser/parts/statusbar/media/statusbarpart.css b/src/vs/workbench/browser/parts/statusbar/media/statusbarpart.css index c528511d2cf..8c31b3a23ec 100644 --- a/src/vs/workbench/browser/parts/statusbar/media/statusbarpart.css +++ b/src/vs/workbench/browser/parts/statusbar/media/statusbarpart.css @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-workbench > .part.statusbar { +.monaco-workbench .part.statusbar { box-sizing: border-box; cursor: default; width: 100%; @@ -11,18 +11,18 @@ font-size: 12px; } -.monaco-workbench > .part.statusbar > .statusbar-item { +.monaco-workbench .part.statusbar > .statusbar-item { display: inline-block; line-height: 22px; height: 100%; vertical-align: top; } -.monaco-workbench > .part.statusbar > .statusbar-item.has-beak { +.monaco-workbench .part.statusbar > .statusbar-item.has-beak { position: relative; } -.monaco-workbench > .part.statusbar > .statusbar-item.has-beak:before { +.monaco-workbench .part.statusbar > .statusbar-item.has-beak:before { content: ''; position: absolute; left: 11px; @@ -33,48 +33,48 @@ border-right: 5px solid transparent; } -.monaco-workbench > .part.statusbar > .statusbar-item.left > :first-child { +.monaco-workbench .part.statusbar > .statusbar-item.left > :first-child { margin-right: 5px; } -.monaco-workbench > .part.statusbar > .statusbar-item.right { +.monaco-workbench .part.statusbar > .statusbar-item.right { float: right; } -.monaco-workbench > .part.statusbar > .statusbar-item.right > :first-child { +.monaco-workbench .part.statusbar > .statusbar-item.right > :first-child { margin-left: 5px; } /* adding padding to the most left status bar item */ -.monaco-workbench > .part.statusbar > .statusbar-item.left:first-child, .monaco-workbench > .part.statusbar > .statusbar-item.right + .statusbar-item.left { +.monaco-workbench .part.statusbar > .statusbar-item.left:first-child, .monaco-workbench .part.statusbar > .statusbar-item.right + .statusbar-item.left { padding-left: 10px; } /* adding padding to the most right status bar item */ -.monaco-workbench > .part.statusbar > .statusbar-item.right:first-child { +.monaco-workbench .part.statusbar > .statusbar-item.right:first-child { padding-right: 10px; } -.monaco-workbench > .part.statusbar > .statusbar-item a { +.monaco-workbench .part.statusbar > .statusbar-item a { cursor: pointer; display: inline-block; height: 100%; } -.monaco-workbench > .part.statusbar > .statusbar-entry > span { +.monaco-workbench .part.statusbar > .statusbar-entry > span { height: 100%; } -.monaco-workbench > .part.statusbar > .statusbar-entry > span, -.monaco-workbench > .part.statusbar > .statusbar-entry > a { +.monaco-workbench .part.statusbar > .statusbar-entry > span, +.monaco-workbench .part.statusbar > .statusbar-entry > a { padding: 0 5px 0 5px; white-space: pre; /* gives some degree of styling */ } -.monaco-workbench > .part.statusbar > .statusbar-entry span.octicon { +.monaco-workbench .part.statusbar > .statusbar-entry span.octicon { text-align: center; font-size: 14px; } -.monaco-workbench > .part.statusbar > .statusbar-item a:hover { +.monaco-workbench .part.statusbar > .statusbar-item a:hover { text-decoration: none; } \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts index 29e6b41dfdc..9fed8f11b46 100644 --- a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts +++ b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts @@ -24,20 +24,32 @@ import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/ import { contrastBorder } from 'vs/platform/theme/common/colorRegistry'; import { isThemeColor } from 'vs/editor/common/editorCommon'; import { Color } from 'vs/base/common/color'; -import { addClass, EventHelper, createStyleSheet, addDisposableListener } from 'vs/base/browser/dom'; +import { addClass, EventHelper, createStyleSheet, addDisposableListener, Dimension } from 'vs/base/browser/dom'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IStorageService } from 'vs/platform/storage/common/storage'; +import { Emitter } from 'vs/base/common/event'; +import { ISerializableView } from 'vs/base/browser/ui/grid/grid'; +import { Parts } from 'vs/workbench/services/part/common/partService'; -export class StatusbarPart extends Part implements IStatusbarService { +export class StatusbarPart extends Part implements IStatusbarService, ISerializableView { _serviceBrand: any; private static readonly PRIORITY_PROP = 'statusbar-entry-priority'; private static readonly ALIGNMENT_PROP = 'statusbar-entry-alignment'; - private statusItemsContainer: HTMLElement; + element: HTMLElement; private statusMsgDispose: IDisposable; + + minimumWidth: number = 0; + maximumWidth: number = Number.POSITIVE_INFINITY; + minimumHeight: number = 22; + maximumHeight: number = 22; + + private _onDidChange = new Emitter<{ width: number; height: number; }>(); + readonly onDidChange = this._onDidChange.event; + private styleElement: HTMLStyleElement; constructor( @@ -64,7 +76,7 @@ export class StatusbarPart extends Part implements IStatusbarService { const toDispose = item.render(el); // Insert according to priority - const container = this.statusItemsContainer; + const container = this.element; const neighbours = this.getEntries(alignment); let inserted = false; for (const neighbour of neighbours) { @@ -95,7 +107,7 @@ export class StatusbarPart extends Part implements IStatusbarService { private getEntries(alignment: StatusbarAlignment): HTMLElement[] { const entries: HTMLElement[] = []; - const container = this.statusItemsContainer; + const container = this.element; const children = container.children; for (let i = 0; i < children.length; i++) { const childElement = children.item(i); @@ -108,7 +120,7 @@ export class StatusbarPart extends Part implements IStatusbarService { } createContentArea(parent: HTMLElement): HTMLElement { - this.statusItemsContainer = parent; + this.element = parent; // Fill in initial items that were contributed from the registry const registry = Registry.as(Extensions.Statusbar); @@ -134,10 +146,10 @@ export class StatusbarPart extends Part implements IStatusbarService { const el = this.doCreateStatusItem(descriptor.alignment, descriptor.priority); this._register(item.render(el)); - this.statusItemsContainer.appendChild(el); + this.element.appendChild(el); } - return this.statusItemsContainer; + return this.element; } protected updateStyles(): void { @@ -161,7 +173,7 @@ export class StatusbarPart extends Part implements IStatusbarService { this.styleElement = createStyleSheet(container); } - this.styleElement.innerHTML = `.monaco-workbench > .part.statusbar > .statusbar-item.has-beak:before { border-bottom-color: ${backgroundColor}; }`; + this.styleElement.innerHTML = `.monaco-workbench .part.statusbar > .statusbar-item.has-beak:before { border-bottom-color: ${backgroundColor}; }`; } private doCreateStatusItem(alignment: StatusbarAlignment, priority: number = 0, extraClass?: string): HTMLElement { @@ -220,6 +232,22 @@ export class StatusbarPart extends Part implements IStatusbarService { return dispose; } + + layout(dimension: Dimension): Dimension[]; + layout(width: number, height: number): void; + layout(dim1: Dimension | number, dim2?: number): Dimension[] | void { + if (dim1 instanceof Dimension) { + return super.layout(dim1); + } else { + super.layout(new Dimension(dim1, dim2!)); + } + } + + toJSON(): object { + return { + type: Parts.STATUSBAR_PART + }; + } } let manageExtensionAction: ManageExtensionAction; @@ -336,21 +364,21 @@ class ManageExtensionAction extends Action { registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const statusBarItemHoverBackground = theme.getColor(STATUS_BAR_ITEM_HOVER_BACKGROUND); if (statusBarItemHoverBackground) { - collector.addRule(`.monaco-workbench > .part.statusbar > .statusbar-item a:hover { background-color: ${statusBarItemHoverBackground}; }`); + collector.addRule(`.monaco-workbench .part.statusbar > .statusbar-item a:hover { background-color: ${statusBarItemHoverBackground}; }`); } const statusBarItemActiveBackground = theme.getColor(STATUS_BAR_ITEM_ACTIVE_BACKGROUND); if (statusBarItemActiveBackground) { - collector.addRule(`.monaco-workbench > .part.statusbar > .statusbar-item a:active { background-color: ${statusBarItemActiveBackground}; }`); + collector.addRule(`.monaco-workbench .part.statusbar > .statusbar-item a:active { background-color: ${statusBarItemActiveBackground}; }`); } const statusBarProminentItemBackground = theme.getColor(STATUS_BAR_PROMINENT_ITEM_BACKGROUND); if (statusBarProminentItemBackground) { - collector.addRule(`.monaco-workbench > .part.statusbar > .statusbar-item .status-bar-info { background-color: ${statusBarProminentItemBackground}; }`); + collector.addRule(`.monaco-workbench .part.statusbar > .statusbar-item .status-bar-info { background-color: ${statusBarProminentItemBackground}; }`); } const statusBarProminentItemHoverBackground = theme.getColor(STATUS_BAR_PROMINENT_ITEM_HOVER_BACKGROUND); if (statusBarProminentItemHoverBackground) { - collector.addRule(`.monaco-workbench > .part.statusbar > .statusbar-item a.status-bar-info:hover { background-color: ${statusBarProminentItemHoverBackground}; }`); + collector.addRule(`.monaco-workbench .part.statusbar > .statusbar-item a.status-bar-info:hover { background-color: ${statusBarProminentItemHoverBackground}; }`); } }); diff --git a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css index 85149003283..38794f793dd 100644 --- a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css +++ b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-workbench > .part.titlebar { +.monaco-workbench .part.titlebar { box-sizing: border-box; width: 100%; padding: 0 70px; @@ -18,7 +18,7 @@ display: flex; } -.monaco-workbench > .part.titlebar > .titlebar-drag-region { +.monaco-workbench .part.titlebar > .titlebar-drag-region { top: 0; left: 0; display: block; @@ -29,7 +29,7 @@ -webkit-app-region: drag; } -.monaco-workbench > .part.titlebar > .window-title { +.monaco-workbench .part.titlebar > .window-title { flex: 0 1 auto; font-size: 12px; overflow: hidden; @@ -42,8 +42,8 @@ /* Windows/Linux: Rules for custom title (icon, window controls) */ -.windows > .monaco-workbench > .part.titlebar, -.linux > .monaco-workbench > .part.titlebar { +.windows > .monaco-workbench .part.titlebar, +.linux > .monaco-workbench .part.titlebar { padding: 0; height: 30px; line-height: 30px; @@ -51,17 +51,17 @@ overflow: visible; } -.windows > .monaco-workbench > .part.titlebar > .window-title, -.linux > .monaco-workbench > .part.titlebar > .window-title { +.windows > .monaco-workbench .part.titlebar > .window-title, +.linux > .monaco-workbench .part.titlebar > .window-title { cursor: default; } -.linux > .monaco-workbench > .part.titlebar > .window-title { +.linux > .monaco-workbench .part.titlebar > .window-title { font-size: inherit; } -.windows > .monaco-workbench > .part.titlebar > .resizer, -.linux > .monaco-workbench > .part.titlebar > .resizer { +.windows > .monaco-workbench .part.titlebar > .resizer, +.linux > .monaco-workbench .part.titlebar > .resizer { -webkit-app-region: no-drag; position: absolute; top: 0; @@ -69,13 +69,13 @@ height: 20%; } -.windows > .monaco-workbench.fullscreen > .part.titlebar > .resizer, -.linux > .monaco-workbench.fullscreen > .part.titlebar > .resizer { +.windows > .monaco-workbench.fullscreen .part.titlebar > .resizer, +.linux > .monaco-workbench.fullscreen .part.titlebar > .resizer { display: none; } -.monaco-workbench > .part.titlebar > .window-appicon { +.monaco-workbench .part.titlebar > .window-appicon { width: 35px; height: 100%; position: relative; @@ -87,11 +87,11 @@ flex-shrink: 0; } -.monaco-workbench.fullscreen > .part.titlebar > .window-appicon { +.monaco-workbench.fullscreen .part.titlebar > .window-appicon { display: none; } -.monaco-workbench > .part.titlebar > .window-controls-container { +.monaco-workbench .part.titlebar > .window-controls-container { display: flex; flex-grow: 0; flex-shrink: 0; @@ -104,56 +104,56 @@ margin-left: auto; } -.monaco-workbench.fullscreen > .part.titlebar > .window-controls-container { +.monaco-workbench.fullscreen .part.titlebar > .window-controls-container { display: none; } -.monaco-workbench > .part.titlebar > .window-controls-container > .window-icon-bg { +.monaco-workbench .part.titlebar > .window-controls-container > .window-icon-bg { display: inline-block; -webkit-app-region: no-drag; height: 100%; width: 33.34%; } -.monaco-workbench > .part.titlebar > .window-controls-container .window-icon svg { +.monaco-workbench .part.titlebar > .window-controls-container .window-icon svg { shape-rendering: crispEdges; text-align: center; } -.monaco-workbench > .part.titlebar.titlebar > .window-controls-container .window-close { +.monaco-workbench .part.titlebar.titlebar > .window-controls-container .window-close { -webkit-mask: url('chrome-close.svg') no-repeat 50% 50%; } -.monaco-workbench > .part.titlebar.titlebar > .window-controls-container .window-unmaximize { +.monaco-workbench .part.titlebar.titlebar > .window-controls-container .window-unmaximize { -webkit-mask: url('chrome-restore.svg') no-repeat 50% 50%; } -.monaco-workbench > .part.titlebar > .window-controls-container .window-maximize { +.monaco-workbench .part.titlebar > .window-controls-container .window-maximize { -webkit-mask: url('chrome-maximize.svg') no-repeat 50% 50%; } -.monaco-workbench > .part.titlebar > .window-controls-container .window-minimize { +.monaco-workbench .part.titlebar > .window-controls-container .window-minimize { -webkit-mask: url('chrome-minimize.svg') no-repeat 50% 50%; } -.monaco-workbench > .part.titlebar > .window-controls-container > .window-icon-bg > .window-icon { +.monaco-workbench .part.titlebar > .window-controls-container > .window-icon-bg > .window-icon { height: 100%; width: 100%; -webkit-mask-size: 23.1%; } -.monaco-workbench > .part.titlebar > .window-controls-container > .window-icon-bg:hover { +.monaco-workbench .part.titlebar > .window-controls-container > .window-icon-bg:hover { background-color: rgba(255, 255, 255, 0.1); } -.monaco-workbench > .part.titlebar.light > .window-controls-container > .window-icon-bg:hover { +.monaco-workbench .part.titlebar.light > .window-controls-container > .window-icon-bg:hover { background-color: rgba(0, 0, 0, 0.1); } -.monaco-workbench > .part.titlebar > .window-controls-container > .window-icon-bg.window-close-bg:hover { +.monaco-workbench .part.titlebar > .window-controls-container > .window-icon-bg.window-close-bg:hover { background-color: rgba(232, 17, 35, 0.9); } -.monaco-workbench > .part.titlebar > .window-controls-container .window-icon.window-close:hover { +.monaco-workbench .part.titlebar > .window-controls-container .window-icon.window-close:hover { background-color: white; } diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index 93d58329d84..8f7d118547c 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -30,10 +30,12 @@ import { MenubarControl } from 'vs/workbench/browser/parts/titlebar/menubarContr import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { template, getBaseLabel } from 'vs/base/common/labels'; import { ILabelService } from 'vs/platform/label/common/label'; -import { Event } from 'vs/base/common/event'; +import { Event, Emitter } from 'vs/base/common/event'; import { IStorageService } from 'vs/platform/storage/common/storage'; +import { ISerializableView } from 'vs/base/browser/ui/grid/grid'; +import { Parts } from 'vs/workbench/services/part/common/partService'; -export class TitlebarPart extends Part implements ITitleService { +export class TitlebarPart extends Part implements ITitleService, ISerializableView { _serviceBrand: any; @@ -43,7 +45,7 @@ export class TitlebarPart extends Part implements ITitleService { private static readonly TITLE_DIRTY = '\u25cf '; private static readonly TITLE_SEPARATOR = isMacintosh ? ' — ' : ' - '; // macOS uses special - separator - private titleContainer: HTMLElement; + element: HTMLElement; private title: HTMLElement; private dragRegion: HTMLElement; private windowControls: HTMLElement; @@ -61,6 +63,14 @@ export class TitlebarPart extends Part implements ITitleService { private properties: ITitleProperties; private activeEditorListeners: IDisposable[]; + minimumWidth: number = 0; + maximumWidth: number = Number.POSITIVE_INFINITY; + minimumHeight: number = 30; + maximumHeight: number = 30; + + private _onDidChange = new Emitter<{ width: number; height: number; }>(); + readonly onDidChange = this._onDidChange.event; + constructor( id: string, @IContextMenuService private readonly contextMenuService: IContextMenuService, @@ -283,14 +293,14 @@ export class TitlebarPart extends Part implements ITitleService { } createContentArea(parent: HTMLElement): HTMLElement { - this.titleContainer = parent; + this.element = parent; // Draggable region that we can manipulate for #52522 - this.dragRegion = append(this.titleContainer, $('div.titlebar-drag-region')); + this.dragRegion = append(this.element, $('div.titlebar-drag-region')); // App Icon (Windows/Linux) if (!isMacintosh) { - this.appIcon = append(this.titleContainer, $('div.window-appicon')); + this.appIcon = append(this.element, $('div.window-appicon')); this.onUpdateAppIconDragBehavior(); this._register(addDisposableListener(this.appIcon, EventType.DBLCLICK, (e => { @@ -300,7 +310,7 @@ export class TitlebarPart extends Part implements ITitleService { // Menubar: the menubar part which is responsible for populating both the custom and native menubars this.menubarPart = this.instantiationService.createInstance(MenubarControl); - this.menubar = append(this.titleContainer, $('div.menubar')); + this.menubar = append(this.element, $('div.menubar')); this.menubar.setAttribute('role', 'menubar'); this.menubarPart.create(this.menubar); @@ -311,7 +321,7 @@ export class TitlebarPart extends Part implements ITitleService { } // Title - this.title = append(this.titleContainer, $('div.window-title')); + this.title = append(this.element, $('div.window-title')); if (this.pendingTitle) { this.title.innerText = this.pendingTitle; } else { @@ -320,7 +330,7 @@ export class TitlebarPart extends Part implements ITitleService { // Maximize/Restore on doubleclick if (isMacintosh) { - this._register(addDisposableListener(this.titleContainer, EventType.DBLCLICK, e => { + this._register(addDisposableListener(this.element, EventType.DBLCLICK, e => { EventHelper.stop(e); this.onTitleDoubleclick(); @@ -340,7 +350,7 @@ export class TitlebarPart extends Part implements ITitleService { // Window Controls (Windows/Linux) if (!isMacintosh) { - this.windowControls = append(this.titleContainer, $('div.window-controls-container')); + this.windowControls = append(this.element, $('div.window-controls-container')); // Minimize @@ -375,7 +385,7 @@ export class TitlebarPart extends Part implements ITitleService { })); // Resizer - this.resizer = append(this.titleContainer, $('div.resizer')); + this.resizer = append(this.element, $('div.resizer')); const isMaximized = this.windowService.getConfiguration().maximized ? true : false; this.onDidChangeMaximized(isMaximized); @@ -384,7 +394,7 @@ export class TitlebarPart extends Part implements ITitleService { // Since the title area is used to drag the window, we do not want to steal focus from the // currently active element. So we restore focus after a timeout back to where it was. - this._register(addDisposableListener(this.titleContainer, EventType.MOUSE_DOWN, e => { + this._register(addDisposableListener(this.element, EventType.MOUSE_DOWN, e => { if (e.target && isAncestor(e.target as HTMLElement, this.menubar)) { return; } @@ -399,7 +409,7 @@ export class TitlebarPart extends Part implements ITitleService { this.updateStyles(); - return this.titleContainer; + return this.element; } private onDidChangeMaximized(maximized: boolean) { @@ -428,26 +438,26 @@ export class TitlebarPart extends Part implements ITitleService { super.updateStyles(); // Part container - if (this.titleContainer) { + if (this.element) { if (this.isInactive) { - addClass(this.titleContainer, 'inactive'); + addClass(this.element, 'inactive'); } else { - removeClass(this.titleContainer, 'inactive'); + removeClass(this.element, 'inactive'); } const titleBackground = this.getColor(this.isInactive ? TITLE_BAR_INACTIVE_BACKGROUND : TITLE_BAR_ACTIVE_BACKGROUND); - this.titleContainer.style.backgroundColor = titleBackground; + this.element.style.backgroundColor = titleBackground; if (Color.fromHex(titleBackground).isLighter()) { - addClass(this.titleContainer, 'light'); + addClass(this.element, 'light'); } else { - removeClass(this.titleContainer, 'light'); + removeClass(this.element, 'light'); } const titleForeground = this.getColor(this.isInactive ? TITLE_BAR_INACTIVE_FOREGROUND : TITLE_BAR_ACTIVE_FOREGROUND); - this.titleContainer.style.color = titleForeground; + this.element.style.color = titleForeground; const titleBorder = this.getColor(TITLE_BAR_BORDER); - this.titleContainer.style.borderBottom = titleBorder ? `1px solid ${titleBorder}` : null; + this.element.style.borderBottom = titleBorder ? `1px solid ${titleBorder}` : null; } } @@ -513,8 +523,8 @@ export class TitlebarPart extends Part implements ITitleService { private adjustTitleMarginToCenter(): void { if (!isMacintosh && - (this.appIcon.clientWidth + this.menubar.clientWidth + 10 > (this.titleContainer.clientWidth - this.title.clientWidth) / 2 || - this.titleContainer.clientWidth - this.windowControls.clientWidth - 10 < (this.titleContainer.clientWidth + this.title.clientWidth) / 2)) { + (this.appIcon.clientWidth + this.menubar.clientWidth + 10 > (this.element.clientWidth - this.title.clientWidth) / 2 || + this.element.clientWidth - this.windowControls.clientWidth - 10 < (this.element.clientWidth + this.title.clientWidth) / 2)) { this.title.style.position = null; this.title.style.left = null; this.title.style.transform = null; @@ -525,7 +535,7 @@ export class TitlebarPart extends Part implements ITitleService { } } - layout(dimension: Dimension): Dimension[] { + updateLayout(dimension: Dimension): void { if (getTitleBarStyle(this.configurationService, this.environmentService) === 'custom') { // Only prevent zooming behavior on macOS or when the menubar is not visible if (isMacintosh || this.configurationService.getValue('window.menuBarVisibility') === 'hidden') { @@ -549,8 +559,27 @@ export class TitlebarPart extends Part implements ITitleService { this.menubarPart.layout(menubarDimension); } } + } - return super.layout(dimension); + layout(dimension: Dimension): Dimension[]; + layout(width: number, height: number): void; + layout(dim1: Dimension | number, dim2?: number): Dimension[] | void { + if (dim1 instanceof Dimension) { + this.updateLayout(dim1); + + return super.layout(dim1); + } + + const dimensions = new Dimension(dim1, dim2); + this.updateLayout(dimensions); + + super.layout(dimensions); + } + + toJSON(): object { + return { + type: Parts.TITLEBAR_PART + }; } } @@ -569,7 +598,7 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const titlebarActiveFg = theme.getColor(TITLE_BAR_ACTIVE_FOREGROUND); if (titlebarActiveFg) { collector.addRule(` - .monaco-workbench > .part.titlebar > .window-controls-container .window-icon { + .monaco-workbench .part.titlebar > .window-controls-container .window-icon { background-color: ${titlebarActiveFg}; } `); @@ -578,7 +607,7 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const titlebarInactiveFg = theme.getColor(TITLE_BAR_INACTIVE_FOREGROUND); if (titlebarInactiveFg) { collector.addRule(` - .monaco-workbench > .part.titlebar.inactive > .window-controls-container .window-icon { + .monaco-workbench .part.titlebar.inactive > .window-controls-container .window-icon { background-color: ${titlebarInactiveFg}; } `); diff --git a/src/vs/workbench/electron-browser/main.contribution.ts b/src/vs/workbench/electron-browser/main.contribution.ts index f478a2cc03a..7d6cce64e17 100644 --- a/src/vs/workbench/electron-browser/main.contribution.ts +++ b/src/vs/workbench/electron-browser/main.contribution.ts @@ -689,6 +689,12 @@ configurationRegistry.registerConfiguration({ 'description': nls.localize('workbench.enableExperiments', "Fetches experiments to run from a Microsoft online service."), 'default': true, 'tags': ['usesOnlineServices'] + }, + 'workbench.useExperimentalGridLayout': { + 'type': 'boolean', + 'description': nls.localize('workbench.useExperimentalGridLayout', "Enables the grid layout for the workbench. This setting may enable additional layout options for workbench components."), + 'default': false, + 'scope': ConfigurationScope.APPLICATION } } }); diff --git a/src/vs/workbench/electron-browser/media/workbench.css b/src/vs/workbench/electron-browser/media/workbench.css index 28c4a57c000..951d0972276 100644 --- a/src/vs/workbench/electron-browser/media/workbench.css +++ b/src/vs/workbench/electron-browser/media/workbench.css @@ -11,7 +11,7 @@ overflow: hidden; } -.monaco-workbench > .part { +.monaco-workbench .part { position: absolute; box-sizing: border-box; } diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts index 8fd07d1e1e4..5ff640f0e6b 100644 --- a/src/vs/workbench/electron-browser/workbench.ts +++ b/src/vs/workbench/electron-browser/workbench.ts @@ -27,14 +27,13 @@ import { PanelPart } from 'vs/workbench/browser/parts/panel/panelPart'; import { StatusbarPart } from 'vs/workbench/browser/parts/statusbar/statusbarPart'; import { TitlebarPart } from 'vs/workbench/browser/parts/titlebar/titlebarPart'; import { EditorPart } from 'vs/workbench/browser/parts/editor/editorPart'; -import { WorkbenchLayout } from 'vs/workbench/browser/layout'; import { IActionBarRegistry, Extensions as ActionBarExtensions } from 'vs/workbench/browser/actions'; import { PanelRegistry, Extensions as PanelExtensions } from 'vs/workbench/browser/panel'; import { QuickOpenController } from 'vs/workbench/browser/parts/quickopen/quickOpenController'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { QuickInputService } from 'vs/workbench/browser/parts/quickinput/quickInput'; import { getServices } from 'vs/platform/instantiation/common/extensions'; -import { Position, Parts, IPartService, ILayoutOptions, IDimension, PositionToString } from 'vs/workbench/services/part/common/partService'; +import { Position, Parts, IPartService, IDimension, PositionToString, ILayoutOptions } from 'vs/workbench/services/part/common/partService'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IStorageService, StorageScope, IWillSaveStateEvent, WillSaveStateReason } from 'vs/platform/storage/common/storage'; import { ContextMenuService as NativeContextMenuService } from 'vs/workbench/services/contextview/electron-browser/contextmenuService'; @@ -113,7 +112,9 @@ import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/work import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; import { FileDialogService } from 'vs/workbench/services/dialogs/electron-browser/dialogService'; import { LogStorageAction } from 'vs/platform/storage/node/storageService'; +import { Sizing, Direction, SerializableGrid, ISerializedGrid } from 'vs/base/browser/ui/grid/grid'; import { IEditor } from 'vs/editor/common/editorCommon'; +import { WorkbenchLayout } from 'vs/workbench/browser/layout'; interface WorkbenchParams { configuration: IWindowConfiguration; @@ -141,6 +142,8 @@ type FontAliasingOption = 'default' | 'antialiased' | 'none' | 'auto'; const fontAliasingValues: FontAliasingOption[] = ['antialiased', 'none', 'auto']; +type WorkbenchView = StatusbarPart | TitlebarPart | SidebarPart | EditorPart | ActivitybarPart | PanelPart; + const Identifiers = { WORKBENCH_CONTAINER: 'workbench.main.container', TITLEBAR_PART: 'workbench.parts.titlebar', @@ -168,8 +171,15 @@ interface IZenMode { wasPanelVisible: boolean; } +interface IWorkbenchUIState { + lastPanelHeight?: number; + lastPanelWidth?: number; + lastSidebarDimension?: number; +} + export class Workbench extends Disposable implements IPartService { + private static readonly workbenchGridUIStateStorageKey = 'workbench.layout.state'; private static readonly sidebarHiddenStorageKey = 'workbench.sidebar.hidden'; private static readonly menubarVisibilityConfigurationKey = 'window.menuBarVisibility'; private static readonly panelHiddenStorageKey = 'workbench.panel.hidden'; @@ -200,7 +210,7 @@ export class Workbench extends Disposable implements IPartService { private fileService: IFileService; private quickInput: QuickInputService; - private workbenchLayout: WorkbenchLayout; + private workbenchGrid: SerializableGrid | WorkbenchLayout; private titlebarPart: TitlebarPart; private activitybarPart: ActivitybarPart; @@ -224,6 +234,7 @@ export class Workbench extends Disposable implements IPartService { private fontAliasing: FontAliasingOption; private hasInitialFilesToOpen: boolean; private shouldCenterLayout = false; + private uiState: IWorkbenchUIState = {}; private inZenMode: IContextKey; private sideBarVisibleContext: IContextKey; @@ -944,6 +955,22 @@ export class Workbench extends Disposable implements IPartService { return getTitleBarStyle(this.configurationService, this.environmentService) === 'custom'; } + private saveLastPanelDimension(): void { + if (!(this.workbenchGrid instanceof SerializableGrid)) { + return; + } + + if (this.panelPosition === Position.BOTTOM) { + this.uiState.lastPanelHeight = this.workbenchGrid.getViewSize(this.panelPart); + } else { + this.uiState.lastPanelWidth = this.workbenchGrid.getViewSize(this.panelPart); + } + } + + private getLastPanelDimension(position: Position): number | undefined { + return position === Position.BOTTOM ? this.uiState.lastPanelHeight : this.uiState.lastPanelWidth; + } + private setStatusBarHidden(hidden: boolean, skipLayout?: boolean): void { this.statusBarHidden = hidden; @@ -956,7 +983,45 @@ export class Workbench extends Disposable implements IPartService { // Layout if (!skipLayout) { - this.workbenchLayout.layout(); + if (this.workbenchGrid instanceof SerializableGrid) { + if (this.statusBarHidden) { + this.workbenchGrid.removeView(this.statusbarPart); + } else { + if (!this.sideBarHidden) { + this.uiState.lastSidebarDimension = this.workbenchGrid.getViewSize(this.sidebarPart); + this.workbenchGrid.removeView(this.sidebarPart); + } + + if (!this.activityBarHidden) { + this.workbenchGrid.removeView(this.activitybarPart); + } + + if (!this.panelHidden) { + this.saveLastPanelDimension(); + this.workbenchGrid.removeView(this.panelPart); + } + + this.workbenchGrid.addView(this.statusbarPart, Sizing.Split, this.editorPart, Direction.Down); + + const sidebarDirection = this.sideBarPosition === Position.LEFT ? Direction.Left : Direction.Right; + if (!this.activityBarHidden) { + this.workbenchGrid.addView(this.activitybarPart, Sizing.Split, this.editorPart, sidebarDirection); + } + + if (!this.sideBarHidden) { + this.workbenchGrid.addView(this.sidebarPart, this.uiState.lastSidebarDimension, this.editorPart, sidebarDirection); + } + + if (!this.panelHidden) { + this.workbenchGrid.addView(this.panelPart, this.getLastPanelDimension(this.panelPosition), this.editorPart, this.panelPosition === Position.BOTTOM ? Direction.Down : Direction.Right); + } + } + + const size = DOM.getClientArea(this.container); + this.workbenchGrid.layout(size.width, size.height); + } else { + this.workbenchGrid.layout(); + } } } @@ -973,23 +1038,72 @@ export class Workbench extends Disposable implements IPartService { } private createWorkbenchLayout(): void { - this.workbenchLayout = this.instantiationService.createInstance( - WorkbenchLayout, - this.container, - this.workbench, - { - titlebar: this.titlebarPart, - activitybar: this.activitybarPart, - editor: this.editorPart, - sidebar: this.sidebarPart, - panel: this.panelPart, - statusbar: this.statusbarPart, - }, - this.quickOpen, - this.quickInput, - this.notificationsCenter, - this.notificationsToasts - ); + if (this.configurationService.getValue('workbench.useExperimentalGridLayout')) { + const serializedWorkbenchGridString = this.storageService.get(Workbench.workbenchGridUIStateStorageKey, StorageScope.GLOBAL, undefined); + + if (serializedWorkbenchGridString) { + console.log(serializedWorkbenchGridString); + const serializedWorkbenchGrid = JSON.parse(serializedWorkbenchGridString) as ISerializedGrid; + this.workbenchGrid = SerializableGrid.deserialize(serializedWorkbenchGrid, { + fromJSON: (serializedView: { type: Parts }): WorkbenchView => { + switch (serializedView.type) { + case Parts.ACTIVITYBAR_PART: return this.activitybarPart; + case Parts.EDITOR_PART: return this.editorPart; + case Parts.PANEL_PART: return this.panelPart; + case Parts.SIDEBAR_PART: return this.sidebarPart; + case Parts.STATUSBAR_PART: return this.statusbarPart; + case Parts.TITLEBAR_PART: return this.titlebarPart; + } + + return null; + } + }); + } else { + this.workbenchGrid = new SerializableGrid(this.editorPart, { proportionalLayout: false }); + + const sidebarDirection = this.sideBarPosition === Position.RIGHT ? Direction.Right : Direction.Left; + + if (!this.statusBarHidden) { + this.workbenchGrid.addView(this.statusbarPart, Sizing.Split, this.editorPart, Direction.Down); + } + + if (this.useCustomTitleBarStyle) { + this.workbenchGrid.addView(this.titlebarPart, Sizing.Split, this.editorPart, Direction.Up); + } + + if (!this.activityBarHidden) { + this.workbenchGrid.addView(this.activitybarPart, Sizing.Split, this.editorPart, sidebarDirection); + } + + if (!this.sideBarHidden) { + this.workbenchGrid.addView(this.sidebarPart, Sizing.Split, this.editorPart, sidebarDirection); + } + + if (!this.panelHidden) { + this.workbenchGrid.addView(this.panelPart, Sizing.Split, this.editorPart, this.panelPosition === Position.BOTTOM ? Direction.Down : Direction.Right); + } + } + + this.workbench.appendChild(this.workbenchGrid.element); + } else { + this.workbenchGrid = this.instantiationService.createInstance( + WorkbenchLayout, + this.container, + this.workbench, + { + titlebar: this.titlebarPart, + activitybar: this.activitybarPart, + editor: this.editorPart, + sidebar: this.sidebarPart, + panel: this.panelPart, + statusbar: this.statusbarPart, + }, + this.quickOpen, + this.quickInput, + this.notificationsCenter, + this.notificationsToasts + ); + } } private renderWorkbench(): void { @@ -1131,6 +1245,10 @@ export class Workbench extends Disposable implements IPartService { this.toggleZenMode(true); } } + + if (this.workbenchGrid instanceof SerializableGrid) { + this.storageService.store(Workbench.workbenchGridUIStateStorageKey, JSON.stringify(this.workbenchGrid.serialize()), StorageScope.GLOBAL); + } } dispose(): void { @@ -1218,7 +1336,7 @@ export class Workbench extends Disposable implements IPartService { getTitleBarOffset(): number { let offset = 0; if (this.isVisible(Parts.TITLEBAR_PART)) { - offset = this.workbenchLayout.partLayoutInfo.titlebar.height; + offset = this.workbenchGrid instanceof SerializableGrid ? this.workbenchGrid.getViewSize2(this.titlebarPart).height : this.workbenchGrid.partLayoutInfo.titlebar.height; if (isMacintosh || this.menubarVisibility === 'hidden') { offset /= browser.getZoomFactor(); } @@ -1320,7 +1438,66 @@ export class Workbench extends Disposable implements IPartService { this.contextViewService.layout(); if (this.workbenchStarted && !this.workbenchShutdown) { - this.workbenchLayout.layout(options); + if (this.workbenchGrid instanceof SerializableGrid) { + const dimensions = DOM.getClientArea(this.container); + DOM.position(this.workbench, 0, 0, 0, 0, 'relative'); + DOM.size(this.workbench, dimensions.width, dimensions.height); + + // Handle titlebar visibility + const hideTitlebar = browser.isFullscreen() && (this.getMenubarVisibility() === 'default' || (this.getMenubarVisibility() === 'toggle' && !this.menubarToggled)); + this.setTitlebarVisibility(!hideTitlebar); + + this.workbenchGrid.layout(dimensions.width, dimensions.height); + } else { + this.workbenchGrid.layout(options); + } + } + } + + setTitlebarVisibility(visible: boolean): void { + if (!(this.workbenchGrid instanceof SerializableGrid)) { + return; + } + + let wasVisible = false; + + try { + wasVisible = !!this.workbenchGrid.getViewSize(this.titlebarPart); + } catch { } + + if (wasVisible !== visible) { + if (!visible) { + this.workbenchGrid.removeView(this.titlebarPart); + } else { + if (!this.sideBarHidden) { + this.uiState.lastSidebarDimension = this.workbenchGrid.getViewSize(this.sidebarPart); + this.workbenchGrid.removeView(this.sidebarPart); + } + + if (!this.activityBarHidden) { + this.workbenchGrid.removeView(this.activitybarPart); + } + + if (!this.panelHidden && this.panelPosition !== Position.BOTTOM) { + this.saveLastPanelDimension(); + this.workbenchGrid.removeView(this.panelPart); + } + + // Add views + this.workbenchGrid.addView(this.titlebarPart, Sizing.Split, this.editorPart, Direction.Up); + + if (!this.activityBarHidden) { + this.workbenchGrid.addView(this.activitybarPart, Sizing.Split, this.editorPart, this.sideBarPosition === Position.LEFT ? Direction.Left : Direction.Right); + } + + if (!this.sideBarHidden) { + this.workbenchGrid.addView(this.sidebarPart, this.uiState.lastSidebarDimension, this.editorPart, this.sideBarPosition === Position.LEFT ? Direction.Left : Direction.Right); + } + + if (!this.panelHidden && this.panelPosition !== Position.BOTTOM) { + this.workbenchGrid.addView(this.panelPart, this.getLastPanelDimension(this.panelPosition), this.editorPart, Direction.Right); + } + } } } @@ -1347,11 +1524,19 @@ export class Workbench extends Disposable implements IPartService { } resizePart(part: Parts, sizeChange: number): void { + let view: WorkbenchView; switch (part) { case Parts.SIDEBAR_PART: + view = this.sidebarPart; case Parts.PANEL_PART: + view = this.panelPart; case Parts.EDITOR_PART: - this.workbenchLayout.resizePart(part, sizeChange); + view = this.editorPart; + if (this.workbenchGrid instanceof SerializableGrid) { + this.workbenchGrid.resizeView(view, this.workbenchGrid.getViewSize(view) + sizeChange); + } else { + this.workbenchGrid.resizePart(part, sizeChange); + } break; default: return; // Cannot resize other parts @@ -1363,7 +1548,20 @@ export class Workbench extends Disposable implements IPartService { // Layout if (!skipLayout) { - this.workbenchLayout.layout(); + if (this.workbenchGrid instanceof SerializableGrid) { + if (hidden) { + this.workbenchGrid.removeView(this.activitybarPart); + } else { + const refView = this.sideBarHidden ? this.editorPart : this.sidebarPart; + const direction = this.sideBarPosition === Position.LEFT ? Direction.Left : Direction.Right; + this.workbenchGrid.addView(this.activitybarPart, Sizing.Split, refView, direction); + } + + const dimensions = DOM.getClientArea(this.container); + this.workbenchGrid.layout(dimensions.width, dimensions.height); + } else { + this.workbenchGrid.layout(); + } } } @@ -1413,7 +1611,33 @@ export class Workbench extends Disposable implements IPartService { // Layout if (!skipLayout) { - this.workbenchLayout.layout(); + if (this.workbenchGrid instanceof SerializableGrid) { + if (this.sideBarHidden) { + this.uiState.lastSidebarDimension = this.workbenchGrid.getViewSize(this.sidebarPart); + this.workbenchGrid.removeView(this.sidebarPart); + } else { + + const removePanelFirst = !this.panelHidden && this.panelPosition === Position.BOTTOM; + const refView = !this.panelHidden && this.panelPosition === Position.RIGHT && this.sideBarPosition === Position.RIGHT ? this.panelPart : this.editorPart; + const direction = this.sideBarPosition === Position.RIGHT ? Direction.Right : Direction.Left; + + if (removePanelFirst) { + this.saveLastPanelDimension(); + this.workbenchGrid.removeView(this.panelPart); + } + + this.workbenchGrid.addView(this.sidebarPart, this.uiState.lastSidebarDimension !== undefined ? this.uiState.lastSidebarDimension : Sizing.Split, refView, direction); + + if (removePanelFirst) { + this.workbenchGrid.addView(this.panelPart, this.getLastPanelDimension(this.panelPosition), this.editorPart, this.panelPosition === Position.BOTTOM ? Direction.Down : Direction.Right); + } + } + + const dimensions = DOM.getClientArea(this.container); + this.workbenchGrid.layout(dimensions.width, dimensions.height); + } else { + this.workbenchGrid.layout(); + } } } @@ -1451,16 +1675,40 @@ export class Workbench extends Disposable implements IPartService { // Layout if (!skipLayout) { - this.workbenchLayout.layout(); + if (this.workbenchGrid instanceof SerializableGrid) { + if (this.panelHidden) { + this.saveLastPanelDimension(); + this.workbenchGrid.removeView(this.panelPart); + } else { + this.workbenchGrid.addView(this.panelPart, this.getLastPanelDimension(this.panelPosition) !== undefined ? this.getLastPanelDimension(this.panelPosition) : Sizing.Split, this.editorPart, this.panelPosition === Position.BOTTOM ? Direction.Down : Direction.Right); + } + + const dimensions = DOM.getClientArea(this.container); + this.workbenchGrid.layout(dimensions.width, dimensions.height); + } else { + this.workbenchGrid.layout(); + } } } toggleMaximizedPanel(): void { - this.workbenchLayout.layout({ toggleMaximizedPanel: true, source: Parts.PANEL_PART }); + if (this.workbenchGrid instanceof SerializableGrid) { + this.workbenchGrid.maximizeViewSize(this.panelPart); + } else { + this.workbenchGrid.layout({ toggleMaximizedPanel: true, source: Parts.PANEL_PART }); + } } isPanelMaximized(): boolean { - return this.workbenchLayout.isPanelMaximized(); + if (this.workbenchGrid instanceof SerializableGrid) { + try { + return this.workbenchGrid.getViewSize2(this.panelPart).height === this.panelPart.maximumHeight; + } catch (e) { + return false; + } + } else { + return this.workbenchGrid.isPanelMaximized(); + } } getSideBarPosition(): Position { @@ -1468,6 +1716,8 @@ export class Workbench extends Disposable implements IPartService { } setSideBarPosition(position: Position): void { + const wasHidden = this.sideBarHidden; + if (this.sideBarHidden) { this.setSideBarHidden(false, true /* Skip Layout */); } @@ -1487,15 +1737,50 @@ export class Workbench extends Disposable implements IPartService { this.sidebarPart.updateStyles(); // Layout - this.workbenchLayout.layout(); + if (this.workbenchGrid instanceof SerializableGrid) { + const refView = !this.panelHidden && this.panelPosition === position ? this.panelPart : this.editorPart; + const direction = position === Position.RIGHT ? Direction.Right : Direction.Left; + const removePanelFirst = !this.panelHidden && this.panelPosition === Position.BOTTOM; + + if (removePanelFirst) { + this.saveLastPanelDimension(); + this.workbenchGrid.removeView(this.panelPart); + } + + if (!this.activityBarHidden) { + this.workbenchGrid.moveView(this.activitybarPart, Sizing.Split, refView, direction); + } + + if (wasHidden) { + this.workbenchGrid.addView(this.sidebarPart, this.uiState.lastSidebarDimension ? this.uiState.lastSidebarDimension : Sizing.Split, refView, direction); + } else { + this.uiState.lastSidebarDimension = this.workbenchGrid.getViewSize(this.sidebarPart); + this.workbenchGrid.moveView(this.sidebarPart, this.uiState.lastSidebarDimension, refView, direction); + } + + if (removePanelFirst) { + this.workbenchGrid.addView(this.panelPart, this.getLastPanelDimension(this.panelPosition), this.editorPart, this.panelPosition === Position.BOTTOM ? Direction.Down : Direction.Right); + } + + const dimensions = DOM.getClientArea(this.container); + this.workbenchGrid.layout(dimensions.width, dimensions.height); + } else { + this.workbenchGrid.layout(); + } } setMenubarVisibility(visibility: MenuBarVisibility, skipLayout: boolean): void { if (this.menubarVisibility !== visibility) { this.menubarVisibility = visibility; + // Layout if (!skipLayout) { - this.workbenchLayout.layout(); + if (this.workbenchGrid instanceof SerializableGrid) { + const dimensions = DOM.getClientArea(this.container); + this.workbenchGrid.layout(dimensions.width, dimensions.height); + } else { + this.workbenchGrid.layout(); + } } } } @@ -1509,8 +1794,12 @@ export class Workbench extends Disposable implements IPartService { } setPanelPosition(position: Position): void { + const wasHidden = this.panelHidden; + if (this.panelHidden) { this.setPanelHidden(false, true /* Skip Layout */); + } else { + this.saveLastPanelDimension(); } const newPositionValue = (position === Position.BOTTOM) ? 'bottom' : 'right'; @@ -1526,8 +1815,17 @@ export class Workbench extends Disposable implements IPartService { this.panelPart.updateStyles(); // Layout - this.workbenchLayout.layout(); - } + if (this.workbenchGrid instanceof SerializableGrid) { + if (wasHidden) { + this.workbenchGrid.addView(this.panelPart, this.getLastPanelDimension(position) !== undefined ? this.getLastPanelDimension(position) : Sizing.Split, this.editorPart, position === Position.BOTTOM ? Direction.Down : Direction.Right); + } else { + this.workbenchGrid.moveView(this.panelPart, this.getLastPanelDimension(position) !== undefined ? this.getLastPanelDimension(position) : Sizing.Split, this.editorPart, position === Position.BOTTOM ? Direction.Down : Direction.Right); + } - //#endregion -} + const dimensions = DOM.getClientArea(this.container); + this.workbenchGrid.layout(dimensions.width, dimensions.height); + } else { + this.workbenchGrid.layout(); + } + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/debug.contribution.css b/src/vs/workbench/parts/debug/browser/media/debug.contribution.css index 330f9e8a0b9..7a5e0f3cfa2 100644 --- a/src/vs/workbench/parts/debug/browser/media/debug.contribution.css +++ b/src/vs/workbench/parts/debug/browser/media/debug.contribution.css @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ /* Activity Bar */ -.monaco-workbench > .activitybar .monaco-action-bar .action-label.debug { +.monaco-workbench .activitybar .monaco-action-bar .action-label.debug { -webkit-mask: url('debug-dark.svg') no-repeat 50% 50%; } @@ -112,7 +112,7 @@ /* Debug status */ /* A very precise css rule to overwrite the display set in statusbar.css */ -.monaco-workbench > .part.statusbar > .statusbar-item > .debug-statusbar-item > a { +.monaco-workbench .part.statusbar > .statusbar-item > .debug-statusbar-item > a { display: flex; padding: 0 5px 0 5px; } diff --git a/src/vs/workbench/parts/debug/browser/media/debugViewlet.css b/src/vs/workbench/parts/debug/browser/media/debugViewlet.css index 38b36067f54..7cf772fb7b0 100644 --- a/src/vs/workbench/parts/debug/browser/media/debugViewlet.css +++ b/src/vs/workbench/parts/debug/browser/media/debugViewlet.css @@ -54,7 +54,7 @@ background: url('repl-inverse.svg') center center no-repeat; } -.monaco-workbench > .part > .title > .title-actions .start-debug-action-item { +.monaco-workbench .part > .title > .title-actions .start-debug-action-item { display: flex; align-items: center; font-size: 11px; @@ -68,7 +68,7 @@ border-radius: 4px; } -.monaco-workbench > .part > .title > .title-actions .start-debug-action-item .icon { +.monaco-workbench .part > .title > .title-actions .start-debug-action-item .icon { height: 20px; width: 20px; background: url('start.svg') no-repeat; @@ -78,8 +78,8 @@ transition: transform 50ms ease; } -.vs-dark .monaco-workbench > .part > .title > .title-actions .start-debug-action-item .icon, -.hc-black .monaco-workbench > .part > .title > .title-actions .start-debug-action-item .icon { +.vs-dark .monaco-workbench .part > .title > .title-actions .start-debug-action-item .icon, +.hc-black .monaco-workbench .part > .title > .title-actions .start-debug-action-item .icon { background-image: url('start-inverse.svg'); } @@ -95,7 +95,7 @@ cursor: initial; } -.monaco-workbench > .part > .title > .title-actions .start-debug-action-item .icon.active { +.monaco-workbench .part > .title > .title-actions .start-debug-action-item .icon.active { transform: scale(1.272019649, 1.272019649); } diff --git a/src/vs/workbench/parts/debug/browser/statusbarColorProvider.ts b/src/vs/workbench/parts/debug/browser/statusbarColorProvider.ts index f3cdc5b0192..43591298a91 100644 --- a/src/vs/workbench/parts/debug/browser/statusbarColorProvider.ts +++ b/src/vs/workbench/parts/debug/browser/statusbarColorProvider.ts @@ -79,7 +79,7 @@ export class StatusBarColorProvider extends Themable implements IWorkbenchContri this.styleElement = createStyleSheet(container); } - this.styleElement.innerHTML = `.monaco-workbench > .part.statusbar > .statusbar-item.has-beak:before { border-bottom-color: ${backgroundColor} !important; }`; + this.styleElement.innerHTML = `.monaco-workbench .part.statusbar > .statusbar-item.has-beak:before { border-bottom-color: ${backgroundColor} !important; }`; } private getColorKey(noFolderColor: string, debuggingColor: string, normalColor: string): string { @@ -115,6 +115,6 @@ export function isStatusbarInDebugMode(debugService: IDebugService): boolean { registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const statusBarItemDebuggingForeground = theme.getColor(STATUS_BAR_DEBUGGING_FOREGROUND); if (statusBarItemDebuggingForeground) { - collector.addRule(`.monaco-workbench > .part.statusbar.debugging > .statusbar-item .mask-icon { background-color: ${statusBarItemDebuggingForeground} !important; }`); + collector.addRule(`.monaco-workbench .part.statusbar.debugging > .statusbar-item .mask-icon { background-color: ${statusBarItemDebuggingForeground} !important; }`); } }); diff --git a/src/vs/workbench/parts/extensions/electron-browser/media/extensions.css b/src/vs/workbench/parts/extensions/electron-browser/media/extensions.css index 52362be55a0..bdd6185f3dd 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/media/extensions.css +++ b/src/vs/workbench/parts/extensions/electron-browser/media/extensions.css @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-workbench > .activitybar > .content .monaco-action-bar .action-label.extensions { +.monaco-workbench .activitybar > .content .monaco-action-bar .action-label.extensions { -webkit-mask: url('extensions-dark.svg') no-repeat 50% 50%; } diff --git a/src/vs/workbench/parts/feedback/electron-browser/feedbackStatusbarItem.ts b/src/vs/workbench/parts/feedback/electron-browser/feedbackStatusbarItem.ts index 2b126ff033c..05db4b3c1db 100644 --- a/src/vs/workbench/parts/feedback/electron-browser/feedbackStatusbarItem.ts +++ b/src/vs/workbench/parts/feedback/electron-browser/feedbackStatusbarItem.ts @@ -168,6 +168,6 @@ class HideAction extends Action { registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const statusBarItemHoverBackground = theme.getColor(STATUS_BAR_ITEM_HOVER_BACKGROUND); if (statusBarItemHoverBackground) { - collector.addRule(`.monaco-workbench > .part.statusbar > .statusbar-item .monaco-dropdown.send-feedback:hover { background-color: ${statusBarItemHoverBackground}; }`); + collector.addRule(`.monaco-workbench .part.statusbar > .statusbar-item .monaco-dropdown.send-feedback:hover { background-color: ${statusBarItemHoverBackground}; }`); } }); diff --git a/src/vs/workbench/parts/files/electron-browser/media/explorerviewlet.css b/src/vs/workbench/parts/files/electron-browser/media/explorerviewlet.css index 7e059485bbf..746324d6769 100644 --- a/src/vs/workbench/parts/files/electron-browser/media/explorerviewlet.css +++ b/src/vs/workbench/parts/files/electron-browser/media/explorerviewlet.css @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ /* Activity Bar */ -.monaco-workbench > .activitybar .monaco-action-bar .action-label.explore { +.monaco-workbench .activitybar .monaco-action-bar .action-label.explore { -webkit-mask: url('files-dark.svg') no-repeat 50% 50%; } diff --git a/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts b/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts index 83be58a4ade..0fee458eec6 100644 --- a/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts +++ b/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts @@ -24,7 +24,7 @@ interface IConfiguration extends IWindowsConfiguration { update: { channel: string; }; telemetry: { enableCrashReporter: boolean }; keyboard: { touchbar: { enabled: boolean } }; - workbench: { tree: { horizontalScrolling: boolean } }; + workbench: { tree: { horizontalScrolling: boolean }, useExperimentalGridLayout: boolean }; files: { useExperimentalFileWatcher: boolean, watcherExclude: object }; } @@ -41,6 +41,7 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo private windowsSmoothScrollingWorkaround: boolean; private experimentalFileWatcher: boolean; private fileWatcherExclude: object; + private useGridLayout: boolean; private firstFolderResource?: URI; private extensionHostRestarter: RunOnceScheduler; @@ -144,6 +145,12 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo changed = true; } + // Workbench Grid Layout + if (config.workbench && typeof config.workbench.useExperimentalGridLayout === 'boolean' && config.workbench.useExperimentalGridLayout !== this.useGridLayout) { + this.useGridLayout = config.workbench.useExperimentalGridLayout; + changed = true; + } + // Notify only when changed and we are the focused window (avoids notification spam across windows) if (notify && changed) { this.doConfirm( diff --git a/src/vs/workbench/parts/scm/electron-browser/media/scmViewlet.css b/src/vs/workbench/parts/scm/electron-browser/media/scmViewlet.css index 84ad51a4b7b..848ee89aee3 100644 --- a/src/vs/workbench/parts/scm/electron-browser/media/scmViewlet.css +++ b/src/vs/workbench/parts/scm/electron-browser/media/scmViewlet.css @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-workbench > .activitybar > .content .monaco-action-bar .action-label.scm { +.monaco-workbench .activitybar > .content .monaco-action-bar .action-label.scm { -webkit-mask: url('icon-dark.svg') no-repeat 50% 50%; } diff --git a/src/vs/workbench/parts/search/electron-browser/media/search.contribution.css b/src/vs/workbench/parts/search/electron-browser/media/search.contribution.css index eb5ed7fd15f..0fe89c6f881 100644 --- a/src/vs/workbench/parts/search/electron-browser/media/search.contribution.css +++ b/src/vs/workbench/parts/search/electron-browser/media/search.contribution.css @@ -4,6 +4,6 @@ *--------------------------------------------------------------------------------------------*/ /* Activity Bar */ -.monaco-workbench > .activitybar .monaco-action-bar .action-label.search { +.monaco-workbench .activitybar .monaco-action-bar .action-label.search { -webkit-mask: url('search-dark.svg') no-repeat 50% 50%; } \ No newline at end of file diff --git a/src/vs/workbench/parts/watermark/electron-browser/watermark.css b/src/vs/workbench/parts/watermark/electron-browser/watermark.css index 8284b8442dd..15c57d04c3d 100644 --- a/src/vs/workbench/parts/watermark/electron-browser/watermark.css +++ b/src/vs/workbench/parts/watermark/electron-browser/watermark.css @@ -11,11 +11,11 @@ background-position-y: 50%; } -.monaco-workbench > .part.editor > .content > .watermark { +.monaco-workbench .part.editor > .content > .watermark { display: none; /* only visible when no editors are opened */ } -.monaco-workbench > .part.editor.has-watermark > .content.empty > .watermark { +.monaco-workbench .part.editor.has-watermark > .content.empty > .watermark { display: block; position: absolute; width: 100%; @@ -25,50 +25,50 @@ overflow: hidden; } -.monaco-workbench > .part.editor > .content.empty > .watermark > .watermark-box { +.monaco-workbench .part.editor > .content.empty > .watermark > .watermark-box { display: inline-table; border-collapse: separate; border-spacing: 13px 17px; } -.monaco-workbench > .part.editor.max-height-478px > .content.empty > .watermark { +.monaco-workbench .part.editor.max-height-478px > .content.empty > .watermark { display: none; } -.monaco-workbench > .part.editor > .content.empty > .watermark dl { +.monaco-workbench .part.editor > .content.empty > .watermark dl { display: table-row; opacity: .8; cursor: default; } -.monaco-workbench > .part.editor > .content.empty > .watermark dt { +.monaco-workbench .part.editor > .content.empty > .watermark dt { text-align: right; letter-spacing: 0.04em } -.monaco-workbench > .part.editor > .content.empty > .watermark dd { +.monaco-workbench .part.editor > .content.empty > .watermark dd { text-align: left; } -.monaco-workbench > .part.editor > .content.empty > .watermark dt, -.monaco-workbench > .part.editor > .content.empty > .watermark dd { +.monaco-workbench .part.editor > .content.empty > .watermark dt, +.monaco-workbench .part.editor > .content.empty > .watermark dd { display: table-cell; } -.monaco-workbench > .part.editor > .content.empty > .watermark dt, -.monaco-workbench > .part.editor > .content.empty > .watermark dl { +.monaco-workbench .part.editor > .content.empty > .watermark dt, +.monaco-workbench .part.editor > .content.empty > .watermark dl { color: rgba(0,0,0,.68); } -.vs-dark .monaco-workbench > .part.editor > .content.empty > .watermark dt, -.vs-dark .monaco-workbench > .part.editor > .content.empty > .watermark dl { +.vs-dark .monaco-workbench .part.editor > .content.empty > .watermark dt, +.vs-dark .monaco-workbench .part.editor > .content.empty > .watermark dl { color: rgba(255,255,255,.6); } -.hc-black .monaco-workbench > .part.editor > .content.empty > .watermark dt { +.hc-black .monaco-workbench .part.editor > .content.empty > .watermark dt { color: #FFF; } -.hc-black .monaco-workbench > .part.editor > .content.empty > .watermark dl { +.hc-black .monaco-workbench .part.editor > .content.empty > .watermark dl { color: #FFF; opacity: 1; } diff --git a/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.css b/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.css index 94d52d379fd..75a18c03da3 100644 --- a/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.css +++ b/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.css @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-workbench > .part.editor > .content .welcomePageContainer { +.monaco-workbench .part.editor > .content .welcomePageContainer { align-items: center; display: flex; justify-content: center; @@ -11,51 +11,51 @@ min-height: 100%; } -.monaco-workbench > .part.editor > .content .welcomePage { +.monaco-workbench .part.editor > .content .welcomePage { width: 90%; max-width: 1200px; font-size: 10px; } -.monaco-workbench > .part.editor > .content .welcomePage .row { +.monaco-workbench .part.editor > .content .welcomePage .row { display: flex; flex-flow: row; } -.monaco-workbench > .part.editor > .content .welcomePage .row .section { +.monaco-workbench .part.editor > .content .welcomePage .row .section { overflow: hidden; } -.monaco-workbench > .part.editor > .content .welcomePage .row .splash { +.monaco-workbench .part.editor > .content .welcomePage .row .splash { overflow: hidden; } -.monaco-workbench > .part.editor > .content .welcomePage .row .commands { +.monaco-workbench .part.editor > .content .welcomePage .row .commands { overflow: hidden; } -.monaco-workbench > .part.editor > .content .welcomePage .row .commands .list { +.monaco-workbench .part.editor > .content .welcomePage .row .commands .list { overflow: hidden; } -.monaco-workbench > .part.editor > .content .welcomePage p { +.monaco-workbench .part.editor > .content .welcomePage p { font-size: 1.3em; } -.monaco-workbench > .part.editor > .content .welcomePage .keyboard { +.monaco-workbench .part.editor > .content .welcomePage .keyboard { font-family: "Lucida Grande", sans-serif;/* Keyboard shortcuts */ } -.monaco-workbench > .part.editor > .content .welcomePage a { +.monaco-workbench .part.editor > .content .welcomePage a { text-decoration: none; } -.monaco-workbench > .part.editor > .content .welcomePage a:focus { +.monaco-workbench .part.editor > .content .welcomePage a:focus { outline: 1px solid -webkit-focus-ring-color; outline-offset: -1px; } -.monaco-workbench > .part.editor > .content .welcomePage h1 { +.monaco-workbench .part.editor > .content .welcomePage h1 { padding: 0; margin: 0; border: none; @@ -64,28 +64,28 @@ white-space: nowrap; } -.monaco-workbench > .part.editor > .content .welcomePage .title { +.monaco-workbench .part.editor > .content .welcomePage .title { margin-top: 1em; margin-bottom: 1em; flex: 1 100%; } -.monaco-workbench > .part.editor > .content .welcomePage .subtitle { +.monaco-workbench .part.editor > .content .welcomePage .subtitle { margin-top: .8em; font-size: 2.6em; display: block; } -.hc-black .monaco-workbench > .part.editor > .content .welcomePage .subtitle { +.hc-black .monaco-workbench .part.editor > .content .welcomePage .subtitle { font-weight: 200; } -.monaco-workbench > .part.editor > .content .welcomePage .splash, -.monaco-workbench > .part.editor > .content .welcomePage .commands { +.monaco-workbench .part.editor > .content .welcomePage .splash, +.monaco-workbench .part.editor > .content .welcomePage .commands { flex: 1 1 0; } -.monaco-workbench > .part.editor > .content .welcomePage h2 { +.monaco-workbench .part.editor > .content .welcomePage h2 { font-weight: 200; margin-top: 17px; margin-bottom: 5px; @@ -93,62 +93,62 @@ line-height: initial; } -.monaco-workbench > .part.editor > .content .welcomePage .splash .section { +.monaco-workbench .part.editor > .content .welcomePage .splash .section { margin-bottom: 5em; } -.monaco-workbench > .part.editor > .content .welcomePage .splash ul { +.monaco-workbench .part.editor > .content .welcomePage .splash ul { margin: 0; font-size: 1.3em; list-style: none; padding: 0; } -.monaco-workbench > .part.editor > .content .welcomePage .splash li { +.monaco-workbench .part.editor > .content .welcomePage .splash li { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } -.monaco-workbench > .part.editor > .content .welcomePage.emptyRecent .splash .recent .list { +.monaco-workbench .part.editor > .content .welcomePage.emptyRecent .splash .recent .list { display: none; } -.monaco-workbench > .part.editor > .content .welcomePage .splash .recent .none { +.monaco-workbench .part.editor > .content .welcomePage .splash .recent .none { display: none; } -.monaco-workbench > .part.editor > .content .welcomePage.emptyRecent .splash .recent .none { +.monaco-workbench .part.editor > .content .welcomePage.emptyRecent .splash .recent .none { display: initial; } -.monaco-workbench > .part.editor > .content .welcomePage .splash .recent li.moreRecent { +.monaco-workbench .part.editor > .content .welcomePage .splash .recent li.moreRecent { margin-top: 5px; } -.monaco-workbench > .part.editor > .content .welcomePage .splash .recent .path { +.monaco-workbench .part.editor > .content .welcomePage .splash .recent .path { padding-left: 1em; } -.monaco-workbench > .part.editor > .content .welcomePage .splash .title, -.monaco-workbench > .part.editor > .content .welcomePage .splash .showOnStartup { +.monaco-workbench .part.editor > .content .welcomePage .splash .title, +.monaco-workbench .part.editor > .content .welcomePage .splash .showOnStartup { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } -.monaco-workbench > .part.editor > .content .welcomePage .splash .showOnStartup > .checkbox { +.monaco-workbench .part.editor > .content .welcomePage .splash .showOnStartup > .checkbox { vertical-align: bottom; } -.monaco-workbench > .part.editor > .content .welcomePage .commands .list { +.monaco-workbench .part.editor > .content .welcomePage .commands .list { list-style: none; padding: 0; } -.monaco-workbench > .part.editor > .content .welcomePage .commands .item { +.monaco-workbench .part.editor > .content .welcomePage .commands .item { margin: 7px 0px; } -.monaco-workbench > .part.editor > .content .welcomePage .commands .item button { +.monaco-workbench .part.editor > .content .welcomePage .commands .item button { margin: 1px; padding: 12px 10px; width: calc(100% - 2px); @@ -160,7 +160,7 @@ font-family: inherit; } -.monaco-workbench > .part.editor > .content .welcomePage .commands .item button > span { +.monaco-workbench .part.editor > .content .welcomePage .commands .item button > span { display: inline-block; width:100%; min-width: 0; @@ -169,7 +169,7 @@ text-overflow: ellipsis; } -.monaco-workbench > .part.editor > .content .welcomePage .commands .item button h3 { +.monaco-workbench .part.editor > .content .welcomePage .commands .item button h3 { font-weight: normal; font-size: 1em; margin: 0; @@ -180,41 +180,41 @@ text-overflow: ellipsis; } -.monaco-workbench > .part.editor > .content .welcomePage .commands .item button { +.monaco-workbench .part.editor > .content .welcomePage .commands .item button { border: none; } -.hc-black .monaco-workbench > .part.editor > .content .welcomePage .commands .item button > h3 { +.hc-black .monaco-workbench .part.editor > .content .welcomePage .commands .item button > h3 { font-weight: bold; } -.monaco-workbench > .part.editor > .content .welcomePage .commands .item button:focus { +.monaco-workbench .part.editor > .content .welcomePage .commands .item button:focus { outline-style: solid; outline-width: 1px; } -.hc-black .monaco-workbench > .part.editor > .content .welcomePage .commands .item button { +.hc-black .monaco-workbench .part.editor > .content .welcomePage .commands .item button { border-width: 1px; border-style: solid; } -.hc-black .monaco-workbench > .part.editor > .content .welcomePage .commands .item button:hover { +.hc-black .monaco-workbench .part.editor > .content .welcomePage .commands .item button:hover { outline-width: 1px; outline-style: dashed; outline-offset: -5px; } -.monaco-workbench > .part.editor > .content .welcomePage .commands .item button .enabledExtension { +.monaco-workbench .part.editor > .content .welcomePage .commands .item button .enabledExtension { display: none; } -.monaco-workbench > .part.editor > .content .welcomePage .commands .item button .installExtension.installed { +.monaco-workbench .part.editor > .content .welcomePage .commands .item button .installExtension.installed { display: none; } -.monaco-workbench > .part.editor > .content .welcomePage .commands .item button .enabledExtension.installed { +.monaco-workbench .part.editor > .content .welcomePage .commands .item button .enabledExtension.installed { display: inline; } -.monaco-workbench > .part.editor > .content .welcomePageContainer.max-height-685px .title { +.monaco-workbench .part.editor > .content .welcomePageContainer.max-height-685px .title { display: none; } @@ -223,9 +223,9 @@ background-image: url('../../code-icon.svg'); } -.monaco-workbench > .part.editor > .content .welcomePage .mac-only, -.monaco-workbench > .part.editor > .content .welcomePage .windows-only, -.monaco-workbench > .part.editor > .content .welcomePage .linux-only { +.monaco-workbench .part.editor > .content .welcomePage .mac-only, +.monaco-workbench .part.editor > .content .welcomePage .windows-only, +.monaco-workbench .part.editor > .content .welcomePage .linux-only { display: none; } .mac > .monaco-workbench > .part.editor > .content .welcomePage .mac-only { diff --git a/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts b/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts index 0a127775cae..3e7bb14b1af 100644 --- a/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts +++ b/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts @@ -640,43 +640,43 @@ export const welcomePageBackground = registerColor('welcomePage.background', { l registerThemingParticipant((theme, collector) => { const backgroundColor = theme.getColor(welcomePageBackground); if (backgroundColor) { - collector.addRule(`.monaco-workbench > .part.editor > .content .welcomePageContainer { background-color: ${backgroundColor}; }`); + collector.addRule(`.monaco-workbench .part.editor > .content .welcomePageContainer { background-color: ${backgroundColor}; }`); } const foregroundColor = theme.getColor(foreground); if (foregroundColor) { - collector.addRule(`.monaco-workbench > .part.editor > .content .welcomePage .caption { color: ${foregroundColor}; }`); + collector.addRule(`.monaco-workbench .part.editor > .content .welcomePage .caption { color: ${foregroundColor}; }`); } const descriptionColor = theme.getColor(descriptionForeground); if (descriptionColor) { - collector.addRule(`.monaco-workbench > .part.editor > .content .welcomePage .detail { color: ${descriptionColor}; }`); + collector.addRule(`.monaco-workbench .part.editor > .content .welcomePage .detail { color: ${descriptionColor}; }`); } const buttonColor = getExtraColor(theme, buttonBackground, { dark: 'rgba(0, 0, 0, .2)', extra_dark: 'rgba(200, 235, 255, .042)', light: 'rgba(0,0,0,.04)', hc: 'black' }); if (buttonColor) { - collector.addRule(`.monaco-workbench > .part.editor > .content .welcomePage .commands .item button { background: ${buttonColor}; }`); + collector.addRule(`.monaco-workbench .part.editor > .content .welcomePage .commands .item button { background: ${buttonColor}; }`); } const buttonHoverColor = getExtraColor(theme, buttonHoverBackground, { dark: 'rgba(200, 235, 255, .072)', extra_dark: 'rgba(200, 235, 255, .072)', light: 'rgba(0,0,0,.10)', hc: null }); if (buttonHoverColor) { - collector.addRule(`.monaco-workbench > .part.editor > .content .welcomePage .commands .item button:hover { background: ${buttonHoverColor}; }`); + collector.addRule(`.monaco-workbench .part.editor > .content .welcomePage .commands .item button:hover { background: ${buttonHoverColor}; }`); } const link = theme.getColor(textLinkForeground); if (link) { - collector.addRule(`.monaco-workbench > .part.editor > .content .welcomePage a { color: ${link}; }`); + collector.addRule(`.monaco-workbench .part.editor > .content .welcomePage a { color: ${link}; }`); } const activeLink = theme.getColor(textLinkActiveForeground); if (activeLink) { - collector.addRule(`.monaco-workbench > .part.editor > .content .welcomePage a:hover, - .monaco-workbench > .part.editor > .content .welcomePage a:active { color: ${activeLink}; }`); + collector.addRule(`.monaco-workbench .part.editor > .content .welcomePage a:hover, + .monaco-workbench .part.editor > .content .welcomePage a:active { color: ${activeLink}; }`); } const focusColor = theme.getColor(focusBorder); if (focusColor) { - collector.addRule(`.monaco-workbench > .part.editor > .content .welcomePage a:focus { outline-color: ${focusColor}; }`); + collector.addRule(`.monaco-workbench .part.editor > .content .welcomePage a:focus { outline-color: ${focusColor}; }`); } const border = theme.getColor(contrastBorder); if (border) { - collector.addRule(`.monaco-workbench > .part.editor > .content .welcomePage .commands .item button { border-color: ${border}; }`); + collector.addRule(`.monaco-workbench .part.editor > .content .welcomePage .commands .item button { border-color: ${border}; }`); } const activeBorder = theme.getColor(activeContrastBorder); if (activeBorder) { - collector.addRule(`.monaco-workbench > .part.editor > .content .welcomePage .commands .item button:hover { outline-color: ${activeBorder}; }`); + collector.addRule(`.monaco-workbench .part.editor > .content .welcomePage .commands .item button:hover { outline-color: ${activeBorder}; }`); } }); diff --git a/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.css b/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.css index 56a76aa42fe..c83ac377cae 100644 --- a/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.css +++ b/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.css @@ -3,45 +3,45 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-workbench > .part.editor > .content .walkThroughContent { +.monaco-workbench .part.editor > .content .walkThroughContent { box-sizing: border-box; padding: 10px 20px; line-height: 22px; user-select: initial; } -.monaco-workbench > .part.editor > .content .walkThroughContent img { +.monaco-workbench .part.editor > .content .walkThroughContent img { max-width: 100%; max-height: 100%; } -.monaco-workbench > .part.editor > .content .walkThroughContent a { +.monaco-workbench .part.editor > .content .walkThroughContent a { text-decoration: none; } -.monaco-workbench > .part.editor > .content .walkThroughContent a:focus, -.monaco-workbench > .part.editor > .content .walkThroughContent input:focus, -.monaco-workbench > .part.editor > .content .walkThroughContent select:focus, -.monaco-workbench > .part.editor > .content .walkThroughContent textarea:focus { +.monaco-workbench .part.editor > .content .walkThroughContent a:focus, +.monaco-workbench .part.editor > .content .walkThroughContent input:focus, +.monaco-workbench .part.editor > .content .walkThroughContent select:focus, +.monaco-workbench .part.editor > .content .walkThroughContent textarea:focus { outline: 1px solid -webkit-focus-ring-color; outline-offset: -1px; } -.monaco-workbench > .part.editor > .content .walkThroughContent hr { +.monaco-workbench .part.editor > .content .walkThroughContent hr { border: 0; height: 2px; border-bottom: 2px solid; } -.monaco-workbench > .part.editor > .content .walkThroughContent h1, -.monaco-workbench > .part.editor > .content .walkThroughContent h2, -.monaco-workbench > .part.editor > .content .walkThroughContent h3 { +.monaco-workbench .part.editor > .content .walkThroughContent h1, +.monaco-workbench .part.editor > .content .walkThroughContent h2, +.monaco-workbench .part.editor > .content .walkThroughContent h3 { font-weight: lighter; margin-top: 20px; margin-bottom: 10px; } -.monaco-workbench > .part.editor > .content .walkThroughContent h1 { +.monaco-workbench .part.editor > .content .walkThroughContent h1 { padding-bottom: 0.3em; line-height: 1.2; border-bottom-width: 1px; @@ -50,66 +50,66 @@ margin-bottom: 15px; } -.monaco-workbench > .part.editor > .content .walkThroughContent h2 { +.monaco-workbench .part.editor > .content .walkThroughContent h2 { font-size: 30px; margin-top: 30px; } -.monaco-workbench > .part.editor > .content .walkThroughContent h3 { +.monaco-workbench .part.editor > .content .walkThroughContent h3 { font-size: 22px; } -.monaco-workbench > .part.editor > .content .walkThroughContent h4 { +.monaco-workbench .part.editor > .content .walkThroughContent h4 { font-size: 12px; text-transform: uppercase; margin-top: 30px; margin-bottom: 10px; } -.monaco-workbench > .part.editor > .content .walkThroughContent a:hover { +.monaco-workbench .part.editor > .content .walkThroughContent a:hover { text-decoration: underline; } -.monaco-workbench > .part.editor > .content .walkThroughContent table { +.monaco-workbench .part.editor > .content .walkThroughContent table { border-collapse: collapse; } -.monaco-workbench > .part.editor > .content .walkThroughContent table > thead > tr > th { +.monaco-workbench .part.editor > .content .walkThroughContent table > thead > tr > th { text-align: left; border-bottom: 1px solid; } -.monaco-workbench > .part.editor > .content .walkThroughContent table > thead > tr > th, -.monaco-workbench > .part.editor > .content .walkThroughContent table > thead > tr > td, -.monaco-workbench > .part.editor > .content .walkThroughContent table > tbody > tr > th, -.monaco-workbench > .part.editor > .content .walkThroughContent table > tbody > tr > td { +.monaco-workbench .part.editor > .content .walkThroughContent table > thead > tr > th, +.monaco-workbench .part.editor > .content .walkThroughContent table > thead > tr > td, +.monaco-workbench .part.editor > .content .walkThroughContent table > tbody > tr > th, +.monaco-workbench .part.editor > .content .walkThroughContent table > tbody > tr > td { padding: 5px 10px; } -.monaco-workbench > .part.editor > .content .walkThroughContent table > tbody > tr + tr > td { +.monaco-workbench .part.editor > .content .walkThroughContent table > tbody > tr + tr > td { border-top: 1px solid; } -.monaco-workbench > .part.editor > .content .walkThroughContent blockquote { +.monaco-workbench .part.editor > .content .walkThroughContent blockquote { margin: 0 7px 0 5px; padding: 0 16px 0 10px; border-left: 5px solid; } -.monaco-workbench > .part.editor > .content .walkThroughContent code, -.monaco-workbench > .part.editor > .content .walkThroughContent .shortcut { +.monaco-workbench .part.editor > .content .walkThroughContent code, +.monaco-workbench .part.editor > .content .walkThroughContent .shortcut { font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Courier New", monospace, "Droid Sans Fallback"; font-size: 14px; line-height: 19px; } -.monaco-workbench > .part.editor > .content .walkThroughContent blockquote { +.monaco-workbench .part.editor > .content .walkThroughContent blockquote { margin: 0 7px 0 5px; padding: 0 16px 0 10px; border-left: 5px solid; } -.monaco-workbench > .part.editor > .content .walkThroughContent .monaco-tokenized-source { +.monaco-workbench .part.editor > .content .walkThroughContent .monaco-tokenized-source { white-space: pre; } @@ -118,9 +118,9 @@ background-image: url('../../code-icon.svg'); } -.monaco-workbench > .part.editor > .content .walkThroughContent .mac-only, -.monaco-workbench > .part.editor > .content .walkThroughContent .windows-only, -.monaco-workbench > .part.editor > .content .walkThroughContent .linux-only { +.monaco-workbench .part.editor > .content .walkThroughContent .mac-only, +.monaco-workbench .part.editor > .content .walkThroughContent .windows-only, +.monaco-workbench .part.editor > .content .walkThroughContent .linux-only { display: none; } .mac > .monaco-workbench > .part.editor > .content .walkThroughContent .mac-only { @@ -133,7 +133,7 @@ display: initial; } -.hc-black .monaco-workbench > .part.editor > .content .walkThroughContent .monaco-editor { +.hc-black .monaco-workbench .part.editor > .content .walkThroughContent .monaco-editor { border-width: 1px; border-style: solid; } diff --git a/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.ts b/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.ts index 533bd83e758..ed99e06ee17 100644 --- a/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.ts +++ b/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.ts @@ -526,37 +526,37 @@ export const embeddedEditorBackground = registerColor('walkThrough.embeddedEdito registerThemingParticipant((theme, collector) => { const color = getExtraColor(theme, embeddedEditorBackground, { dark: 'rgba(0, 0, 0, .4)', extra_dark: 'rgba(200, 235, 255, .064)', light: '#f4f4f4', hc: null }); if (color) { - collector.addRule(`.monaco-workbench > .part.editor > .content .walkThroughContent .monaco-editor-background, - .monaco-workbench > .part.editor > .content .walkThroughContent .margin-view-overlays { background: ${color}; }`); + collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .monaco-editor-background, + .monaco-workbench .part.editor > .content .walkThroughContent .margin-view-overlays { background: ${color}; }`); } const link = theme.getColor(textLinkForeground); if (link) { - collector.addRule(`.monaco-workbench > .part.editor > .content .walkThroughContent a { color: ${link}; }`); + collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent a { color: ${link}; }`); } const activeLink = theme.getColor(textLinkActiveForeground); if (activeLink) { - collector.addRule(`.monaco-workbench > .part.editor > .content .walkThroughContent a:hover, - .monaco-workbench > .part.editor > .content .walkThroughContent a:active { color: ${activeLink}; }`); + collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent a:hover, + .monaco-workbench .part.editor > .content .walkThroughContent a:active { color: ${activeLink}; }`); } const focusColor = theme.getColor(focusBorder); if (focusColor) { - collector.addRule(`.monaco-workbench > .part.editor > .content .walkThroughContent a:focus { outline-color: ${focusColor}; }`); + collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent a:focus { outline-color: ${focusColor}; }`); } const shortcut = theme.getColor(textPreformatForeground); if (shortcut) { - collector.addRule(`.monaco-workbench > .part.editor > .content .walkThroughContent code, - .monaco-workbench > .part.editor > .content .walkThroughContent .shortcut { color: ${shortcut}; }`); + collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent code, + .monaco-workbench .part.editor > .content .walkThroughContent .shortcut { color: ${shortcut}; }`); } const border = theme.getColor(contrastBorder); if (border) { - collector.addRule(`.monaco-workbench > .part.editor > .content .walkThroughContent .monaco-editor { border-color: ${border}; }`); + collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .monaco-editor { border-color: ${border}; }`); } const quoteBackground = theme.getColor(textBlockQuoteBackground); if (quoteBackground) { - collector.addRule(`.monaco-workbench > .part.editor > .content .walkThroughContent blockquote { background: ${quoteBackground}; }`); + collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent blockquote { background: ${quoteBackground}; }`); } const quoteBorder = theme.getColor(textBlockQuoteBorder); if (quoteBorder) { - collector.addRule(`.monaco-workbench > .part.editor > .content .walkThroughContent blockquote { border-color: ${quoteBorder}; }`); + collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent blockquote { border-color: ${quoteBorder}; }`); } }); diff --git a/src/vs/workbench/services/editor/test/browser/editorService.test.ts b/src/vs/workbench/services/editor/test/browser/editorService.test.ts index 267af62dc3c..45cbfeb9953 100644 --- a/src/vs/workbench/services/editor/test/browser/editorService.test.ts +++ b/src/vs/workbench/services/editor/test/browser/editorService.test.ts @@ -15,7 +15,6 @@ import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService import { EditorService, DelegatingEditorService } from 'vs/workbench/services/editor/browser/editorService'; import { IEditorGroup, IEditorGroupsService, GroupDirection } from 'vs/workbench/services/group/common/editorGroupsService'; import { EditorPart } from 'vs/workbench/browser/parts/editor/editorPart'; -import { Dimension } from 'vs/base/browser/dom'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -79,7 +78,7 @@ suite('Editor service', () => { const part = partInstantiator.createInstance(EditorPart, 'id', false); part.create(document.createElement('div')); - part.layout(new Dimension(400, 300)); + part.layout(400, 300); const testInstantiationService = partInstantiator.createChild(new ServiceCollection([IEditorGroupsService, part])); @@ -152,7 +151,7 @@ suite('Editor service', () => { const part = partInstantiator.createInstance(EditorPart, 'id', false); part.create(document.createElement('div')); - part.layout(new Dimension(400, 300)); + part.layout(400, 300); const testInstantiationService = partInstantiator.createChild(new ServiceCollection([IEditorGroupsService, part])); @@ -293,7 +292,7 @@ suite('Editor service', () => { const part = partInstantiator.createInstance(EditorPart, 'id', false); part.create(document.createElement('div')); - part.layout(new Dimension(400, 300)); + part.layout(400, 300); const testInstantiationService = partInstantiator.createChild(new ServiceCollection([IEditorGroupsService, part])); @@ -332,7 +331,7 @@ suite('Editor service', () => { const part = partInstantiator.createInstance(EditorPart, 'id', false); part.create(document.createElement('div')); - part.layout(new Dimension(400, 300)); + part.layout(400, 300); const testInstantiationService = partInstantiator.createChild(new ServiceCollection([IEditorGroupsService, part])); @@ -366,7 +365,7 @@ suite('Editor service', () => { const part = partInstantiator.createInstance(EditorPart, 'id', false); part.create(document.createElement('div')); - part.layout(new Dimension(400, 300)); + part.layout(400, 300); const testInstantiationService = partInstantiator.createChild(new ServiceCollection([IEditorGroupsService, part])); @@ -576,7 +575,7 @@ suite('Editor service', () => { const part = partInstantiator.createInstance(EditorPart, 'id', false); part.create(document.createElement('div')); - part.layout(new Dimension(400, 300)); + part.layout(400, 300); const testInstantiationService = partInstantiator.createChild(new ServiceCollection([IEditorGroupsService, part])); diff --git a/src/vs/workbench/services/group/test/browser/editorGroupsService.test.ts b/src/vs/workbench/services/group/test/browser/editorGroupsService.test.ts index d9a1a1f8c33..806b62a5e55 100644 --- a/src/vs/workbench/services/group/test/browser/editorGroupsService.test.ts +++ b/src/vs/workbench/services/group/test/browser/editorGroupsService.test.ts @@ -7,7 +7,6 @@ import * as assert from 'assert'; import { EditorPart } from 'vs/workbench/browser/parts/editor/editorPart'; import { workbenchInstantiationService, TestStorageService } from 'vs/workbench/test/workbenchTestServices'; import { GroupDirection, GroupsOrder, MergeGroupMode, GroupOrientation, GroupChangeKind, EditorsOrder, GroupLocation } from 'vs/workbench/services/group/common/editorGroupsService'; -import { Dimension } from 'vs/base/browser/dom'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IEditorPartOptions } from 'vs/workbench/browser/parts/editor/editor'; import { EditorInput, IFileEditorInput, IEditorInputFactory, IEditorInputFactoryRegistry, Extensions as EditorExtensions, EditorOptions, CloseDirection } from 'vs/workbench/common/editor'; @@ -90,7 +89,7 @@ suite('Editor groups service', () => { const part = instantiationService.createInstance(EditorPart, 'id', false); part.create(document.createElement('div')); - part.layout(new Dimension(400, 300)); + part.layout(400, 300); return part; } diff --git a/src/vs/workbench/services/progress/browser/media/progressService2.css b/src/vs/workbench/services/progress/browser/media/progressService2.css index a841cb76c2f..850fdf2e3c5 100644 --- a/src/vs/workbench/services/progress/browser/media/progressService2.css +++ b/src/vs/workbench/services/progress/browser/media/progressService2.css @@ -3,11 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-workbench > .part.statusbar > .statusbar-item.progress { +.monaco-workbench .part.statusbar > .statusbar-item.progress { padding-left: 5px; } -.monaco-workbench > .part.statusbar > .statusbar-item.progress .spinner-container { +.monaco-workbench .part.statusbar > .statusbar-item.progress .spinner-container { padding-right: 5px; } From 936facd191891aef34543f068550440fb14c5ddb Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 23 Jan 2019 15:05:39 -0800 Subject: [PATCH 042/274] less async#always, #67027 --- src/vs/platform/progress/common/progress.ts | 2 +- .../api/node/extHostDocumentSaveParticipant.ts | 4 ++-- .../services/progress/browser/progressService2.ts | 15 +++++++-------- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/vs/platform/progress/common/progress.ts b/src/vs/platform/progress/common/progress.ts index 37f2d1630cf..8a82a1eb30c 100644 --- a/src/vs/platform/progress/common/progress.ts +++ b/src/vs/platform/progress/common/progress.ts @@ -52,7 +52,7 @@ export interface IProgressService2 { _serviceBrand: any; - withProgress

, R=any>(options: IProgressOptions, task: (progress: IProgress) => P, onDidCancel?: () => void): P; + withProgress(options: IProgressOptions, task: (progress: IProgress) => Promise, onDidCancel?: () => void): Promise; } export interface IProgressRunner { diff --git a/src/vs/workbench/api/node/extHostDocumentSaveParticipant.ts b/src/vs/workbench/api/node/extHostDocumentSaveParticipant.ts index 226248d1699..29c19a7cefc 100644 --- a/src/vs/workbench/api/node/extHostDocumentSaveParticipant.ts +++ b/src/vs/workbench/api/node/extHostDocumentSaveParticipant.ts @@ -5,7 +5,7 @@ import { Event } from 'vs/base/common/event'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { sequence, always } from 'vs/base/common/async'; +import { sequence } from 'vs/base/common/async'; import { illegalState } from 'vs/base/common/errors'; import { ExtHostDocumentSaveParticipantShape, MainThreadTextEditorsShape, ResourceTextEditDto } from 'vs/workbench/api/node/extHost.protocol'; import { TextEdit } from 'vs/workbench/api/node/extHostTypes'; @@ -67,7 +67,7 @@ export class ExtHostDocumentSaveParticipant implements ExtHostDocumentSavePartic return this._deliverEventAsyncAndBlameBadListeners(listener, { document, reason: TextDocumentSaveReason.to(reason) }); }; })); - return always(promise, () => clearTimeout(didTimeoutHandle)); + return promise.finally(() => clearTimeout(didTimeoutHandle)); } private _deliverEventAsyncAndBlameBadListeners([listener, thisArg, extension]: Listener, stubEvent: vscode.TextDocumentWillSaveEvent): Promise { diff --git a/src/vs/workbench/services/progress/browser/progressService2.ts b/src/vs/workbench/services/progress/browser/progressService2.ts index 606793db659..da77ec66b8d 100644 --- a/src/vs/workbench/services/progress/browser/progressService2.ts +++ b/src/vs/workbench/services/progress/browser/progressService2.ts @@ -10,7 +10,7 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IProgressService2, IProgressOptions, IProgressStep, ProgressLocation, IProgress, emptyProgress, Progress } from 'vs/platform/progress/common/progress'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { StatusbarAlignment, IStatusbarService } from 'vs/platform/statusbar/common/statusbar'; -import { always, timeout } from 'vs/base/common/async'; +import { timeout } from 'vs/base/common/async'; import { ProgressBadge, IActivityService } from 'vs/workbench/services/activity/common/activity'; import { INotificationService, Severity, INotificationHandle, INotificationActions } from 'vs/platform/notification/common/notification'; import { Action } from 'vs/base/common/actions'; @@ -30,7 +30,7 @@ export class ProgressService2 implements IProgressService2 { @IStatusbarService private readonly _statusbarService: IStatusbarService, ) { } - withProgress

, R=any>(options: IProgressOptions, task: (progress: IProgress) => P, onDidCancel?: () => void): P { + withProgress(options: IProgressOptions, task: (progress: IProgress) => Promise, onDidCancel?: () => void): Promise { const { location } = options; if (typeof location === 'string') { @@ -59,7 +59,7 @@ export class ProgressService2 implements IProgressService2 { } } - private _withWindowProgress

, R=any>(options: IProgressOptions, callback: (progress: IProgress<{ message?: string }>) => P): P { + private _withWindowProgress(options: IProgressOptions, callback: (progress: IProgress<{ message?: string }>) => Promise): Promise { const task: [IProgressOptions, Progress] = [options, new Progress(() => this._updateWindowProgress())]; @@ -71,10 +71,10 @@ export class ProgressService2 implements IProgressService2 { this._updateWindowProgress(); // show progress for at least 150ms - always(Promise.all([ + Promise.all([ timeout(150), promise - ]), () => { + ]).finally(() => { const idx = this._stack.indexOf(task); this._stack.splice(idx, 1); this._updateWindowProgress(); @@ -83,8 +83,7 @@ export class ProgressService2 implements IProgressService2 { }, 150); // cancel delay if promise finishes below 150ms - always(promise, () => clearTimeout(delayHandle)); - return promise; + return promise.finally(() => clearTimeout(delayHandle)); } private _updateWindowProgress(idx: number = 0) { @@ -214,7 +213,7 @@ export class ProgressService2 implements IProgressService2 { }); // Show progress for at least 800ms and then hide once done or canceled - always(Promise.all([timeout(800), p]), () => { + Promise.all([timeout(800), p]).finally(() => { if (handle) { handle.close(); } From 4d503b21cd507d6600efc0f22ef368eccedae731 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 23 Jan 2019 15:06:54 -0800 Subject: [PATCH 043/274] less async#always #67027 --- src/vs/editor/browser/editorExtensions.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/browser/editorExtensions.ts b/src/vs/editor/browser/editorExtensions.ts index 071893e858b..15365b311a9 100644 --- a/src/vs/editor/browser/editorExtensions.ts +++ b/src/vs/editor/browser/editorExtensions.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { IPosition } from 'vs/base/browser/ui/contextview/contextview'; -import { always } from 'vs/base/common/async'; import { illegalArgument } from 'vs/base/common/errors'; import { URI } from 'vs/base/common/uri'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; @@ -266,14 +265,14 @@ export function registerDefaultLanguageCommand(id: string, handler: (model: ITex } return accessor.get(ITextModelService).createModelReference(resource).then(reference => { - return always(new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { try { let result = handler(reference.object.textEditorModel, Position.lift(position), args); resolve(result); } catch (err) { reject(err); } - }), () => { + }).finally(() => { reference.dispose(); }); }); From 0c579d7ac8bc5f94d3c9e4c1a1301950fdccea75 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Wed, 23 Jan 2019 15:10:12 -0800 Subject: [PATCH 044/274] Run safe for html selectionRange --- .../server/src/htmlServerMain.ts | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/extensions/html-language-features/server/src/htmlServerMain.ts b/extensions/html-language-features/server/src/htmlServerMain.ts index 2ccb26c3f60..bcc3feedf12 100644 --- a/extensions/html-language-features/server/src/htmlServerMain.ts +++ b/extensions/html-language-features/server/src/htmlServerMain.ts @@ -480,17 +480,19 @@ connection.onFoldingRanges((params, token) => { }, null, `Error while computing folding regions for ${params.textDocument.uri}`, token); }); -connection.onRequest('$/textDocument/selectionRange', async (params) => { - const document = documents.get(params.textDocument.uri); - const position: Position = params.position; +connection.onRequest('$/textDocument/selectionRange', async (params, token) => { + return runSafe(() => { + const document = documents.get(params.textDocument.uri); + const position: Position = params.position; - if (document) { - const htmlMode = languageModes.getMode('html'); - if (htmlMode && htmlMode.doSelection) { - return htmlMode.doSelection(document, position); + if (document) { + const htmlMode = languageModes.getMode('html'); + if (htmlMode && htmlMode.doSelection) { + return htmlMode.doSelection(document, position); + } } - } - return Promise.resolve(null); + return Promise.resolve(null); + }, null, `Error while computing selection ranges for ${params.textDocument.uri}`, token); }); From 2344260139dfa57121f291dc6b4122168d053abc Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Wed, 23 Jan 2019 23:51:24 +0000 Subject: [PATCH 045/274] fix zen mode with grid layout fixes #67019 --- .../workbench/electron-browser/workbench.ts | 290 +++++++++--------- 1 file changed, 145 insertions(+), 145 deletions(-) diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts index 5ff640f0e6b..4d16c8d6c83 100644 --- a/src/vs/workbench/electron-browser/workbench.ts +++ b/src/vs/workbench/electron-browser/workbench.ts @@ -223,6 +223,7 @@ export class Workbench extends Disposable implements IPartService { private notificationsToasts: NotificationsToasts; private sideBarHidden: boolean; + private titleBarHidden: boolean; private statusBarHidden: boolean; private activityBarHidden: boolean; private menubarToggled: boolean; @@ -984,41 +985,7 @@ export class Workbench extends Disposable implements IPartService { // Layout if (!skipLayout) { if (this.workbenchGrid instanceof SerializableGrid) { - if (this.statusBarHidden) { - this.workbenchGrid.removeView(this.statusbarPart); - } else { - if (!this.sideBarHidden) { - this.uiState.lastSidebarDimension = this.workbenchGrid.getViewSize(this.sidebarPart); - this.workbenchGrid.removeView(this.sidebarPart); - } - - if (!this.activityBarHidden) { - this.workbenchGrid.removeView(this.activitybarPart); - } - - if (!this.panelHidden) { - this.saveLastPanelDimension(); - this.workbenchGrid.removeView(this.panelPart); - } - - this.workbenchGrid.addView(this.statusbarPart, Sizing.Split, this.editorPart, Direction.Down); - - const sidebarDirection = this.sideBarPosition === Position.LEFT ? Direction.Left : Direction.Right; - if (!this.activityBarHidden) { - this.workbenchGrid.addView(this.activitybarPart, Sizing.Split, this.editorPart, sidebarDirection); - } - - if (!this.sideBarHidden) { - this.workbenchGrid.addView(this.sidebarPart, this.uiState.lastSidebarDimension, this.editorPart, sidebarDirection); - } - - if (!this.panelHidden) { - this.workbenchGrid.addView(this.panelPart, this.getLastPanelDimension(this.panelPosition), this.editorPart, this.panelPosition === Position.BOTTOM ? Direction.Down : Direction.Right); - } - } - - const size = DOM.getClientArea(this.container); - this.workbenchGrid.layout(size.width, size.height); + this.layout(); } else { this.workbenchGrid.layout(); } @@ -1336,7 +1303,12 @@ export class Workbench extends Disposable implements IPartService { getTitleBarOffset(): number { let offset = 0; if (this.isVisible(Parts.TITLEBAR_PART)) { - offset = this.workbenchGrid instanceof SerializableGrid ? this.workbenchGrid.getViewSize2(this.titlebarPart).height : this.workbenchGrid.partLayoutInfo.titlebar.height; + if (this.workbenchGrid instanceof SerializableGrid) { + offset = this.gridHasView(this.titlebarPart) ? this.workbenchGrid.getViewSize2(this.titlebarPart).height : 0; + } else { + offset = this.workbenchGrid.partLayoutInfo.titlebar.height; + } + if (isMacintosh || this.menubarVisibility === 'hidden') { offset /= browser.getZoomFactor(); } @@ -1434,6 +1406,132 @@ export class Workbench extends Disposable implements IPartService { } } + private gridHasView(view: WorkbenchView): boolean { + if (!(this.workbenchGrid instanceof SerializableGrid)) { + return false; + } + + try { + this.workbenchGrid.getViewSize(view); + return true; + } catch { + return false; + } + } + + private updateGrid(): void { + if (!(this.workbenchGrid instanceof SerializableGrid)) { + return; + } + + let panelInGrid = this.gridHasView(this.panelPart); + let sidebarInGrid = this.gridHasView(this.sidebarPart); + let activityBarInGrid = this.gridHasView(this.activitybarPart); + let statusBarInGrid = this.gridHasView(this.statusbarPart); + let titlebarInGrid = this.gridHasView(this.titlebarPart); + + // Remove hidden parts + if (this.panelHidden && panelInGrid) { + this.saveLastPanelDimension(); + this.workbenchGrid.removeView(this.panelPart); + panelInGrid = false; + } + + if (this.statusBarHidden && statusBarInGrid) { + this.workbenchGrid.removeView(this.statusbarPart); + statusBarInGrid = false; + } + + if (this.titleBarHidden && titlebarInGrid) { + this.workbenchGrid.removeView(this.titlebarPart); + titlebarInGrid = false; + } + + if (this.activityBarHidden && activityBarInGrid) { + this.workbenchGrid.removeView(this.activitybarPart); + activityBarInGrid = false; + } + + if (this.sideBarHidden && sidebarInGrid) { + this.uiState.lastSidebarDimension = this.workbenchGrid.getViewSize(this.sidebarPart); + this.workbenchGrid.removeView(this.sidebarPart); + sidebarInGrid = false; + } + + + // Add visible parts + if (!this.statusBarHidden && !statusBarInGrid) { + if (sidebarInGrid) { + this.uiState.lastSidebarDimension = this.workbenchGrid.getViewSize(this.sidebarPart); + this.workbenchGrid.removeView(this.sidebarPart); + sidebarInGrid = false; + } + + if (activityBarInGrid) { + this.workbenchGrid.removeView(this.activitybarPart); + activityBarInGrid = false; + } + + if (panelInGrid) { + this.saveLastPanelDimension(); + this.workbenchGrid.removeView(this.panelPart); + panelInGrid = false; + } + + this.workbenchGrid.addView(this.statusbarPart, Sizing.Split, this.editorPart, Direction.Down); + statusBarInGrid = true; + } + + if (!this.titleBarHidden && !titlebarInGrid) { + if (sidebarInGrid) { + this.uiState.lastSidebarDimension = this.workbenchGrid.getViewSize(this.sidebarPart); + this.workbenchGrid.removeView(this.sidebarPart); + sidebarInGrid = false; + } + + if (activityBarInGrid) { + this.workbenchGrid.removeView(this.activitybarPart); + activityBarInGrid = false; + } + + if (panelInGrid && this.panelPosition !== Position.BOTTOM) { + this.saveLastPanelDimension(); + this.workbenchGrid.removeView(this.panelPart); + panelInGrid = false; + } + + this.workbenchGrid.addView(this.titlebarPart, Sizing.Split, this.editorPart, Direction.Up); + titlebarInGrid = true; + } + + if (!this.activityBarHidden && !activityBarInGrid) { + if (sidebarInGrid) { + this.uiState.lastSidebarDimension = this.workbenchGrid.getViewSize(this.sidebarPart); + this.workbenchGrid.removeView(this.sidebarPart); + sidebarInGrid = false; + } + + this.workbenchGrid.addView(this.activitybarPart, Sizing.Split, this.editorPart, this.sideBarPosition === Position.RIGHT ? Direction.Right : Direction.Left); + activityBarInGrid = true; + } + + if (!this.sideBarHidden && !sidebarInGrid) { + if (panelInGrid && this.panelPosition === Position.BOTTOM) { + this.saveLastPanelDimension(); + this.workbenchGrid.removeView(this.panelPart); + panelInGrid = false; + } + + this.workbenchGrid.addView(this.sidebarPart, this.uiState.lastSidebarDimension !== undefined ? this.uiState.lastSidebarDimension : Sizing.Split, this.editorPart, this.sideBarPosition === Position.RIGHT ? Direction.Right : Direction.Left); + sidebarInGrid = true; + } + + if (!this.panelHidden && !panelInGrid) { + this.workbenchGrid.addView(this.panelPart, this.getLastPanelDimension(this.panelPosition) !== undefined ? this.getLastPanelDimension(this.panelPosition) : Sizing.Split, this.editorPart, this.panelPosition === Position.BOTTOM ? Direction.Down : Direction.Right); + panelInGrid = true; + } + } + layout(options?: ILayoutOptions): void { this.contextViewService.layout(); @@ -1443,10 +1541,9 @@ export class Workbench extends Disposable implements IPartService { DOM.position(this.workbench, 0, 0, 0, 0, 'relative'); DOM.size(this.workbench, dimensions.width, dimensions.height); - // Handle titlebar visibility - const hideTitlebar = browser.isFullscreen() && (this.getMenubarVisibility() === 'default' || (this.getMenubarVisibility() === 'toggle' && !this.menubarToggled)); - this.setTitlebarVisibility(!hideTitlebar); + this.titleBarHidden = browser.isFullscreen() && (this.getMenubarVisibility() === 'default' || (this.getMenubarVisibility() === 'toggle' && !this.menubarToggled)); + this.updateGrid(); this.workbenchGrid.layout(dimensions.width, dimensions.height); } else { this.workbenchGrid.layout(options); @@ -1455,50 +1552,7 @@ export class Workbench extends Disposable implements IPartService { } setTitlebarVisibility(visible: boolean): void { - if (!(this.workbenchGrid instanceof SerializableGrid)) { - return; - } - - let wasVisible = false; - - try { - wasVisible = !!this.workbenchGrid.getViewSize(this.titlebarPart); - } catch { } - - if (wasVisible !== visible) { - if (!visible) { - this.workbenchGrid.removeView(this.titlebarPart); - } else { - if (!this.sideBarHidden) { - this.uiState.lastSidebarDimension = this.workbenchGrid.getViewSize(this.sidebarPart); - this.workbenchGrid.removeView(this.sidebarPart); - } - - if (!this.activityBarHidden) { - this.workbenchGrid.removeView(this.activitybarPart); - } - - if (!this.panelHidden && this.panelPosition !== Position.BOTTOM) { - this.saveLastPanelDimension(); - this.workbenchGrid.removeView(this.panelPart); - } - - // Add views - this.workbenchGrid.addView(this.titlebarPart, Sizing.Split, this.editorPart, Direction.Up); - - if (!this.activityBarHidden) { - this.workbenchGrid.addView(this.activitybarPart, Sizing.Split, this.editorPart, this.sideBarPosition === Position.LEFT ? Direction.Left : Direction.Right); - } - - if (!this.sideBarHidden) { - this.workbenchGrid.addView(this.sidebarPart, this.uiState.lastSidebarDimension, this.editorPart, this.sideBarPosition === Position.LEFT ? Direction.Left : Direction.Right); - } - - if (!this.panelHidden && this.panelPosition !== Position.BOTTOM) { - this.workbenchGrid.addView(this.panelPart, this.getLastPanelDimension(this.panelPosition), this.editorPart, Direction.Right); - } - } - } + this.titleBarHidden = visible; } isEditorLayoutCentered(): boolean { @@ -1549,16 +1603,7 @@ export class Workbench extends Disposable implements IPartService { // Layout if (!skipLayout) { if (this.workbenchGrid instanceof SerializableGrid) { - if (hidden) { - this.workbenchGrid.removeView(this.activitybarPart); - } else { - const refView = this.sideBarHidden ? this.editorPart : this.sidebarPart; - const direction = this.sideBarPosition === Position.LEFT ? Direction.Left : Direction.Right; - this.workbenchGrid.addView(this.activitybarPart, Sizing.Split, refView, direction); - } - - const dimensions = DOM.getClientArea(this.container); - this.workbenchGrid.layout(dimensions.width, dimensions.height); + this.layout(); } else { this.workbenchGrid.layout(); } @@ -1612,29 +1657,7 @@ export class Workbench extends Disposable implements IPartService { // Layout if (!skipLayout) { if (this.workbenchGrid instanceof SerializableGrid) { - if (this.sideBarHidden) { - this.uiState.lastSidebarDimension = this.workbenchGrid.getViewSize(this.sidebarPart); - this.workbenchGrid.removeView(this.sidebarPart); - } else { - - const removePanelFirst = !this.panelHidden && this.panelPosition === Position.BOTTOM; - const refView = !this.panelHidden && this.panelPosition === Position.RIGHT && this.sideBarPosition === Position.RIGHT ? this.panelPart : this.editorPart; - const direction = this.sideBarPosition === Position.RIGHT ? Direction.Right : Direction.Left; - - if (removePanelFirst) { - this.saveLastPanelDimension(); - this.workbenchGrid.removeView(this.panelPart); - } - - this.workbenchGrid.addView(this.sidebarPart, this.uiState.lastSidebarDimension !== undefined ? this.uiState.lastSidebarDimension : Sizing.Split, refView, direction); - - if (removePanelFirst) { - this.workbenchGrid.addView(this.panelPart, this.getLastPanelDimension(this.panelPosition), this.editorPart, this.panelPosition === Position.BOTTOM ? Direction.Down : Direction.Right); - } - } - - const dimensions = DOM.getClientArea(this.container); - this.workbenchGrid.layout(dimensions.width, dimensions.height); + this.layout(); } else { this.workbenchGrid.layout(); } @@ -1676,15 +1699,7 @@ export class Workbench extends Disposable implements IPartService { // Layout if (!skipLayout) { if (this.workbenchGrid instanceof SerializableGrid) { - if (this.panelHidden) { - this.saveLastPanelDimension(); - this.workbenchGrid.removeView(this.panelPart); - } else { - this.workbenchGrid.addView(this.panelPart, this.getLastPanelDimension(this.panelPosition) !== undefined ? this.getLastPanelDimension(this.panelPosition) : Sizing.Split, this.editorPart, this.panelPosition === Position.BOTTOM ? Direction.Down : Direction.Right); - } - - const dimensions = DOM.getClientArea(this.container); - this.workbenchGrid.layout(dimensions.width, dimensions.height); + this.layout(); } else { this.workbenchGrid.layout(); } @@ -1738,32 +1753,17 @@ export class Workbench extends Disposable implements IPartService { // Layout if (this.workbenchGrid instanceof SerializableGrid) { - const refView = !this.panelHidden && this.panelPosition === position ? this.panelPart : this.editorPart; - const direction = position === Position.RIGHT ? Direction.Right : Direction.Left; - const removePanelFirst = !this.panelHidden && this.panelPosition === Position.BOTTOM; - if (removePanelFirst) { - this.saveLastPanelDimension(); - this.workbenchGrid.removeView(this.panelPart); + if (!wasHidden) { + this.uiState.lastSidebarDimension = this.workbenchGrid.getViewSize(this.sidebarPart); + this.workbenchGrid.removeView(this.sidebarPart); } if (!this.activityBarHidden) { - this.workbenchGrid.moveView(this.activitybarPart, Sizing.Split, refView, direction); + this.workbenchGrid.removeView(this.activitybarPart); } - if (wasHidden) { - this.workbenchGrid.addView(this.sidebarPart, this.uiState.lastSidebarDimension ? this.uiState.lastSidebarDimension : Sizing.Split, refView, direction); - } else { - this.uiState.lastSidebarDimension = this.workbenchGrid.getViewSize(this.sidebarPart); - this.workbenchGrid.moveView(this.sidebarPart, this.uiState.lastSidebarDimension, refView, direction); - } - - if (removePanelFirst) { - this.workbenchGrid.addView(this.panelPart, this.getLastPanelDimension(this.panelPosition), this.editorPart, this.panelPosition === Position.BOTTOM ? Direction.Down : Direction.Right); - } - - const dimensions = DOM.getClientArea(this.container); - this.workbenchGrid.layout(dimensions.width, dimensions.height); + this.layout(); } else { this.workbenchGrid.layout(); } From 75c7e86ffae78adf25a6c9be22d3fb4d4671e199 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Thu, 24 Jan 2019 00:10:32 +0000 Subject: [PATCH 046/274] fix formatting of setting --- src/vs/workbench/electron-browser/main.contribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/electron-browser/main.contribution.ts b/src/vs/workbench/electron-browser/main.contribution.ts index 7d6cce64e17..b130c7b55ab 100644 --- a/src/vs/workbench/electron-browser/main.contribution.ts +++ b/src/vs/workbench/electron-browser/main.contribution.ts @@ -826,7 +826,7 @@ configurationRegistry.registerConfiguration({ 'type': 'boolean', 'default': false, 'scope': ConfigurationScope.APPLICATION, - 'description': nls.localize('window.doubleClickIconToClose', "If enabled, double clicking the application icon in the title bar will close the window and the window cannot be dragged by the icon. This setting only has an effect when `#window.titleBarStyle#` is set to `custom`.") + 'markdownDescription': nls.localize('window.doubleClickIconToClose', "If enabled, double clicking the application icon in the title bar will close the window and the window cannot be dragged by the icon. This setting only has an effect when `#window.titleBarStyle#` is set to `custom`.") }, 'window.titleBarStyle': { 'type': 'string', From f053101c41b1311536d51f720d32a7c54348b197 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Wed, 23 Jan 2019 16:10:48 -0800 Subject: [PATCH 047/274] CSS semantic selection. Fix #65925 --- .../client/src/cssMain.ts | 22 +- .../client/src/vscode.proposed.d.ts | 1142 +++++++++++++++++ extensions/css-language-features/package.json | 1 + .../css-language-features/server/package.json | 2 +- .../server/src/cssServerMain.ts | 16 +- .../css-language-features/server/yarn.lock | 8 +- 6 files changed, 1183 insertions(+), 8 deletions(-) create mode 100644 extensions/css-language-features/client/src/vscode.proposed.d.ts diff --git a/extensions/css-language-features/client/src/cssMain.ts b/extensions/css-language-features/client/src/cssMain.ts index ee8c9973d3a..3108b13e5bd 100644 --- a/extensions/css-language-features/client/src/cssMain.ts +++ b/extensions/css-language-features/client/src/cssMain.ts @@ -9,8 +9,8 @@ import * as fs from 'fs'; import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); -import { languages, window, commands, ExtensionContext, Range, Position, CompletionItem, CompletionItemKind, TextEdit, SnippetString, workspace } from 'vscode'; -import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind, Disposable } from 'vscode-languageclient'; +import { languages, window, commands, ExtensionContext, Range, Position, CompletionItem, CompletionItemKind, TextEdit, SnippetString, workspace, TextDocument, SelectionRange, SelectionRangeKind } from 'vscode'; +import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind, Disposable, TextDocumentIdentifier } from 'vscode-languageclient'; import { getCustomDataPathsInAllWorkspaces, getCustomDataPathsFromAllExtensions } from './customData'; // this method is called when vs code is activated @@ -80,6 +80,24 @@ export function activate(context: ExtensionContext) { context.subscriptions.push(initCompletionProvider()); }); + const selectionRangeProvider = { + async provideSelectionRanges(document: TextDocument, position: Position): Promise { + const textDocument = TextDocumentIdentifier.create(document.uri.toString()); + const rawRanges: Range[] = await client.sendRequest('$/textDocument/selectionRange', { textDocument, position }); + + return rawRanges.map(r => { + const actualRange = new Range(new Position(r.start.line, r.start.character), new Position(r.end.line, r.end.character)); + return { + range: actualRange, + kind: SelectionRangeKind.Declaration + }; + }); + } + }; + languages.registerSelectionRangeProvider('css', selectionRangeProvider); + languages.registerSelectionRangeProvider('less', selectionRangeProvider); + languages.registerSelectionRangeProvider('scss', selectionRangeProvider); + function initCompletionProvider(): Disposable { const regionCompletionRegExpr = /^(\s*)(\/(\*\s*(#\w*)?)?)?$/; diff --git a/extensions/css-language-features/client/src/vscode.proposed.d.ts b/extensions/css-language-features/client/src/vscode.proposed.d.ts new file mode 100644 index 00000000000..44ad9a1d01b --- /dev/null +++ b/extensions/css-language-features/client/src/vscode.proposed.d.ts @@ -0,0 +1,1142 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/** + * This is the place for API experiments and proposals. + * These API are NOT stable and subject to change. They are only available in the Insiders + * distribution and CANNOT be used in published extensions. + * + * To test these API in local environment: + * - Use Insiders release of VS Code. + * - Add `"enableProposedApi": true` to your package.json. + * - Copy this file to your project. + */ + +declare module 'vscode' { + + //#region Joh - vscode.open + + export namespace env { + + /** + * Opens an *external* item, e.g. a http(s) or mailto-link, using the + * default application. + * + * *Note* that [`showTextDocument`](#window.showTextDocument) is the right + * way to open a text document inside the editor, not this function. + * + * @param target The uri that should be opened. + * @returns A promise indicating if open was successful. + */ + export function open(target: Uri): Thenable; + } + + //#endregion + + //#region Joh - selection range provider + + export class SelectionRangeKind { + + /** + * Empty Kind. + */ + static readonly Empty: SelectionRangeKind; + + /** + * The statment kind, its value is `statement`, possible extensions can be + * `statement.if` etc + */ + static readonly Statement: SelectionRangeKind; + + /** + * The declaration kind, its value is `declaration`, possible extensions can be + * `declaration.function`, `declaration.class` etc. + */ + static readonly Declaration: SelectionRangeKind; + + readonly value: string; + + private constructor(value: string); + + append(value: string): SelectionRangeKind; + } + + export class SelectionRange { + kind: SelectionRangeKind; + range: Range; + constructor(range: Range, kind: SelectionRangeKind); + } + + export interface SelectionRangeProvider { + /** + * Provide selection ranges starting at a given position. The first range must [contain](#Range.contains) + * position and subsequent ranges must contain the previous range. + */ + provideSelectionRanges(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; + } + + export namespace languages { + export function registerSelectionRangeProvider(selector: DocumentSelector, provider: SelectionRangeProvider): Disposable; + } + + //#endregion + + //#region Joh - read/write in chunks + + export interface FileSystemProvider { + open?(resource: Uri): number | Thenable; + close?(fd: number): void | Thenable; + read?(fd: number, pos: number, data: Uint8Array, offset: number, length: number): number | Thenable; + write?(fd: number, pos: number, data: Uint8Array, offset: number, length: number): number | Thenable; + } + + //#endregion + + //#region Rob: search provider + + /** + * The parameters of a query for text search. + */ + export interface TextSearchQuery { + /** + * The text pattern to search for. + */ + pattern: string; + + /** + * Whether or not `pattern` should match multiple lines of text. + */ + isMultiline?: boolean; + + /** + * Whether or not `pattern` should be interpreted as a regular expression. + */ + isRegExp?: boolean; + + /** + * Whether or not the search should be case-sensitive. + */ + isCaseSensitive?: boolean; + + /** + * Whether or not to search for whole word matches only. + */ + isWordMatch?: boolean; + } + + /** + * A file glob pattern to match file paths against. + * TODO@roblou - merge this with the GlobPattern docs/definition in vscode.d.ts. + * @see [GlobPattern](#GlobPattern) + */ + export type GlobString = string; + + /** + * Options common to file and text search + */ + export interface SearchOptions { + /** + * The root folder to search within. + */ + folder: Uri; + + /** + * Files that match an `includes` glob pattern should be included in the search. + */ + includes: GlobString[]; + + /** + * Files that match an `excludes` glob pattern should be excluded from the search. + */ + excludes: GlobString[]; + + /** + * Whether external files that exclude files, like .gitignore, should be respected. + * See the vscode setting `"search.useIgnoreFiles"`. + */ + useIgnoreFiles: boolean; + + /** + * Whether symlinks should be followed while searching. + * See the vscode setting `"search.followSymlinks"`. + */ + followSymlinks: boolean; + + /** + * Whether global files that exclude files, like .gitignore, should be respected. + * See the vscode setting `"search.useGlobalIgnoreFiles"`. + */ + useGlobalIgnoreFiles: boolean; + + } + + /** + * Options to specify the size of the result text preview. + * These options don't affect the size of the match itself, just the amount of preview text. + */ + export interface TextSearchPreviewOptions { + /** + * The maximum number of lines in the preview. + * Only search providers that support multiline search will ever return more than one line in the match. + */ + matchLines: number; + + /** + * The maximum number of characters included per line. + */ + charsPerLine: number; + } + + /** + * Options that apply to text search. + */ + export interface TextSearchOptions extends SearchOptions { + /** + * The maximum number of results to be returned. + */ + maxResults: number; + + /** + * Options to specify the size of the result text preview. + */ + previewOptions?: TextSearchPreviewOptions; + + /** + * Exclude files larger than `maxFileSize` in bytes. + */ + maxFileSize?: number; + + /** + * Interpret files using this encoding. + * See the vscode setting `"files.encoding"` + */ + encoding?: string; + + /** + * Number of lines of context to include before each match. + */ + beforeContext?: number; + + /** + * Number of lines of context to include after each match. + */ + afterContext?: number; + } + + /** + * Information collected when text search is complete. + */ + export interface TextSearchComplete { + /** + * Whether the search hit the limit on the maximum number of search results. + * `maxResults` on [`TextSearchOptions`](#TextSearchOptions) specifies the max number of results. + * - If exactly that number of matches exist, this should be false. + * - If `maxResults` matches are returned and more exist, this should be true. + * - If search hits an internal limit which is less than `maxResults`, this should be true. + */ + limitHit?: boolean; + } + + /** + * The parameters of a query for file search. + */ + export interface FileSearchQuery { + /** + * The search pattern to match against file paths. + */ + pattern: string; + } + + /** + * Options that apply to file search. + */ + export interface FileSearchOptions extends SearchOptions { + /** + * The maximum number of results to be returned. + */ + maxResults?: number; + + /** + * A CancellationToken that represents the session for this search query. If the provider chooses to, this object can be used as the key for a cache, + * and searches with the same session object can search the same cache. When the token is cancelled, the session is complete and the cache can be cleared. + */ + session?: CancellationToken; + } + + /** + * Options that apply to requesting the file index. + */ + export interface FileIndexOptions extends SearchOptions { } + + /** + * A preview of the text result. + */ + export interface TextSearchMatchPreview { + /** + * The matching lines of text, or a portion of the matching line that contains the match. + */ + text: string; + + /** + * The Range within `text` corresponding to the text of the match. + * The number of matches must match the TextSearchMatch's range property. + */ + matches: Range | Range[]; + } + + /** + * A match from a text search + */ + export interface TextSearchMatch { + /** + * The uri for the matching document. + */ + uri: Uri; + + /** + * The range of the match within the document, or multiple ranges for multiple matches. + */ + ranges: Range | Range[]; + + /** + * A preview of the text match. + */ + preview: TextSearchMatchPreview; + } + + /** + * A line of context surrounding a TextSearchMatch. + */ + export interface TextSearchContext { + /** + * The uri for the matching document. + */ + uri: Uri; + + /** + * One line of text. + * previewOptions.charsPerLine applies to this + */ + text: string; + + /** + * The line number of this line of context. + */ + lineNumber: number; + } + + export type TextSearchResult = TextSearchMatch | TextSearchContext; + + /** + * A FileIndexProvider provides a list of files in the given folder. VS Code will filter that list for searching with quickopen or from other extensions. + * + * A FileIndexProvider is the simpler of two ways to implement file search in VS Code. Use a FileIndexProvider if you are able to provide a listing of all files + * in a folder, and want VS Code to filter them according to the user's search query. + * + * The FileIndexProvider will be invoked once when quickopen is opened, and VS Code will filter the returned list. It will also be invoked when + * `workspace.findFiles` is called. + * + * If a [`FileSearchProvider`](#FileSearchProvider) is registered for the scheme, that provider will be used instead. + */ + export interface FileIndexProvider { + /** + * Provide the set of files in the folder. + * @param options A set of options to consider while searching. + * @param token A cancellation token. + */ + provideFileIndex(options: FileIndexOptions, token: CancellationToken): ProviderResult; + } + + /** + * A FileSearchProvider provides search results for files in the given folder that match a query string. It can be invoked by quickopen or other extensions. + * + * A FileSearchProvider is the more powerful of two ways to implement file search in VS Code. Use a FileSearchProvider if you wish to search within a folder for + * all files that match the user's query. + * + * The FileSearchProvider will be invoked on every keypress in quickopen. When `workspace.findFiles` is called, it will be invoked with an empty query string, + * and in that case, every file in the folder should be returned. + * + * @see [FileIndexProvider](#FileIndexProvider) + */ + export interface FileSearchProvider { + /** + * Provide the set of files that match a certain file path pattern. + * @param query The parameters for this query. + * @param options A set of options to consider while searching files. + * @param progress A progress callback that must be invoked for all results. + * @param token A cancellation token. + */ + provideFileSearchResults(query: FileSearchQuery, options: FileSearchOptions, token: CancellationToken): ProviderResult; + } + + /** + * A TextSearchProvider provides search results for text results inside files in the workspace. + */ + export interface TextSearchProvider { + /** + * Provide results that match the given text pattern. + * @param query The parameters for this query. + * @param options A set of options to consider while searching. + * @param progress A progress callback that must be invoked for all results. + * @param token A cancellation token. + */ + provideTextSearchResults(query: TextSearchQuery, options: TextSearchOptions, progress: Progress, token: CancellationToken): ProviderResult; + } + + /** + * Options that can be set on a findTextInFiles search. + */ + export interface FindTextInFilesOptions { + /** + * A [glob pattern](#GlobPattern) that defines the files to search for. The glob pattern + * will be matched against the file paths of files relative to their workspace. Use a [relative pattern](#RelativePattern) + * to restrict the search results to a [workspace folder](#WorkspaceFolder). + */ + include?: GlobPattern; + + /** + * A [glob pattern](#GlobPattern) that defines files and folders to exclude. The glob pattern + * will be matched against the file paths of resulting matches relative to their workspace. When `undefined` only default excludes will + * apply, when `null` no excludes will apply. + */ + exclude?: GlobPattern | null; + + /** + * The maximum number of results to search for + */ + maxResults?: number; + + /** + * Whether external files that exclude files, like .gitignore, should be respected. + * See the vscode setting `"search.useIgnoreFiles"`. + */ + useIgnoreFiles?: boolean; + + /** + * Whether global files that exclude files, like .gitignore, should be respected. + * See the vscode setting `"search.useGlobalIgnoreFiles"`. + */ + useGlobalIgnoreFiles?: boolean; + + /** + * Whether symlinks should be followed while searching. + * See the vscode setting `"search.followSymlinks"`. + */ + followSymlinks?: boolean; + + /** + * Interpret files using this encoding. + * See the vscode setting `"files.encoding"` + */ + encoding?: string; + + /** + * Options to specify the size of the result text preview. + */ + previewOptions?: TextSearchPreviewOptions; + + /** + * Number of lines of context to include before each match. + */ + beforeContext?: number; + + /** + * Number of lines of context to include after each match. + */ + afterContext?: number; + } + + export namespace workspace { + /** + * DEPRECATED + */ + export function registerSearchProvider(): Disposable; + + /** + * Register a file index provider. + * + * Only one provider can be registered per scheme. + * + * @param scheme The provider will be invoked for workspace folders that have this file scheme. + * @param provider The provider. + * @return A [disposable](#Disposable) that unregisters this provider when being disposed. + */ + export function registerFileIndexProvider(scheme: string, provider: FileIndexProvider): Disposable; + + /** + * Register a search provider. + * + * Only one provider can be registered per scheme. + * + * @param scheme The provider will be invoked for workspace folders that have this file scheme. + * @param provider The provider. + * @return A [disposable](#Disposable) that unregisters this provider when being disposed. + */ + export function registerFileSearchProvider(scheme: string, provider: FileSearchProvider): Disposable; + + /** + * Register a text search provider. + * + * Only one provider can be registered per scheme. + * + * @param scheme The provider will be invoked for workspace folders that have this file scheme. + * @param provider The provider. + * @return A [disposable](#Disposable) that unregisters this provider when being disposed. + */ + export function registerTextSearchProvider(scheme: string, provider: TextSearchProvider): Disposable; + + /** + * Search text in files across all [workspace folders](#workspace.workspaceFolders) in the workspace. + * @param query The query parameters for the search - the search string, whether it's case-sensitive, or a regex, or matches whole words. + * @param callback A callback, called for each result + * @param token A token that can be used to signal cancellation to the underlying search engine. + * @return A thenable that resolves when the search is complete. + */ + export function findTextInFiles(query: TextSearchQuery, callback: (result: TextSearchResult) => void, token?: CancellationToken): Thenable; + + /** + * Search text in files across all [workspace folders](#workspace.workspaceFolders) in the workspace. + * @param query The query parameters for the search - the search string, whether it's case-sensitive, or a regex, or matches whole words. + * @param options An optional set of query options. Include and exclude patterns, maxResults, etc. + * @param callback A callback, called for each result + * @param token A token that can be used to signal cancellation to the underlying search engine. + * @return A thenable that resolves when the search is complete. + */ + export function findTextInFiles(query: TextSearchQuery, options: FindTextInFilesOptions, callback: (result: TextSearchResult) => void, token?: CancellationToken): Thenable; + } + + //#endregion + + //#region Joao: diff command + + /** + * The contiguous set of modified lines in a diff. + */ + export interface LineChange { + readonly originalStartLineNumber: number; + readonly originalEndLineNumber: number; + readonly modifiedStartLineNumber: number; + readonly modifiedEndLineNumber: number; + } + + export namespace commands { + + /** + * Registers a diff information command that can be invoked via a keyboard shortcut, + * a menu item, an action, or directly. + * + * Diff information commands are different from ordinary [commands](#commands.registerCommand) as + * they only execute when there is an active diff editor when the command is called, and the diff + * information has been computed. Also, the command handler of an editor command has access to + * the diff information. + * + * @param command A unique identifier for the command. + * @param callback A command handler function with access to the [diff information](#LineChange). + * @param thisArg The `this` context used when invoking the handler function. + * @return Disposable which unregisters this command on disposal. + */ + export function registerDiffInformationCommand(command: string, callback: (diff: LineChange[], ...args: any[]) => any, thisArg?: any): Disposable; + } + + //#endregion + + //#region Joh: decorations + + //todo@joh -> make class + export interface DecorationData { + letter?: string; + title?: string; + color?: ThemeColor; + priority?: number; + bubble?: boolean; + source?: string; // hacky... we should remove it and use equality under the hood + } + + export interface SourceControlResourceDecorations { + source?: string; + letter?: string; + color?: ThemeColor; + } + + export interface DecorationProvider { + onDidChangeDecorations: Event; + provideDecoration(uri: Uri, token: CancellationToken): ProviderResult; + } + + export namespace window { + export function registerDecorationProvider(provider: DecorationProvider): Disposable; + } + + //#endregion + + //#region André: debug + + // deprecated + + export interface DebugConfigurationProvider { + /** + * Deprecated, use DebugAdapterDescriptorFactory.provideDebugAdapter instead. + * @deprecated Use DebugAdapterDescriptorFactory.createDebugAdapterDescriptor instead + */ + debugAdapterExecutable?(folder: WorkspaceFolder | undefined, token?: CancellationToken): ProviderResult; + } + + //#endregion + + //#region Rob, Matt: logging + + /** + * The severity level of a log message + */ + export enum LogLevel { + Trace = 1, + Debug = 2, + Info = 3, + Warning = 4, + Error = 5, + Critical = 6, + Off = 7 + } + + export namespace env { + /** + * Current logging level. + */ + export const logLevel: LogLevel; + + /** + * An [event](#Event) that fires when the log level has changed. + */ + export const onDidChangeLogLevel: Event; + } + + //#endregion + + //#region Joao: SCM validation + + /** + * Represents the validation type of the Source Control input. + */ + export enum SourceControlInputBoxValidationType { + + /** + * Something not allowed by the rules of a language or other means. + */ + Error = 0, + + /** + * Something suspicious but allowed. + */ + Warning = 1, + + /** + * Something to inform about but not a problem. + */ + Information = 2 + } + + export interface SourceControlInputBoxValidation { + + /** + * The validation message to display. + */ + readonly message: string; + + /** + * The validation type. + */ + readonly type: SourceControlInputBoxValidationType; + } + + /** + * Represents the input box in the Source Control viewlet. + */ + export interface SourceControlInputBox { + + /** + * A validation function for the input box. It's possible to change + * the validation provider simply by setting this property to a different function. + */ + validateInput?(value: string, cursorPosition: number): ProviderResult; + } + + //#endregion + + //#region Joao: SCM selected provider + + export interface SourceControl { + + /** + * Whether the source control is selected. + */ + readonly selected: boolean; + + /** + * An event signaling when the selection state changes. + */ + readonly onDidChangeSelection: Event; + } + + //#endregion + + //#region Joao: SCM Input Box + + /** + * Represents the input box in the Source Control viewlet. + */ + export interface SourceControlInputBox { + + /** + * Controls whether the input box is visible (default is `true`). + */ + visible: boolean; + } + + //#endregion + + //#region Comments + /** + * Comments provider related APIs are still in early stages, they may be changed significantly during our API experiments. + */ + + interface CommentInfo { + /** + * All of the comment threads associated with the document. + */ + threads: CommentThread[]; + + /** + * The ranges of the document which support commenting. + */ + commentingRanges?: Range[]; + + /** + * If it's in draft mode or not + */ + inDraftMode?: boolean; + } + + export enum CommentThreadCollapsibleState { + /** + * Determines an item is collapsed + */ + Collapsed = 0, + /** + * Determines an item is expanded + */ + Expanded = 1 + } + + /** + * A collection of comments representing a conversation at a particular range in a document. + */ + interface CommentThread { + /** + * A unique identifier of the comment thread. + */ + threadId: string; + + /** + * The uri of the document the thread has been created on. + */ + resource: Uri; + + /** + * The range the comment thread is located within the document. The thread icon will be shown + * at the first line of the range. + */ + range: Range; + + /** + * The ordered comments of the thread. + */ + comments: Comment[]; + + /** + * Whether the thread should be collapsed or expanded when opening the document. Defaults to Collapsed. + */ + collapsibleState?: CommentThreadCollapsibleState; + } + + /** + * A comment is displayed within the editor or the Comments Panel, depending on how it is provided. + */ + interface Comment { + /** + * The id of the comment + */ + commentId: string; + + /** + * The text of the comment + */ + body: MarkdownString; + + /** + * The display name of the user who created the comment + */ + userName: string; + + /** + * The icon path for the user who created the comment + */ + userIconPath?: Uri; + + + /** + * @deprecated Use userIconPath instead. The avatar src of the user who created the comment + */ + gravatar?: string; + + /** + * Whether the current user has permission to edit the comment. + * + * This will be treated as false if the comment is provided by a `WorkspaceCommentProvider`, or + * if it is provided by a `DocumentCommentProvider` and no `editComment` method is given. + */ + canEdit?: boolean; + + /** + * Whether the current user has permission to delete the comment. + * + * This will be treated as false if the comment is provided by a `WorkspaceCommentProvider`, or + * if it is provided by a `DocumentCommentProvider` and no `deleteComment` method is given. + */ + canDelete?: boolean; + + /** + * The command to be executed if the comment is selected in the Comments Panel + */ + command?: Command; + + isDraft?: boolean; + } + + export interface CommentThreadChangedEvent { + /** + * Added comment threads. + */ + readonly added: CommentThread[]; + + /** + * Removed comment threads. + */ + readonly removed: CommentThread[]; + + /** + * Changed comment threads. + */ + readonly changed: CommentThread[]; + + /** + * Changed draft mode + */ + readonly inDraftMode: boolean; + } + + interface DocumentCommentProvider { + /** + * Provide the commenting ranges and comment threads for the given document. The comments are displayed within the editor. + */ + provideDocumentComments(document: TextDocument, token: CancellationToken): Promise; + + /** + * Called when a user adds a new comment thread in the document at the specified range, with body text. + */ + createNewCommentThread(document: TextDocument, range: Range, text: string, token: CancellationToken): Promise; + + /** + * Called when a user replies to a new comment thread in the document at the specified range, with body text. + */ + replyToCommentThread(document: TextDocument, range: Range, commentThread: CommentThread, text: string, token: CancellationToken): Promise; + + /** + * Called when a user edits the comment body to the be new text. + */ + editComment?(document: TextDocument, comment: Comment, text: string, token: CancellationToken): Promise; + + /** + * Called when a user deletes the comment. + */ + deleteComment?(document: TextDocument, comment: Comment, token: CancellationToken): Promise; + + startDraft?(document: TextDocument, token: CancellationToken): Promise; + deleteDraft?(document: TextDocument, token: CancellationToken): Promise; + finishDraft?(document: TextDocument, token: CancellationToken): Promise; + + startDraftLabel?: string; + deleteDraftLabel?: string; + finishDraftLabel?: string; + + /** + * Notify of updates to comment threads. + */ + onDidChangeCommentThreads: Event; + } + + interface WorkspaceCommentProvider { + /** + * Provide all comments for the workspace. Comments are shown within the comments panel. Selecting a comment + * from the panel runs the comment's command. + */ + provideWorkspaceComments(token: CancellationToken): Promise; + + /** + * Notify of updates to comment threads. + */ + onDidChangeCommentThreads: Event; + } + + namespace workspace { + export function registerDocumentCommentProvider(provider: DocumentCommentProvider): Disposable; + export function registerWorkspaceCommentProvider(provider: WorkspaceCommentProvider): Disposable; + } + //#endregion + + //#region Terminal + + export interface Terminal { + /** + * Fires when the terminal's pty slave pseudo-device is written to. In other words, this + * provides access to the raw data stream from the process running within the terminal, + * including VT sequences. + */ + onDidWriteData: Event; + } + + /** + * Represents the dimensions of a terminal. + */ + export interface TerminalDimensions { + /** + * The number of columns in the terminal. + */ + readonly columns: number; + + /** + * The number of rows in the terminal. + */ + readonly rows: number; + } + + /** + * Represents a terminal without a process where all interaction and output in the terminal is + * controlled by an extension. This is similar to an output window but has the same VT sequence + * compatility as the regular terminal. + * + * Note that an instance of [Terminal](#Terminal) will be created when a TerminalRenderer is + * created with all its APIs available for use by extensions. When using the Terminal object + * of a TerminalRenderer it acts just like normal only the extension that created the + * TerminalRenderer essentially acts as a process. For example when an + * [Terminal.onDidWriteData](#Terminal.onDidWriteData) listener is registered, that will fire + * when [TerminalRenderer.write](#TerminalRenderer.write) is called. Similarly when + * [Terminal.sendText](#Terminal.sendText) is triggered that will fire the + * [TerminalRenderer.onDidAcceptInput](#TerminalRenderer.onDidAcceptInput) event. + * + * **Example:** Create a terminal renderer, show it and write hello world in red + * ```typescript + * const renderer = window.createTerminalRenderer('foo'); + * renderer.terminal.then(t => t.show()); + * renderer.write('\x1b[31mHello world\x1b[0m'); + * ``` + */ + export interface TerminalRenderer { + /** + * The name of the terminal, this will appear in the terminal selector. + */ + name: string; + + /** + * The dimensions of the terminal, the rows and columns of the terminal can only be set to + * a value smaller than the maximum value, if this is undefined the terminal will auto fit + * to the maximum value [maximumDimensions](TerminalRenderer.maximumDimensions). + * + * **Example:** Override the dimensions of a TerminalRenderer to 20 columns and 10 rows + * ```typescript + * terminalRenderer.dimensions = { + * cols: 20, + * rows: 10 + * }; + * ``` + */ + dimensions: TerminalDimensions | undefined; + + /** + * The maximum dimensions of the terminal, this will be undefined immediately after a + * terminal renderer is created and also until the terminal becomes visible in the UI. + * Listen to [onDidChangeMaximumDimensions](TerminalRenderer.onDidChangeMaximumDimensions) + * to get notified when this value changes. + */ + readonly maximumDimensions: TerminalDimensions | undefined; + + /** + * The corressponding [Terminal](#Terminal) for this TerminalRenderer. + */ + readonly terminal: Terminal; + + /** + * Write text to the terminal. Unlike [Terminal.sendText](#Terminal.sendText) which sends + * text to the underlying _process_, this will write the text to the terminal itself. + * + * **Example:** Write red text to the terminal + * ```typescript + * terminalRenderer.write('\x1b[31mHello world\x1b[0m'); + * ``` + * + * **Example:** Move the cursor to the 10th row and 20th column and write an asterisk + * ```typescript + * terminalRenderer.write('\x1b[10;20H*'); + * ``` + * + * @param text The text to write. + */ + write(text: string): void; + + /** + * An event which fires on keystrokes in the terminal or when an extension calls + * [Terminal.sendText](#Terminal.sendText). Keystrokes are converted into their + * corresponding VT sequence representation. + * + * **Example:** Simulate interaction with the terminal from an outside extension or a + * workbench command such as `workbench.action.terminal.runSelectedText` + * ```typescript + * const terminalRenderer = window.createTerminalRenderer('test'); + * terminalRenderer.onDidAcceptInput(data => { + * cosole.log(data); // 'Hello world' + * }); + * terminalRenderer.terminal.then(t => t.sendText('Hello world')); + * ``` + */ + readonly onDidAcceptInput: Event; + + /** + * An event which fires when the [maximum dimensions](#TerminalRenderer.maimumDimensions) of + * the terminal renderer change. + */ + readonly onDidChangeMaximumDimensions: Event; + } + + export namespace window { + /** + * Create a [TerminalRenderer](#TerminalRenderer). + * + * @param name The name of the terminal renderer, this shows up in the terminal selector. + */ + export function createTerminalRenderer(name: string): TerminalRenderer; + } + + //#endregion + + //#region Joh -> exclusive document filters + + export interface DocumentFilter { + exclusive?: boolean; + } + + //#endregion + + //#region mjbvz,joh: https://github.com/Microsoft/vscode/issues/43768 + export interface FileRenameEvent { + readonly oldUri: Uri; + readonly newUri: Uri; + } + + export interface FileWillRenameEvent { + readonly oldUri: Uri; + readonly newUri: Uri; + waitUntil(thenable: Thenable): void; + } + + export namespace workspace { + export const onWillRenameFile: Event; + export const onDidRenameFile: Event; + } + //#endregion + + //#region Alex - OnEnter enhancement + export interface OnEnterRule { + /** + * This rule will only execute if the text above the this line matches this regular expression. + */ + oneLineAboveText?: RegExp; + } + //#endregion + + //#region Tree View + + export interface TreeView { + + /** + * An optional human-readable message that will be rendered in the view. + */ + message?: string | MarkdownString; + + } + + /** + * Label describing the [Tree item](#TreeItem) + */ + export interface TreeItemLabel { + + /** + * A human-readable string describing the [Tree item](#TreeItem). + */ + label: string; + + /** + * Ranges in the label to highlight. A range is defined as a tuple of two number where the + * first is the inclusive start index and the second the exclusive end index + */ + highlights?: [number, number][]; + + } + + export class TreeItem2 extends TreeItem { + /** + * Label describing this item. When `falsy`, it is derived from [resourceUri](#TreeItem.resourceUri). + */ + label?: string | TreeItemLabel | /* for compilation */ any; + + /** + * @param label Label describing this item + * @param collapsibleState [TreeItemCollapsibleState](#TreeItemCollapsibleState) of the tree item. Default is [TreeItemCollapsibleState.None](#TreeItemCollapsibleState.None) + */ + constructor(label: TreeItemLabel, collapsibleState?: TreeItemCollapsibleState); + } + //#endregion + + //#region SignatureHelpContext active paramters - mjbvz + export interface SignatureHelpContext { + /** + * The currently active [`SignatureHelp`](#SignatureHelp). + * + * Will have the [`SignatureHelp.activeSignature`] field updated based on user arrowing through sig help + */ + readonly activeSignatureHelp?: SignatureHelp; + } + //#endregion + + //#region CodeAction.isPreferred - mjbvz + export interface CodeAction { + /** + * If the action is a preferred action or fix to take. + * + * A quick fix should be marked preferred if it properly addresses the underlying error. + * A refactoring should be marked preferred if it is the most reasonable choice of actions to take. + */ + isPreferred?: boolean; + } + //#endregion + + + //#region Autofix - mjbvz + export namespace CodeActionKind { + /** + * Base kind for an auto fix source action: `source.autoFix`. + */ + export const SourceAutoFix: CodeActionKind; + } + //#endregion +} \ No newline at end of file diff --git a/extensions/css-language-features/package.json b/extensions/css-language-features/package.json index abb927c319c..7ef56c5f423 100644 --- a/extensions/css-language-features/package.json +++ b/extensions/css-language-features/package.json @@ -1,4 +1,5 @@ { + "enableProposedApi": true, "name": "css-language-features", "displayName": "%displayName%", "description": "%description%", diff --git a/extensions/css-language-features/server/package.json b/extensions/css-language-features/server/package.json index 3e87558befb..b48257bb0c5 100644 --- a/extensions/css-language-features/server/package.json +++ b/extensions/css-language-features/server/package.json @@ -9,7 +9,7 @@ }, "main": "./out/cssServerMain", "dependencies": { - "vscode-css-languageservice": "^3.0.13-next.6", + "vscode-css-languageservice": "^3.0.13-next.7", "vscode-languageserver": "^5.1.0" }, "devDependencies": { diff --git a/extensions/css-language-features/server/src/cssServerMain.ts b/extensions/css-language-features/server/src/cssServerMain.ts index 0108e1df628..c5fd3bdb65d 100644 --- a/extensions/css-language-features/server/src/cssServerMain.ts +++ b/extensions/css-language-features/server/src/cssServerMain.ts @@ -8,7 +8,7 @@ import { } from 'vscode-languageserver'; import URI from 'vscode-uri'; import * as fs from 'fs'; -import { TextDocument, CompletionList } from 'vscode-languageserver-types'; +import { TextDocument, CompletionList, Position } from 'vscode-languageserver-types'; import { getCSSLanguageService, getSCSSLanguageService, getLESSLanguageService, LanguageSettings, LanguageService, Stylesheet, CSSData } from 'vscode-css-languageservice'; import { getLanguageModelCache } from './languageModelCache'; @@ -346,5 +346,19 @@ connection.onFoldingRanges((params, token) => { }, null, `Error while computing folding ranges for ${params.textDocument.uri}`, token); }); +connection.onRequest('$/textDocument/selectionRange', async (params, token) => { + return runSafe(() => { + const document = documents.get(params.textDocument.uri); + const position: Position = params.position; + + if (document) { + const stylesheet = stylesheets.get(document); + return getLanguageService(document).getSelectionRanges(document, position, stylesheet); + } + return Promise.resolve(null); + }, null, `Error while computing selection ranges for ${params.textDocument.uri}`, token); +}); + + // Listen on the connection connection.listen(); \ No newline at end of file diff --git a/extensions/css-language-features/server/yarn.lock b/extensions/css-language-features/server/yarn.lock index 813bbb333c6..53a82e931a8 100644 --- a/extensions/css-language-features/server/yarn.lock +++ b/extensions/css-language-features/server/yarn.lock @@ -229,10 +229,10 @@ supports-color@5.4.0: dependencies: has-flag "^3.0.0" -vscode-css-languageservice@^3.0.13-next.6: - version "3.0.13-next.6" - resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-3.0.13-next.6.tgz#0329ea1da29ca84d1821cd32ee10bca0ee4c50cd" - integrity sha512-wC8zaFWHNnqIaOT4LXByy3NyTl916uHxGy3U3cpV7Gw7F8ENylSGM2RAO+l7NohIbP0WZlet441HEwSbb+bZbQ== +vscode-css-languageservice@^3.0.13-next.7: + version "3.0.13-next.7" + resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-3.0.13-next.7.tgz#48392edbf35ce60c54aeaf8d3e83c994d6c4c84e" + integrity sha512-2Z28n0CVwwNpdDmoIbjUMJPnchm+eRJE/PhhjXss/p0CnSJfXzbll3Ay1V+nDLW2NIGap0CJFbbfT+cilyivIA== dependencies: vscode-languageserver-types "^3.13.0" vscode-nls "^4.0.0" From f14dceaf49b9e2e76615fc6dc846dacfbc34965f Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 23 Jan 2019 16:38:13 -0800 Subject: [PATCH 048/274] Fix terminal process env variables --- .../api/electron-browser/mainThreadTerminalService.ts | 7 ------- src/vs/workbench/api/node/extHostTerminalService.ts | 7 ++++--- .../terminal/electron-browser/terminalProcessManager.ts | 3 +-- 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts b/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts index 49bee8d7d2d..84ed8b00b64 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts @@ -12,7 +12,6 @@ import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostC export class MainThreadTerminalService implements MainThreadTerminalServiceShape { private _proxy: ExtHostTerminalServiceShape; - private _remoteAuthority: string | null; private _toDispose: IDisposable[] = []; private _terminalProcesses: { [id: number]: ITerminalProcessExtHostProxy } = {}; private _terminalOnDidWriteDataListeners: { [id: number]: IDisposable } = {}; @@ -23,7 +22,6 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape @ITerminalService private readonly terminalService: ITerminalService ) { this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTerminalService); - this._remoteAuthority = extHostContext.remoteAuthority; this._toDispose.push(terminalService.onInstanceCreated((instance) => { // Delay this message so the TerminalInstance constructor has a chance to finish and // return the ID normally to the extension host. The ID that is passed here will be used @@ -204,11 +202,6 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape } private _onTerminalRequestExtHostProcess(request: ITerminalProcessExtHostRequest): void { - // Only allow processes on remote ext hosts - if (!this._remoteAuthority) { - return; - } - this._terminalProcesses[request.proxy.terminalId] = request.proxy; const shellLaunchConfigDto: ShellLaunchConfigDto = { name: request.shellLaunchConfig.name, diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts index 90a3c7ad382..99ba5c8878d 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -405,13 +405,14 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { // TODO: Pull in and resolve config settings // // Resolve env vars from config and shell // const lastActiveWorkspaceRoot = this._workspaceContextService.getWorkspaceFolder(lastActiveWorkspaceRootUri); - // const platformKey = platform.isWindows ? 'windows' : (platform.isMacintosh ? 'osx' : 'linux'); - // const envFromConfig = terminalEnvironment.resolveConfigurationVariables(this._configurationResolverService, { ...this._configHelper.config.env[platformKey] }, lastActiveWorkspaceRoot); + const platformKey = platform.isWindows ? 'windows' : (platform.isMacintosh ? 'osx' : 'linux'); + // const envFromConfig = terminalEnvironment.resolveConfigurationVariables(this._configurationResolverService, { ...terminalConfig.env[platformKey] }, lastActiveWorkspaceRoot); + const envFromConfig = { ...terminalConfig.env[platformKey] }; // const envFromShell = terminalEnvironment.resolveConfigurationVariables(this._configurationResolverService, { ...shellLaunchConfig.env }, lastActiveWorkspaceRoot); // Merge process env with the env from config const env = { ...process.env }; - // terminalEnvironment.mergeEnvironments(env, envFromConfig); + terminalEnvironment.mergeEnvironments(env, envFromConfig); terminalEnvironment.mergeEnvironments(env, shellLaunchConfig.env); // Sanitize the environment, removing any undesirable VS Code and Electron environment diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts index ffab899d101..f838343e3ff 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts @@ -90,14 +90,13 @@ export class TerminalProcessManager implements ITerminalProcessManager { cols: number, rows: number ): void { - let launchRemotely = false; if (shellLaunchConfig.cwd && typeof shellLaunchConfig.cwd === 'object') { launchRemotely = !!getRemoteAuthority(shellLaunchConfig.cwd); shellLaunchConfig.cwd = shellLaunchConfig.cwd.fsPath; } else { - launchRemotely = !!this._windowService.getConfiguration().remoteAuthority; + launchRemotely = !!this._windowService.getConfiguration().remoteAuthority || (this._configHelper.config as any).extHostProcess; } if (launchRemotely) { From 1bfb4a4723921036c7d55754b4219f64c7704d28 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Thu, 24 Jan 2019 00:59:50 +0000 Subject: [PATCH 049/274] fixes double titlebar when native #67020 --- .../workbench/electron-browser/workbench.ts | 44 ++++--------------- 1 file changed, 9 insertions(+), 35 deletions(-) diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts index 4d16c8d6c83..72e6255f3f7 100644 --- a/src/vs/workbench/electron-browser/workbench.ts +++ b/src/vs/workbench/electron-browser/workbench.ts @@ -223,7 +223,6 @@ export class Workbench extends Disposable implements IPartService { private notificationsToasts: NotificationsToasts; private sideBarHidden: boolean; - private titleBarHidden: boolean; private statusBarHidden: boolean; private activityBarHidden: boolean; private menubarToggled: boolean; @@ -1009,7 +1008,6 @@ export class Workbench extends Disposable implements IPartService { const serializedWorkbenchGridString = this.storageService.get(Workbench.workbenchGridUIStateStorageKey, StorageScope.GLOBAL, undefined); if (serializedWorkbenchGridString) { - console.log(serializedWorkbenchGridString); const serializedWorkbenchGrid = JSON.parse(serializedWorkbenchGridString) as ISerializedGrid; this.workbenchGrid = SerializableGrid.deserialize(serializedWorkbenchGrid, { fromJSON: (serializedView: { type: Parts }): WorkbenchView => { @@ -1028,30 +1026,10 @@ export class Workbench extends Disposable implements IPartService { } else { this.workbenchGrid = new SerializableGrid(this.editorPart, { proportionalLayout: false }); - const sidebarDirection = this.sideBarPosition === Position.RIGHT ? Direction.Right : Direction.Left; - - if (!this.statusBarHidden) { - this.workbenchGrid.addView(this.statusbarPart, Sizing.Split, this.editorPart, Direction.Down); - } - - if (this.useCustomTitleBarStyle) { - this.workbenchGrid.addView(this.titlebarPart, Sizing.Split, this.editorPart, Direction.Up); - } - - if (!this.activityBarHidden) { - this.workbenchGrid.addView(this.activitybarPart, Sizing.Split, this.editorPart, sidebarDirection); - } - - if (!this.sideBarHidden) { - this.workbenchGrid.addView(this.sidebarPart, Sizing.Split, this.editorPart, sidebarDirection); - } - - if (!this.panelHidden) { - this.workbenchGrid.addView(this.panelPart, Sizing.Split, this.editorPart, this.panelPosition === Position.BOTTOM ? Direction.Down : Direction.Right); - } } - this.workbench.appendChild(this.workbenchGrid.element); + this.updateGrid(); + this.workbench.prepend(this.workbenchGrid.element); } else { this.workbenchGrid = this.instantiationService.createInstance( WorkbenchLayout, @@ -1159,9 +1137,11 @@ export class Workbench extends Disposable implements IPartService { part.id = id; part.setAttribute('role', role); - // Insert all workbench parts at the beginning. Issue #52531 - // This is primarily for the title bar to allow overriding -webkit-app-region - this.workbench.insertBefore(part, this.workbench.lastChild); + if (!this.configurationService.getValue('workbench.useExperimentalGridLayout')) { + // Insert all workbench parts at the beginning. Issue #52531 + // This is primarily for the title bar to allow overriding -webkit-app-region + this.workbench.insertBefore(part, this.workbench.lastChild); + } return part; } @@ -1442,7 +1422,7 @@ export class Workbench extends Disposable implements IPartService { statusBarInGrid = false; } - if (this.titleBarHidden && titlebarInGrid) { + if (!this.isVisible(Parts.TITLEBAR_PART) && titlebarInGrid) { this.workbenchGrid.removeView(this.titlebarPart); titlebarInGrid = false; } @@ -1482,7 +1462,7 @@ export class Workbench extends Disposable implements IPartService { statusBarInGrid = true; } - if (!this.titleBarHidden && !titlebarInGrid) { + if (this.isVisible(Parts.TITLEBAR_PART) && !titlebarInGrid) { if (sidebarInGrid) { this.uiState.lastSidebarDimension = this.workbenchGrid.getViewSize(this.sidebarPart); this.workbenchGrid.removeView(this.sidebarPart); @@ -1541,8 +1521,6 @@ export class Workbench extends Disposable implements IPartService { DOM.position(this.workbench, 0, 0, 0, 0, 'relative'); DOM.size(this.workbench, dimensions.width, dimensions.height); - this.titleBarHidden = browser.isFullscreen() && (this.getMenubarVisibility() === 'default' || (this.getMenubarVisibility() === 'toggle' && !this.menubarToggled)); - this.updateGrid(); this.workbenchGrid.layout(dimensions.width, dimensions.height); } else { @@ -1551,10 +1529,6 @@ export class Workbench extends Disposable implements IPartService { } } - setTitlebarVisibility(visible: boolean): void { - this.titleBarHidden = visible; - } - isEditorLayoutCentered(): boolean { return this.shouldCenterLayout; } From cc4217ccbfc1d99b821fd65bc787c2cea820acb4 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 23 Jan 2019 17:39:05 -0800 Subject: [PATCH 050/274] Delete legacy file search - Fix #65337 --- src/vs/platform/search/common/search.ts | 1 - .../parts/search/browser/searchView.ts | 70 +---- .../parts/search/common/queryBuilder.ts | 9 +- .../parts/search/common/searchModel.ts | 2 - .../electron-browser/search.contribution.ts | 5 - .../services/search/node/fileSearch.ts | 2 +- .../node/legacy/rawLegacyTextSearchService.ts | 75 ----- .../services/search/node/legacy/search.ts | 35 --- .../services/search/node/legacy/textSearch.ts | 205 ------------ .../node/legacy/textSearchWorkerProvider.ts | 50 --- .../search/node/legacy/worker/searchWorker.ts | 291 ------------------ .../node/legacy/worker/searchWorkerApp.ts | 13 - .../node/legacy/worker/searchWorkerIpc.ts | 64 ---- .../services/search/node/rawSearchService.ts | 6 +- .../test/node/textSearch.integrationTest.ts | 50 +-- 15 files changed, 22 insertions(+), 856 deletions(-) delete mode 100644 src/vs/workbench/services/search/node/legacy/rawLegacyTextSearchService.ts delete mode 100644 src/vs/workbench/services/search/node/legacy/search.ts delete mode 100644 src/vs/workbench/services/search/node/legacy/textSearch.ts delete mode 100644 src/vs/workbench/services/search/node/legacy/textSearchWorkerProvider.ts delete mode 100644 src/vs/workbench/services/search/node/legacy/worker/searchWorker.ts delete mode 100644 src/vs/workbench/services/search/node/legacy/worker/searchWorkerApp.ts delete mode 100644 src/vs/workbench/services/search/node/legacy/worker/searchWorkerIpc.ts diff --git a/src/vs/platform/search/common/search.ts b/src/vs/platform/search/common/search.ts index 39029dc6706..605eebab8a3 100644 --- a/src/vs/platform/search/common/search.ts +++ b/src/vs/platform/search/common/search.ts @@ -317,7 +317,6 @@ export class OneLineRange extends SearchRange { export interface ISearchConfigurationProperties { exclude: glob.IExpression; useRipgrep: boolean; - useLegacySearch: boolean; /** * Use ignore file for file search. */ diff --git a/src/vs/workbench/parts/search/browser/searchView.ts b/src/vs/workbench/parts/search/browser/searchView.ts index bc43a26e383..f5077e3bb16 100644 --- a/src/vs/workbench/parts/search/browser/searchView.ts +++ b/src/vs/workbench/parts/search/browser/searchView.ts @@ -36,32 +36,32 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { TreeResourceNavigator2, WorkbenchObjectTree } from 'vs/platform/list/browser/listService'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { IProgressService } from 'vs/platform/progress/common/progress'; -import { IPatternInfo, ISearchComplete, ISearchConfiguration, ISearchConfigurationProperties, ISearchHistoryService, ISearchHistoryValues, ISearchProgressItem, ITextQuery, SearchErrorCode, VIEW_ID, IProgress } from 'vs/platform/search/common/search'; +import { IPatternInfo, ISearchComplete, ISearchConfiguration, ISearchConfigurationProperties, ISearchHistoryService, ISearchHistoryValues, ITextQuery, SearchErrorCode, VIEW_ID } from 'vs/platform/search/common/search'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { diffInserted, diffInsertedOutline, diffRemoved, diffRemovedOutline, editorFindMatchHighlight, editorFindMatchHighlightBorder, listActiveSelectionForeground } from 'vs/platform/theme/common/colorRegistry'; import { ICssStyleCollector, ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { OpenFileFolderAction, OpenFolderAction } from 'vs/workbench/browser/actions/workspaceActions'; +import { ResourceLabels } from 'vs/workbench/browser/labels'; import { Viewlet } from 'vs/workbench/browser/viewlet'; import { IEditor } from 'vs/workbench/common/editor'; import { IPanel } from 'vs/workbench/common/panel'; import { IViewlet } from 'vs/workbench/common/viewlet'; import { ExcludePatternInputWidget, PatternInputWidget } from 'vs/workbench/parts/search/browser/patternInputWidget'; -import { CancelSearchAction, ClearSearchResultsAction, CollapseDeepestExpandedLevelAction, RefreshAction, getKeyboardEventForEditorOpen } from 'vs/workbench/parts/search/browser/searchActions'; -import { FileMatchRenderer, FolderMatchRenderer, MatchRenderer, SearchDelegate, SearchAccessibilityProvider } from 'vs/workbench/parts/search/browser/searchResultsView'; +import { CancelSearchAction, ClearSearchResultsAction, CollapseDeepestExpandedLevelAction, getKeyboardEventForEditorOpen, RefreshAction } from 'vs/workbench/parts/search/browser/searchActions'; +import { FileMatchRenderer, FolderMatchRenderer, MatchRenderer, SearchAccessibilityProvider, SearchDelegate } from 'vs/workbench/parts/search/browser/searchResultsView'; import { ISearchWidgetOptions, SearchWidget } from 'vs/workbench/parts/search/browser/searchWidget'; import * as Constants from 'vs/workbench/parts/search/common/constants'; import { ITextQueryBuilderOptions, QueryBuilder } from 'vs/workbench/parts/search/common/queryBuilder'; import { IReplaceService } from 'vs/workbench/parts/search/common/replace'; import { getOutOfWorkspaceEditorResources } from 'vs/workbench/parts/search/common/search'; -import { FileMatch, FileMatchOrMatch, FolderMatch, IChangeEvent, ISearchWorkbenchService, Match, RenderableMatch, SearchModel, SearchResult, searchMatchComparer } from 'vs/workbench/parts/search/common/searchModel'; +import { FileMatch, FileMatchOrMatch, FolderMatch, IChangeEvent, ISearchWorkbenchService, Match, RenderableMatch, searchMatchComparer, SearchModel, SearchResult } from 'vs/workbench/parts/search/common/searchModel'; import { ACTIVE_GROUP, IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; import { IPartService } from 'vs/workbench/services/part/common/partService'; import { IPreferencesService, ISettingsEditorOptions } from 'vs/workbench/services/preferences/common/preferences'; import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; -import { ResourceLabels } from 'vs/workbench/browser/labels'; const $ = dom.$; @@ -1241,13 +1241,7 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { } private doSearch(query: ITextQuery, options: ITextQueryBuilderOptions, excludePatternText: string, includePatternText: string): Thenable { - // Progress total is 100.0% for more progress bar granularity - const progressTotal = 1000; - let progressWorked = 0; - - const progressRunner = query.useRipgrep ? - this.progressService.show(/*infinite=*/true) : - this.progressService.show(progressTotal); + const progressRunner = this.progressService.show(/*infinite=*/true); this.searchWidget.searchInput.clearMessage(); this.searching = true; @@ -1263,12 +1257,7 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { this.searching = false; // Complete up to 100% as needed - if (completed && !query.useRipgrep) { - progressRunner.worked(progressTotal - progressWorked); - setTimeout(() => progressRunner.done(), 200); - } else { - progressRunner.done(); - } + progressRunner.done(); // Do final render, then expand if just 1 file with less than 50 matches this.onSearchResultsChanged(); @@ -1370,13 +1359,7 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { this.searchWidget.searchInput.showMessage({ content: e.message, type: MessageType.ERROR }); this.viewModel.searchResult.clear(); - if (e.code === SearchErrorCode.unknownEncoding && !this.configurationService.getValue('search.useLegacySearch')) { - this.notificationService.prompt(Severity.Info, nls.localize('otherEncodingWarning', "You can enable \"search.useLegacySearch\" to search non-standard file encodings."), - [{ - label: nls.localize('otherEncodingWarning.openSettingsLabel', "Open Settings"), - run: () => this.openSettings('search.useLegacySearch') - }]); - } else if (e.code === SearchErrorCode.regexParseError && !this.configurationService.getValue('search.usePCRE2')) { + if (e.code === SearchErrorCode.regexParseError && !this.configurationService.getValue('search.usePCRE2')) { this.showPcre2Hint(); } @@ -1384,18 +1367,7 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { } }; - let total: number = 0; - let worked: number = 0; let visibleMatches = 0; - const onProgress = (p: ISearchProgressItem) => { - // Progress - if ((p).total) { - total = (p).total; - } - if ((p).worked) { - worked = (p).worked; - } - }; // Handle UI updates in an interval to show frequent progress and results const uiRefreshHandle: any = setInterval(() => { @@ -1404,30 +1376,6 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { return; } - if (!query.useRipgrep) { - // Progress bar update - let fakeProgress = true; - if (total > 0 && worked > 0) { - const ratio = Math.round((worked / total) * progressTotal); - if (ratio > progressWorked) { // never show less progress than what we have already - progressRunner.worked(ratio - progressWorked); - progressWorked = ratio; - fakeProgress = false; - } - } - - // Fake progress up to 90%, or when actual progress beats it - const fakeMax = 900; - const fakeMultiplier = 12; - if (fakeProgress && progressWorked < fakeMax) { - // Linearly decrease the rate of fake progress. - // 1 is the smallest allowed amount of progress. - const fakeAmt = Math.round((fakeMax - progressWorked) / fakeMax * fakeMultiplier) || 1; - progressWorked += fakeAmt; - progressRunner.worked(fakeAmt); - } - } - // Search result tree update const fileCount = this.viewModel.searchResult.fileCount(); if (visibleMatches !== fileCount) { @@ -1441,7 +1389,7 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { this.searchWidget.setReplaceAllActionState(false); - return this.viewModel.search(query, onProgress) + return this.viewModel.search(query) .then(onComplete, onError); } diff --git a/src/vs/workbench/parts/search/common/queryBuilder.ts b/src/vs/workbench/parts/search/common/queryBuilder.ts index 979b7a2e4d9..035215bddeb 100644 --- a/src/vs/workbench/parts/search/common/queryBuilder.ts +++ b/src/vs/workbench/parts/search/common/queryBuilder.ts @@ -51,7 +51,6 @@ export interface ICommonQueryBuilderOptions { maxResults?: number; maxFileSize?: number; - useRipgrep?: boolean; disregardIgnoreFiles?: boolean; disregardGlobalIgnoreFiles?: boolean; disregardExcludeSettings?: boolean; @@ -150,11 +149,6 @@ export class QueryBuilder { folderResources.map(uri => this.getFolderQueryForRoot(uri, options, excludePattern))) .filter(query => !!query) as IFolderQuery[]; - // const useRipgrep = !folderResources || folderResources.every(folder => { - // const folderConfig = this.configurationService.getValue({ resource: folder }); - // return !folderConfig.search.useLegacySearch; - // }); - const queryProps: ICommonQueryProps = { _reason: options._reason, folderQueries, @@ -163,8 +157,7 @@ export class QueryBuilder { excludePattern: excludePattern.pattern, includePattern, - maxResults: options.maxResults, - useRipgrep: true + maxResults: options.maxResults }; // Filter extraFileResources against global include/exclude patterns - they are already expected to not belong to a workspace diff --git a/src/vs/workbench/parts/search/common/searchModel.ts b/src/vs/workbench/parts/search/common/searchModel.ts index f254431f6de..edd87128a3f 100644 --- a/src/vs/workbench/parts/search/common/searchModel.ts +++ b/src/vs/workbench/parts/search/common/searchModel.ts @@ -960,7 +960,6 @@ export class SearchModel extends Disposable { "fileCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "options": { "${inline}": [ "${IPatternInfo}" ] }, "duration": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "useRipgrep": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "type" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, "scheme" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } } @@ -970,7 +969,6 @@ export class SearchModel extends Disposable { fileCount: this._searchResult.fileCount(), options, duration, - useRipgrep: this._searchQuery.useRipgrep, type: stats && stats.type, scheme }); diff --git a/src/vs/workbench/parts/search/electron-browser/search.contribution.ts b/src/vs/workbench/parts/search/electron-browser/search.contribution.ts index 67263d4f954..a299f18b82a 100644 --- a/src/vs/workbench/parts/search/electron-browser/search.contribution.ts +++ b/src/vs/workbench/parts/search/electron-browser/search.contribution.ts @@ -618,11 +618,6 @@ configurationRegistry.registerConfiguration({ deprecationMessage: nls.localize('useRipgrepDeprecated', "Deprecated. Consider \"search.usePCRE2\" for advanced regex feature support."), default: true }, - 'search.useLegacySearch': { - type: 'boolean', - description: nls.localize('useLegacySearch', "Controls whether to use the deprecated legacy mode for text and file search. It supports some text encodings that are not supported by the standard ripgrep-based search."), - default: false - }, 'search.useIgnoreFiles': { type: 'boolean', markdownDescription: nls.localize('useIgnoreFiles', "Controls whether to use `.gitignore` and `.ignore` files when searching for files."), diff --git a/src/vs/workbench/services/search/node/fileSearch.ts b/src/vs/workbench/services/search/node/fileSearch.ts index 138360c0ccc..2fe094108c8 100644 --- a/src/vs/workbench/services/search/node/fileSearch.ts +++ b/src/vs/workbench/services/search/node/fileSearch.ts @@ -75,7 +75,7 @@ export class FileWalker { constructor(config: IFileQuery, maxFileSize?: number) { this.config = config; - this.useRipgrep = config.useRipgrep !== false; + this.useRipgrep = true; this.filePattern = config.filePattern || ''; this.includePattern = config.includePattern && glob.parse(config.includePattern); this.maxResults = config.maxResults || null; diff --git a/src/vs/workbench/services/search/node/legacy/rawLegacyTextSearchService.ts b/src/vs/workbench/services/search/node/legacy/rawLegacyTextSearchService.ts deleted file mode 100644 index e7189c56c12..00000000000 --- a/src/vs/workbench/services/search/node/legacy/rawLegacyTextSearchService.ts +++ /dev/null @@ -1,75 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as fs from 'fs'; -import * as gracefulFs from 'graceful-fs'; -import { CancellationToken } from 'vs/base/common/cancellation'; -import { MAX_FILE_SIZE } from 'vs/platform/files/node/files'; -import { ITextQuery, QueryType } from 'vs/platform/search/common/search'; -import { FileWalker } from 'vs/workbench/services/search/node/fileSearch'; -import { Engine } from 'vs/workbench/services/search/node/legacy/textSearch'; -import { TextSearchWorkerProvider } from 'vs/workbench/services/search/node/legacy/textSearchWorkerProvider'; -import { BatchedCollector } from 'vs/workbench/services/search/node/textSearchManager'; -import { ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, ISerializedSearchSuccess } from '../search'; - -gracefulFs.gracefulify(fs); - -type IProgressCallback = (p: ISerializedSearchProgressItem) => void; - -export class LegacyTextSearchService { - private static readonly BATCH_SIZE = 512; - - private textSearchWorkerProvider: TextSearchWorkerProvider; - - textSearch(config: ITextQuery, progressCallback: IProgressCallback, token?: CancellationToken): Promise { - if (!this.textSearchWorkerProvider) { - this.textSearchWorkerProvider = new TextSearchWorkerProvider(); - } - - let engine = new Engine( - config, - new FileWalker({ - type: QueryType.File, - folderQueries: config.folderQueries, - extraFileResources: config.extraFileResources, - includePattern: config.includePattern, - excludePattern: config.excludePattern, - useRipgrep: false, - filePattern: '' - }, MAX_FILE_SIZE), - this.textSearchWorkerProvider); - - return this.doTextSearch(engine, progressCallback, LegacyTextSearchService.BATCH_SIZE, token); - } - - private doTextSearch(engine: Engine, progressCallback: IProgressCallback, batchSize: number, token?: CancellationToken): Promise { - if (token) { - token.onCancellationRequested(() => engine.cancel()); - } - - return new Promise((c, e) => { - // Use BatchedCollector to get new results to the frontend every 2s at least, until 50 results have been returned - const collector = new BatchedCollector(batchSize, progressCallback); - engine.search((matches) => { - const totalMatches = matches.reduce((acc, m) => acc + m.numMatches!, 0); - collector.addItems(matches, totalMatches); - }, (progress) => { - progressCallback(progress); - }, (error, stats) => { - collector.flush(); - - if (error) { - e(error); - } else { - c({ - type: 'success', - limitHit: stats.limitHit, - stats: null - }); - } - }); - }); - } -} diff --git a/src/vs/workbench/services/search/node/legacy/search.ts b/src/vs/workbench/services/search/node/legacy/search.ts deleted file mode 100644 index 84cac9d4f5a..00000000000 --- a/src/vs/workbench/services/search/node/legacy/search.ts +++ /dev/null @@ -1,35 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as glob from 'vs/base/common/glob'; -import { IPatternInfo, ITextSearchPreviewOptions } from 'vs/platform/search/common/search'; - -export interface IFolderSearch { - folder: string; - excludePattern?: glob.IExpression; - includePattern?: glob.IExpression; - fileEncoding?: string; - disregardIgnoreFiles?: boolean; - disregardGlobalIgnoreFiles?: boolean; -} - -export interface IRawSearch { - folderQueries: IFolderSearch[]; - ignoreSymlinks?: boolean; - extraFiles?: string[]; - filePattern?: string; - excludePattern?: glob.IExpression; - includePattern?: glob.IExpression; - contentPattern: IPatternInfo; - maxResults?: number; - exists?: boolean; - sortByScore?: boolean; - cacheKey?: string; - maxFilesize?: number; - useRipgrep?: boolean; - disregardIgnoreFiles?: boolean; - previewOptions?: ITextSearchPreviewOptions; - disregardGlobalIgnoreFiles?: boolean; -} diff --git a/src/vs/workbench/services/search/node/legacy/textSearch.ts b/src/vs/workbench/services/search/node/legacy/textSearch.ts deleted file mode 100644 index 0c8fa95a224..00000000000 --- a/src/vs/workbench/services/search/node/legacy/textSearch.ts +++ /dev/null @@ -1,205 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as path from 'path'; -import { onUnexpectedError } from 'vs/base/common/errors'; -import { IProgress, ITextQuery } from 'vs/platform/search/common/search'; -import { FileWalker } from 'vs/workbench/services/search/node/fileSearch'; -import { ISearchEngine, ISearchEngineSuccess, ISerializedFileMatch } from '../search'; -import { ITextSearchWorkerProvider } from './textSearchWorkerProvider'; -import { ISearchWorker, ISearchWorkerSearchArgs } from './worker/searchWorkerIpc'; -import { IRawSearch } from 'vs/workbench/services/search/node/legacy/search'; - -export class Engine implements ISearchEngine { - - private static readonly PROGRESS_FLUSH_CHUNK_SIZE = 50; // optimization: number of files to process before emitting progress event - - private config: IRawSearch; - private config2: ITextQuery; - private walker: FileWalker; - private walkerError: Error | null; - - private isCanceled = false; - private isDone = false; - private totalBytes = 0; - private processedBytes = 0; - private progressed = 0; - private walkerIsDone = false; - private limitReached = false; - private numResults = 0; - - private workerProvider: ITextSearchWorkerProvider; - private workers: ISearchWorker[]; - - private nextWorker = 0; - - constructor(config: ITextQuery, walker: FileWalker, workerProvider: ITextSearchWorkerProvider) { - this.config = makeRawSearch(config); - this.config2 = config; - this.walker = walker; - this.workerProvider = workerProvider; - } - - cancel(): void { - this.isCanceled = true; - this.walker.cancel(); - - this.workers.forEach(w => { - w.cancel() - .then(undefined, onUnexpectedError); - }); - } - - initializeWorkers(): void { - this.workers.forEach(w => { - w.initialize() - .then(undefined, onUnexpectedError); - }); - } - - search(onResult: (match: ISerializedFileMatch[]) => void, onProgress: (progress: IProgress) => void, done: (error: Error | null, complete: ISearchEngineSuccess) => void): void { - this.workers = this.workerProvider.getWorkers(); - this.initializeWorkers(); - - const fileEncoding = this.config.folderQueries.length === 1 ? - this.config.folderQueries[0].fileEncoding || 'utf8' : - 'utf8'; - - const progress = () => { - if (++this.progressed % Engine.PROGRESS_FLUSH_CHUNK_SIZE === 0) { - onProgress({ total: this.totalBytes, worked: this.processedBytes }); // buffer progress in chunks to reduce pressure - } - }; - - const unwind = (processed: number) => { - this.processedBytes += processed; - - // Emit progress() unless we got canceled or hit the limit - if (processed && !this.isDone && !this.isCanceled && !this.limitReached) { - progress(); - } - - // Emit done() - if (!this.isDone && this.processedBytes === this.totalBytes && this.walkerIsDone) { - this.isDone = true; - done(this.walkerError, { - limitHit: this.limitReached, - stats: this.walker.getStats() - }); - } - }; - - const run = (batch: string[], batchBytes: number): void => { - const worker = this.workers[this.nextWorker]; - this.nextWorker = (this.nextWorker + 1) % this.workers.length; - - const maxResults = this.config.maxResults && (this.config.maxResults - this.numResults); - const searchArgs: ISearchWorkerSearchArgs = { absolutePaths: batch, maxResults, pattern: this.config.contentPattern, fileEncoding, previewOptions: this.config.previewOptions }; - worker.search(searchArgs).then(result => { - if (!result || this.limitReached || this.isCanceled) { - return unwind(batchBytes); - } - - const matches = result.matches; - onResult(matches); - this.numResults += result.numMatches; - - if (this.config.maxResults && this.numResults >= this.config.maxResults) { - // It's possible to go over maxResults like this, but it's much simpler than trying to extract the exact number - // of file matches, line matches, and matches within a line to == maxResults. - this.limitReached = true; - } - - unwind(batchBytes); - }, - error => { - // An error on the worker's end, not in reading the file, but in processing the batch. Log and continue. - onUnexpectedError(error); - unwind(batchBytes); - }); - }; - - // Walk over the file system - let nextBatch: string[] = []; - let nextBatchBytes = 0; - const batchFlushBytes = 2 ** 20; // 1MB - this.walker.walk(this.config2.folderQueries, this.config2.extraFileResources || [], result => { - let bytes = result.size || 1; - this.totalBytes += bytes; - - // If we have reached the limit or we are canceled, ignore it - if (this.limitReached || this.isCanceled) { - return unwind(bytes); - } - - // Indicate progress to the outside - progress(); - - const absolutePath = result.base ? [result.base, result.relativePath].join(path.sep) : result.relativePath; - nextBatch.push(absolutePath); - nextBatchBytes += bytes; - - if (nextBatchBytes >= batchFlushBytes) { - run(nextBatch, nextBatchBytes); - nextBatch = []; - nextBatchBytes = 0; - } - }, - onProgress, - (error, isLimitHit) => { - this.walkerIsDone = true; - this.walkerError = error; - - // Send any remaining paths to a worker, or unwind if we're stopping - if (nextBatch.length) { - if (this.limitReached || this.isCanceled) { - unwind(nextBatchBytes); - } else { - run(nextBatch, nextBatchBytes); - } - } else { - unwind(0); - } - }); - } -} - -/** - * Exported for tests - */ -export function makeRawSearch(query: ITextQuery): IRawSearch { - let rawSearch: IRawSearch = { - folderQueries: [], - extraFiles: [], - excludePattern: query.excludePattern, - includePattern: query.includePattern, - maxResults: query.maxResults, - useRipgrep: query.useRipgrep, - disregardIgnoreFiles: query.folderQueries.some(fq => fq.disregardIgnoreFiles!), - disregardGlobalIgnoreFiles: query.folderQueries.some(fq => fq.disregardGlobalIgnoreFiles!), - ignoreSymlinks: query.folderQueries.some(fq => fq.ignoreSymlinks!), - previewOptions: query.previewOptions, - contentPattern: query.contentPattern - }; - - for (const q of query.folderQueries) { - rawSearch.folderQueries.push({ - excludePattern: q.excludePattern, - includePattern: q.includePattern, - fileEncoding: q.fileEncoding, - disregardIgnoreFiles: q.disregardIgnoreFiles, - disregardGlobalIgnoreFiles: q.disregardGlobalIgnoreFiles, - folder: q.folder.fsPath - }); - } - - if (query.extraFileResources) { - for (const r of query.extraFileResources) { - rawSearch.extraFiles!.push(r.fsPath); - } - } - - return rawSearch; -} diff --git a/src/vs/workbench/services/search/node/legacy/textSearchWorkerProvider.ts b/src/vs/workbench/services/search/node/legacy/textSearchWorkerProvider.ts deleted file mode 100644 index ac848db101e..00000000000 --- a/src/vs/workbench/services/search/node/legacy/textSearchWorkerProvider.ts +++ /dev/null @@ -1,50 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as os from 'os'; - -import * as ipc from 'vs/base/parts/ipc/node/ipc'; -import { Client } from 'vs/base/parts/ipc/node/ipc.cp'; - -import { ISearchWorker, SearchWorkerChannelClient } from './worker/searchWorkerIpc'; -import { getPathFromAmdModule } from 'vs/base/common/amd'; - -export interface ITextSearchWorkerProvider { - getWorkers(): ISearchWorker[]; -} - -export class TextSearchWorkerProvider implements ITextSearchWorkerProvider { - private workers: ISearchWorker[] = []; - - getWorkers(): ISearchWorker[] { - const numWorkers = os.cpus().length; - while (this.workers.length < numWorkers) { - this.createWorker(); - } - - return this.workers; - } - - private createWorker(): void { - let client = new Client( - getPathFromAmdModule(require, 'bootstrap-fork'), - { - serverName: 'Search Worker ' + this.workers.length, - args: ['--type=searchWorker'], - timeout: 30 * 1000, - env: { - AMD_ENTRYPOINT: 'vs/workbench/services/search/node/legacy/worker/searchWorkerApp', - PIPE_LOGGING: 'true', - VERBOSE_LOGGING: process.env.VERBOSE_LOGGING - }, - useQueue: true - }); - - const channel = ipc.getNextTickChannel(client.getChannel('searchWorker')); - const channelClient = new SearchWorkerChannelClient(channel); - - this.workers.push(channelClient); - } -} diff --git a/src/vs/workbench/services/search/node/legacy/worker/searchWorker.ts b/src/vs/workbench/services/search/node/legacy/worker/searchWorker.ts deleted file mode 100644 index 084455e1e46..00000000000 --- a/src/vs/workbench/services/search/node/legacy/worker/searchWorker.ts +++ /dev/null @@ -1,291 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as fs from 'fs'; -import * as gracefulFs from 'graceful-fs'; -import { onUnexpectedError } from 'vs/base/common/errors'; -import * as strings from 'vs/base/common/strings'; -import { bomLength, decode, detectEncodingFromBuffer, encodingExists, UTF16be, UTF16le, UTF8, UTF8_with_bom } from 'vs/base/node/encoding'; -import { Range } from 'vs/editor/common/core/range'; -import { ITextSearchPreviewOptions, TextSearchMatch } from 'vs/platform/search/common/search'; -import { ISearchWorker, ISearchWorkerSearchArgs, ISearchWorkerSearchResult } from './searchWorkerIpc'; -import { FileMatch } from 'vs/workbench/services/search/node/search'; - -gracefulFs.gracefulify(fs); - -interface ReadLinesOptions { - bufferLength: number; - encoding: string; -} - -const MAX_FILE_ERRORS = 5; // Don't report more than this number of errors, 1 per file, to avoid flooding the log when there's a general issue -let numErrorsLogged = 0; -function onError(error: any): void { - if (numErrorsLogged++ < MAX_FILE_ERRORS) { - onUnexpectedError(error); - } -} - -export class SearchWorker implements ISearchWorker { - private currentSearchEngine: SearchWorkerEngine; - - initialize(): Promise { - this.currentSearchEngine = new SearchWorkerEngine(); - return Promise.resolve(undefined); - } - - cancel(): Promise { - // Cancel the current search. It will stop searching and close its open files. - if (this.currentSearchEngine) { - this.currentSearchEngine.cancel(); - } - - return Promise.resolve(undefined); - } - - search(args: ISearchWorkerSearchArgs): Promise { - if (!this.currentSearchEngine) { - // Worker timed out during search - this.initialize(); - } - - return this.currentSearchEngine.searchBatch(args); - } -} - -interface IFileSearchResult { - match: FileMatch; - numMatches: number; - limitReached?: boolean; -} - -const LF = 0x0A; -const CR = 0x0D; - -export class SearchWorkerEngine { - private nextSearch: Promise = Promise.resolve(null); - private isCanceled = false; - - /** - * Searches some number of the given paths concurrently, and starts searches in other paths when those complete. - */ - searchBatch(args: ISearchWorkerSearchArgs): Promise { - const contentPattern = strings.createRegExp(args.pattern.pattern, !!args.pattern.isRegExp, { matchCase: args.pattern.isCaseSensitive, wholeWord: args.pattern.isWordMatch, multiline: false, global: true }); - const fileEncoding = encodingExists(args.fileEncoding) ? args.fileEncoding : UTF8; - return this.nextSearch = - this.nextSearch.then(() => this._searchBatch(args, contentPattern, fileEncoding)); - } - - - private _searchBatch(args: ISearchWorkerSearchArgs, contentPattern: RegExp, fileEncoding: string): Promise { - if (this.isCanceled) { - return Promise.resolve(null); - } - - return new Promise(batchDone => { - const result: ISearchWorkerSearchResult = { - matches: [], - numMatches: 0, - limitReached: false - }; - - // Search in the given path, and when it's finished, search in the next path in absolutePaths - const startSearchInFile = (absolutePath: string): Promise => { - return this.searchInFile(absolutePath, contentPattern, fileEncoding, args.maxResults && (args.maxResults - result.numMatches), args.previewOptions).then(fileResult => { - // Finish early if search is canceled - if (this.isCanceled) { - return; - } - - if (fileResult) { - result.numMatches += fileResult.numMatches; - result.matches.push(fileResult.match.serialize()); - if (fileResult.limitReached) { - // If the limit was reached, terminate early with the results so far and cancel in-progress searches. - this.cancel(); - result.limitReached = true; - return batchDone(result); - } - } - }, onError); - }; - - Promise.all(args.absolutePaths.map(startSearchInFile)).then(() => { - batchDone(result); - }); - }); - } - - cancel(): void { - this.isCanceled = true; - } - - private searchInFile(absolutePath: string, contentPattern: RegExp, fileEncoding: string, maxResults?: number, previewOptions?: ITextSearchPreviewOptions): Promise { - let fileMatch: FileMatch | null = null; - let limitReached = false; - let numMatches = 0; - - const perLineCallback = (line: string, lineNumber: number) => { - let match = contentPattern.exec(line); - - // Record all matches into file result - while (match !== null && match[0].length > 0 && !this.isCanceled && !limitReached) { - if (fileMatch === null) { - fileMatch = new FileMatch(absolutePath); - } - - const lineMatch = new TextSearchMatch(line, new Range(lineNumber, match.index, lineNumber, match.index + match[0].length), previewOptions); - fileMatch.addMatch(lineMatch); - - numMatches++; - if (maxResults && numMatches >= maxResults) { - limitReached = true; - } - - match = contentPattern.exec(line); - } - }; - - // Read lines buffered to support large files - return this.readlinesAsync(absolutePath, perLineCallback, { bufferLength: 8096, encoding: fileEncoding }).then( - () => fileMatch ? { match: fileMatch, limitReached, numMatches } : null); - } - - private readlinesAsync(filename: string, perLineCallback: (line: string, lineNumber: number) => void, options: ReadLinesOptions): Promise { - return new Promise((resolve, reject) => { - fs.open(filename, 'r', null, (error: Error, fd: number) => { - if (error) { - return resolve(undefined); - } - - const buffer = Buffer.allocUnsafe(options.bufferLength); - let line = ''; - let lineNumber = 0; - let lastBufferHadTrailingCR = false; - - const readFile = (isFirstRead: boolean, clb: (error: Error | null) => void): void => { - if (this.isCanceled) { - return clb(null); // return early if canceled or limit reached - } - - fs.read(fd, buffer, 0, buffer.length, null, (error: Error, bytesRead: number, buffer: Buffer) => { - const decodeBuffer = (buffer: Buffer, start: number, end: number): string => { - if (options.encoding === UTF8 || options.encoding === UTF8_with_bom) { - return buffer.toString(undefined, start, end); // much faster to use built in toString() when encoding is default - } - - return decode(buffer.slice(start, end), options.encoding); - }; - - const lineFinished = (offset: number): void => { - line += decodeBuffer(buffer, pos, i + offset); - perLineCallback(line, lineNumber); - line = ''; - lineNumber++; - pos = i + offset; - }; - - if (error || bytesRead === 0 || this.isCanceled) { - return clb(error); // return early if canceled or limit reached or no more bytes to read - } - - let crlfCharSize = 1; - let crBytes = [CR]; - let lfBytes = [LF]; - let pos = 0; - let i = 0; - - // Detect encoding and mime when this is the beginning of the file - if (isFirstRead) { - const detected = detectEncodingFromBuffer({ buffer, bytesRead }, false); - if (detected.seemsBinary) { - return clb(null); // skip files that seem binary - } - - // Check for BOM offset - switch (detected.encoding) { - case UTF8: - pos = i = bomLength(UTF8); - options.encoding = UTF8; - break; - case UTF16be: - pos = i = bomLength(UTF16be); - options.encoding = UTF16be; - break; - case UTF16le: - pos = i = bomLength(UTF16le); - options.encoding = UTF16le; - break; - } - - // when we are running with UTF16le/be, LF and CR are encoded as - // two bytes, like 0A 00 (LF) / 0D 00 (CR) for LE or flipped around - // for BE. We need to account for this when splitting the buffer into - // newlines, and when detecting a CRLF combo. - if (options.encoding === UTF16le) { - crlfCharSize = 2; - crBytes = [CR, 0x00]; - lfBytes = [LF, 0x00]; - } else if (options.encoding === UTF16be) { - crlfCharSize = 2; - crBytes = [0x00, CR]; - lfBytes = [0x00, LF]; - } - } - - if (lastBufferHadTrailingCR) { - if (buffer[i] === lfBytes[0] && (lfBytes.length === 1 || buffer[i + 1] === lfBytes[1])) { - lineFinished(1 * crlfCharSize); - i++; - } else { - lineFinished(0); - } - - lastBufferHadTrailingCR = false; - } - - /** - * This loop executes for every byte of every file in the workspace - it is highly performance-sensitive! - * Hence the duplication in reading the buffer to avoid a function call. Previously a function call was not - * being inlined by V8. - */ - for (; i < bytesRead; ++i) { - if (buffer[i] === lfBytes[0] && (lfBytes.length === 1 || buffer[i + 1] === lfBytes[1])) { - lineFinished(1 * crlfCharSize); - } else if (buffer[i] === crBytes[0] && (crBytes.length === 1 || buffer[i + 1] === crBytes[1])) { // CR (Carriage Return) - if (i + crlfCharSize === bytesRead) { - lastBufferHadTrailingCR = true; - } else if (buffer[i + crlfCharSize] === lfBytes[0] && (lfBytes.length === 1 || buffer[i + crlfCharSize + 1] === lfBytes[1])) { - lineFinished(2 * crlfCharSize); - i += 2 * crlfCharSize - 1; - } else { - lineFinished(1 * crlfCharSize); - } - } - } - - line += decodeBuffer(buffer, pos, bytesRead); - - readFile(/*isFirstRead=*/false, clb); // Continue reading - }); - }; - - readFile(/*isFirstRead=*/true, (error: Error) => { - if (error) { - return resolve(undefined); - } - - if (line.length) { - perLineCallback(line, lineNumber); // handle last line - } - - fs.close(fd, (error: Error) => { - resolve(undefined); - }); - }); - }); - }); - } -} diff --git a/src/vs/workbench/services/search/node/legacy/worker/searchWorkerApp.ts b/src/vs/workbench/services/search/node/legacy/worker/searchWorkerApp.ts deleted file mode 100644 index 3abda129ab4..00000000000 --- a/src/vs/workbench/services/search/node/legacy/worker/searchWorkerApp.ts +++ /dev/null @@ -1,13 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Server } from 'vs/base/parts/ipc/node/ipc.cp'; -import { SearchWorkerChannel } from './searchWorkerIpc'; -import { SearchWorker } from './searchWorker'; - -const server = new Server('searchWorker'); -const worker = new SearchWorker(); -const channel = new SearchWorkerChannel(worker); -server.registerChannel('searchWorker', channel); diff --git a/src/vs/workbench/services/search/node/legacy/worker/searchWorkerIpc.ts b/src/vs/workbench/services/search/node/legacy/worker/searchWorkerIpc.ts deleted file mode 100644 index 693574a3794..00000000000 --- a/src/vs/workbench/services/search/node/legacy/worker/searchWorkerIpc.ts +++ /dev/null @@ -1,64 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { IChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc'; -import { IPatternInfo, ITextSearchPreviewOptions } from 'vs/platform/search/common/search'; -import { SearchWorker } from './searchWorker'; -import { Event } from 'vs/base/common/event'; -import { ISerializedFileMatch } from 'vs/workbench/services/search/node/search'; - -export interface ISearchWorkerSearchArgs { - pattern: IPatternInfo; - fileEncoding: string; - absolutePaths: string[]; - maxResults?: number; - previewOptions?: ITextSearchPreviewOptions; -} - -export interface ISearchWorkerSearchResult { - matches: ISerializedFileMatch[]; - numMatches: number; - limitReached: boolean; -} - -export interface ISearchWorker { - initialize(): Promise; - search(args: ISearchWorkerSearchArgs): Promise; - cancel(): Promise; -} - -export class SearchWorkerChannel implements IServerChannel { - constructor(private worker: SearchWorker) { - } - - listen(): Event { - throw new Error('No events'); - } - - call(_, command: string, arg?: any): Promise { - switch (command) { - case 'initialize': return this.worker.initialize(); - case 'search': return this.worker.search(arg); - case 'cancel': return this.worker.cancel(); - } - throw new Error(`Call not found: ${command}`); - } -} - -export class SearchWorkerChannelClient implements ISearchWorker { - constructor(private channel: IChannel) { } - - initialize(): Promise { - return this.channel.call('initialize'); - } - - search(args: ISearchWorkerSearchArgs): Promise { - return this.channel.call('search', args); - } - - cancel(): Promise { - return this.channel.call('cancel'); - } -} diff --git a/src/vs/workbench/services/search/node/rawSearchService.ts b/src/vs/workbench/services/search/node/rawSearchService.ts index 4dc4c3bca14..da52da42622 100644 --- a/src/vs/workbench/services/search/node/rawSearchService.ts +++ b/src/vs/workbench/services/search/node/rawSearchService.ts @@ -19,7 +19,6 @@ import { compareItemsByScore, IItemAccessor, prepareQuery, ScorerCache } from 'v import { MAX_FILE_SIZE } from 'vs/platform/files/node/files'; import { ICachedSearchStats, IFileQuery, IFileSearchStats, IFolderQuery, IProgress, IRawFileQuery, IRawQuery, IRawTextQuery, ITextQuery } from 'vs/platform/search/common/search'; import { Engine as FileSearchEngine } from 'vs/workbench/services/search/node/fileSearch'; -import { LegacyTextSearchService } from 'vs/workbench/services/search/node/legacy/rawLegacyTextSearchService'; import { TextSearchEngineAdapter } from 'vs/workbench/services/search/node/textSearchAdapter'; import { IFileSearchProgressItem, IRawFileMatch, IRawSearchService, ISearchEngine, ISearchEngineSuccess, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, ISerializedSearchSuccess } from './search'; @@ -32,7 +31,6 @@ export class SearchService implements IRawSearchService { private static readonly BATCH_SIZE = 512; - private legacyTextSearchService = new LegacyTextSearchService(); private caches: { [cacheKey: string]: Cache; } = Object.create(null); fileSearch(config: IRawFileQuery): Event { @@ -64,9 +62,7 @@ export class SearchService implements IRawSearchService { const emitter = new Emitter({ onFirstListenerDidAdd: () => { promise = createCancelablePromise(token => { - return (rawQuery.useRipgrep ? - this.ripgrepTextSearch(query, p => emitter.fire(p), token) : - this.legacyTextSearchService.textSearch(query, p => emitter.fire(p), token)); + return this.ripgrepTextSearch(query, p => emitter.fire(p), token); }); promise.then( diff --git a/src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts b/src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts index 6d776117484..8b56afda721 100644 --- a/src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts +++ b/src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts @@ -9,15 +9,10 @@ import { getPathFromAmdModule } from 'vs/base/common/amd'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import * as glob from 'vs/base/common/glob'; import { URI } from 'vs/base/common/uri'; -import { IFolderQuery, ISearchRange, ITextQuery, ITextSearchMatch, QueryType, ITextSearchContext, deserializeSearchError, SearchErrorCode } from 'vs/platform/search/common/search'; -import { LegacyTextSearchService } from 'vs/workbench/services/search/node/legacy/rawLegacyTextSearchService'; +import { deserializeSearchError, IFolderQuery, ISearchRange, ITextQuery, ITextSearchContext, ITextSearchMatch, QueryType, SearchErrorCode } from 'vs/platform/search/common/search'; import { ISerializedFileMatch } from 'vs/workbench/services/search/node/search'; import { TextSearchEngineAdapter } from 'vs/workbench/services/search/node/textSearchAdapter'; -function countAll(matches: ISerializedFileMatch[]): number { - return matches.reduce((acc, m) => acc + m.numMatches!, 0); -} - const TEST_FIXTURES = path.normalize(getPathFromAmdModule(require, './fixtures')); const EXAMPLES_FIXTURES = path.join(TEST_FIXTURES, 'examples'); const MORE_FIXTURES = path.join(TEST_FIXTURES, 'more'); @@ -31,24 +26,7 @@ const MULTIROOT_QUERIES: IFolderQuery[] = [ { folder: URI.file(MORE_FIXTURES) } ]; -function doLegacySearchTest(config: ITextQuery, expectedResultCount: number | Function): Promise { - const engine = new LegacyTextSearchService(); - - let c = 0; - return engine.textSearch(config, (result) => { - if (result && Array.isArray(result)) { - c += countAll(result); - } - }, null!).then(() => { - if (typeof expectedResultCount === 'function') { - assert(expectedResultCount(c)); - } else { - assert.equal(c, expectedResultCount, 'legacy'); - } - }); -} - -function doRipgrepSearchTest(query: ITextQuery, expectedResultCount: number | Function): Promise { +function doSearchTest(query: ITextQuery, expectedResultCount: number | Function): Promise { const engine = new TextSearchEngineAdapter(query); let c = 0; @@ -69,11 +47,6 @@ function doRipgrepSearchTest(query: ITextQuery, expectedResultCount: number | Fu }); } -function doSearchTest(query: ITextQuery, expectedResultCount: number) { - return doLegacySearchTest(query, expectedResultCount) - .then(() => doRipgrepSearchTest(query, expectedResultCount)); -} - suite('Search-integration', function () { this.timeout(1000 * 60); // increase timeout for this suite @@ -240,10 +213,7 @@ suite('Search-integration', function () { maxResults }; - // (Legacy) search can go over the maxResults because it doesn't trim the results from its worker processes to the exact max size. - // But the worst-case scenario should be 2*max-1 - return doLegacySearchTest(config, count => count < maxResults * 2) - .then(() => doRipgrepSearchTest(config, maxResults)); + return doSearchTest(config, maxResults); }); test('Text: a (no results)', () => { @@ -318,7 +288,7 @@ suite('Search-integration', function () { contentPattern: { pattern: '语' } }; - return doRipgrepSearchTest(config, 1).then(results => { + return doSearchTest(config, 1).then(results => { const matchRange = (results[0].results![0]).ranges; assert.deepEqual(matchRange, [{ startLineNumber: 0, @@ -336,7 +306,7 @@ suite('Search-integration', function () { contentPattern: { pattern: 'h\\d,', isRegExp: true } }; - return doRipgrepSearchTest(config, 15).then(results => { + return doSearchTest(config, 15).then(results => { assert.equal(results.length, 3); assert.equal(results[0].results!.length, 1); const match = results[0].results![0]; @@ -353,7 +323,7 @@ suite('Search-integration', function () { afterContext: 2 }; - return doRipgrepSearchTest(config, 4).then(results => { + return doSearchTest(config, 4).then(results => { assert.equal(results.length, 4); assert.equal((results[0].results![0]).lineNumber, 25); assert.equal((results[0].results![0]).text, ' compiler.addUnit(prog,"input.ts");'); @@ -378,7 +348,7 @@ suite('Search-integration', function () { contentPattern: { pattern: 'test' }, }; - return doRipgrepSearchTest(config, 0).then(() => { + return doSearchTest(config, 0).then(() => { throw new Error('expected fail'); }, err => { const searchError = deserializeSearchError(err.message); @@ -394,7 +364,7 @@ suite('Search-integration', function () { contentPattern: { pattern: ')', isRegExp: true }, }; - return doRipgrepSearchTest(config, 0).then(() => { + return doSearchTest(config, 0).then(() => { throw new Error('expected fail'); }, err => { const searchError = deserializeSearchError(err.message); @@ -413,7 +383,7 @@ suite('Search-integration', function () { } }; - return doRipgrepSearchTest(config, 0).then(() => { + return doSearchTest(config, 0).then(() => { throw new Error('expected fail'); }, err => { const searchError = deserializeSearchError(err.message); @@ -429,7 +399,7 @@ suite('Search-integration', function () { contentPattern: { pattern: 'foo\nbar', isRegExp: true } }; - return doRipgrepSearchTest(config, 0).then(() => { + return doSearchTest(config, 0).then(() => { throw new Error('expected fail'); }, err => { const searchError = deserializeSearchError(err.message); From 53d325a6db70c4f70310553f7f63e31d5aca1276 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 23 Jan 2019 18:01:12 -0800 Subject: [PATCH 051/274] More cleanup from #65337 --- src/vs/platform/search/common/search.ts | 2 - .../electron-browser/mainThreadWorkspace.ts | 6 -- .../search/test/common/queryBuilder.test.ts | 2 +- .../search/test/common/searchModel.test.ts | 1 - .../services/search/node/fileSearch.ts | 97 ++++--------------- .../services/search/node/searchService.ts | 7 +- .../search/test/node/rawSearchService.test.ts | 1 - .../services/search/test/node/search.test.ts | 29 +++--- 8 files changed, 35 insertions(+), 110 deletions(-) diff --git a/src/vs/platform/search/common/search.ts b/src/vs/platform/search/common/search.ts index 605eebab8a3..877b69da34a 100644 --- a/src/vs/platform/search/common/search.ts +++ b/src/vs/platform/search/common/search.ts @@ -81,7 +81,6 @@ export interface ICommonQueryProps { excludePattern?: glob.IExpression; extraFileResources?: U[]; - useRipgrep?: boolean; maxResults?: number; usingSearchPaths?: boolean; } @@ -232,7 +231,6 @@ export interface ICachedSearchStats { } export interface ISearchEngineStats { - traversal: string; fileWalkTime: number; directoriesWalked: number; filesWalked: number; diff --git a/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts b/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts index da0ef360fdd..14baea58a97 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts @@ -131,11 +131,6 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape { return undefined; // invalid query parameters } - const useRipgrep = folderQueries.every(folderQuery => { - const folderConfig = this._configurationService.getValue({ resource: folderQuery.folder }); - return folderConfig.search.useRipgrep; - }); - const ignoreSymlinks = folderQueries.every(folderQuery => { const folderConfig = this._configurationService.getValue({ resource: folderQuery.folder }); return !folderConfig.search.followSymlinks; @@ -151,7 +146,6 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape { type: QueryType.File, maxResults, disregardExcludeSettings: excludePatternOrDisregardExcludes === false, - useRipgrep, _reason: 'startFileSearch' }; if (typeof includePattern === 'string') { diff --git a/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts b/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts index a62178ae986..310e3b4d731 100644 --- a/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts +++ b/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts @@ -17,7 +17,7 @@ import { TestContextService, TestEnvironmentService } from 'vs/workbench/test/wo const DEFAULT_EDITOR_CONFIG = {}; const DEFAULT_USER_CONFIG = { useRipgrep: true, useIgnoreFiles: true, useGlobalIgnoreFiles: true }; -const DEFAULT_QUERY_PROPS = { useRipgrep: true }; +const DEFAULT_QUERY_PROPS = {}; const DEFAULT_TEXT_QUERY_PROPS = { usePCRE2: false }; suite('QueryBuilder', () => { diff --git a/src/vs/workbench/parts/search/test/common/searchModel.test.ts b/src/vs/workbench/parts/search/test/common/searchModel.test.ts index 5a8b8fdeb95..3e20387d40a 100644 --- a/src/vs/workbench/parts/search/test/common/searchModel.test.ts +++ b/src/vs/workbench/parts/search/test/common/searchModel.test.ts @@ -50,7 +50,6 @@ suite('SearchModel', () => { resultCount: 1, type: 'searchProcess', detailStats: { - traversal: 'node', fileWalkTime: 0, cmdTime: 0, cmdResultCount: 0, diff --git a/src/vs/workbench/services/search/node/fileSearch.ts b/src/vs/workbench/services/search/node/fileSearch.ts index 2fe094108c8..e656f69ccc6 100644 --- a/src/vs/workbench/services/search/node/fileSearch.ts +++ b/src/vs/workbench/services/search/node/fileSearch.ts @@ -25,13 +25,6 @@ import { IFileQuery, IFolderQuery, IProgress, ISearchEngineStats } from 'vs/plat import { IRawFileMatch, ISearchEngine, ISearchEngineSuccess } from 'vs/workbench/services/search/node/search'; import { spawnRipgrepCmd } from './ripgrepFileSearch'; -enum Traversal { - Node = 1, - MacFind, - LinuxFind, - Ripgrep -} - interface IDirectoryEntry { base: string; relativePath: string; @@ -50,7 +43,6 @@ process.on('exit', () => { export class FileWalker { private config: IFileQuery; - private useRipgrep: boolean; private filePattern: string; private normalizedFilePatternLowercase: string; private includePattern: glob.ParsedExpression | undefined; @@ -63,7 +55,6 @@ export class FileWalker { private fileWalkSW: StopWatch; private directoriesWalked: number; private filesWalked: number; - private traversal: Traversal; private errors: string[]; private cmdSW: StopWatch; private cmdResultCount: number; @@ -73,20 +64,17 @@ export class FileWalker { private walkedPaths: { [path: string]: boolean; }; - constructor(config: IFileQuery, maxFileSize?: number) { + constructor(config: IFileQuery) { this.config = config; - this.useRipgrep = true; this.filePattern = config.filePattern || ''; this.includePattern = config.includePattern && glob.parse(config.includePattern); this.maxResults = config.maxResults || null; this.exists = !!config.exists; - this.maxFilesize = maxFileSize || null; this.walkedPaths = Object.create(null); this.resultCount = 0; this.isLimitHit = false; this.directoriesWalked = 0; this.filesWalked = 0; - this.traversal = Traversal.Node; this.errors = []; if (this.filePattern) { @@ -138,28 +126,11 @@ export class FileWalker { this.matchFile(onResult, { relativePath: extraFilePath.fsPath /* no workspace relative path */, basename }); }); - let traverse = this.nodeJSTraversal; - if (!this.maxFilesize) { - if (this.useRipgrep) { - this.traversal = Traversal.Ripgrep; - traverse = this.cmdTraversal; - } else if (platform.isMacintosh) { - this.traversal = Traversal.MacFind; - traverse = this.cmdTraversal; - } else if (platform.isLinux) { - this.traversal = Traversal.LinuxFind; - traverse = this.cmdTraversal; - } - } - - const isNodeTraversal = traverse === this.nodeJSTraversal; - if (!isNodeTraversal) { - this.cmdSW = StopWatch.create(false); - } + this.cmdSW = StopWatch.create(false); // For each root folder flow.parallel(folderQueries, (folderQuery: IFolderQuery, rootFolderDone: (err: Error | null, result: void) => void) => { - this.call(traverse, this, folderQuery, onResult, onMessage, (err?: Error) => { + this.call(this.cmdTraversal, this, folderQuery, onResult, onMessage, (err?: Error) => { if (err) { const errorMessage = toErrorMessage(err); console.error(errorMessage); @@ -197,31 +168,25 @@ export class FileWalker { cb(err); }; let leftover = ''; - let first = true; const tree = this.initDirectoryTree(); - const useRipgrep = this.useRipgrep; let noSiblingsClauses: boolean; - if (useRipgrep) { - const ripgrep = spawnRipgrepCmd(this.config, folderQuery, this.config.includePattern, this.folderExcludePatterns.get(folderQuery.folder.fsPath)!.expression); - cmd = ripgrep.cmd; - noSiblingsClauses = !Object.keys(ripgrep.siblingClauses).length; + const ripgrep = spawnRipgrepCmd(this.config, folderQuery, this.config.includePattern, this.folderExcludePatterns.get(folderQuery.folder.fsPath)!.expression); + cmd = ripgrep.cmd; + noSiblingsClauses = !Object.keys(ripgrep.siblingClauses).length; - const escapedArgs = ripgrep.rgArgs.args - .map(arg => arg.match(/^-/) ? arg : `'${arg}'`) - .join(' '); + const escapedArgs = ripgrep.rgArgs.args + .map(arg => arg.match(/^-/) ? arg : `'${arg}'`) + .join(' '); - let rgCmd = `rg ${escapedArgs}\n - cwd: ${ripgrep.cwd}`; - if (ripgrep.rgArgs.siblingClauses) { - rgCmd += `\n - Sibling clauses: ${JSON.stringify(ripgrep.rgArgs.siblingClauses)}`; - } - onMessage({ message: rgCmd }); - } else { - cmd = this.spawnFindCmd(folderQuery); + let rgCmd = `rg ${escapedArgs}\n - cwd: ${ripgrep.cwd}`; + if (ripgrep.rgArgs.siblingClauses) { + rgCmd += `\n - Sibling clauses: ${JSON.stringify(ripgrep.rgArgs.siblingClauses)}`; } + onMessage({ message: rgCmd }); this.cmdResultCount = 0; - this.collectStdout(cmd, 'utf8', useRipgrep, onMessage, (err: Error, stdout?: string, last?: boolean) => { + this.collectStdout(cmd, 'utf8', onMessage, (err: Error, stdout?: string, last?: boolean) => { if (err) { done(err); return; @@ -233,11 +198,7 @@ export class FileWalker { // Mac: uses NFD unicode form on disk, but we want NFC const normalized = leftover + (isMac ? normalization.normalizeNFC(stdout || '') : stdout); - const relativeFiles = normalized.split(useRipgrep ? '\n' : '\n./'); - if (!useRipgrep && first && normalized.length >= 2) { - first = false; - relativeFiles[0] = relativeFiles[0].trim().substr(2); - } + const relativeFiles = normalized.split('\n'); if (last) { const n = relativeFiles.length; @@ -256,7 +217,7 @@ export class FileWalker { this.cmdResultCount += relativeFiles.length; - if (useRipgrep && noSiblingsClauses) { + if (noSiblingsClauses) { for (const relativePath of relativeFiles) { const basename = path.basename(relativePath); this.matchFile(onResult, { base: rootFolder, relativePath, basename }); @@ -310,9 +271,9 @@ export class FileWalker { /** * Public for testing. */ - readStdout(cmd: childProcess.ChildProcess, encoding: string, isRipgrep: boolean, cb: (err: Error | null, stdout?: string) => void): void { + readStdout(cmd: childProcess.ChildProcess, encoding: string, cb: (err: Error | null, stdout?: string) => void): void { let all = ''; - this.collectStdout(cmd, encoding, isRipgrep, () => { }, (err: Error, stdout?: string, last?: boolean) => { + this.collectStdout(cmd, encoding, () => { }, (err: Error, stdout?: string, last?: boolean) => { if (err) { cb(err); return; @@ -325,7 +286,7 @@ export class FileWalker { }); } - private collectStdout(cmd: childProcess.ChildProcess, encoding: string, isRipgrep: boolean, onMessage: (message: IProgress) => void, cb: (err: Error | null, stdout?: string, last?: boolean) => void): void { + private collectStdout(cmd: childProcess.ChildProcess, encoding: string, onMessage: (message: IProgress) => void, cb: (err: Error | null, stdout?: string, last?: boolean) => void): void { let onData = (err: Error | null, stdout?: string, last?: boolean) => { if (err || last) { onData = () => { }; @@ -361,10 +322,10 @@ export class FileWalker { cmd.on('close', (code: number) => { // ripgrep returns code=1 when no results are found let stderrText: string; - if (isRipgrep ? (!gotData && (stderrText = this.decodeData(stderr, encoding)) && rgErrorMsgForDisplay(stderrText)) : code !== 0) { + if (!gotData && (stderrText = this.decodeData(stderr, encoding)) && rgErrorMsgForDisplay(stderrText)) { onData(new Error(`command failed with error code ${code}: ${this.decodeData(stderr, encoding)}`)); } else { - if (isRipgrep && this.exists && code === 0) { + if (this.exists && code === 0) { this.isLimitHit = true; } onData(null, '', true); @@ -465,26 +426,10 @@ export class FileWalker { matchDirectory(rootEntries); } - private nodeJSTraversal(folderQuery: IFolderQuery, onResult: (result: IRawFileMatch) => void, onMessage: (message: IProgress) => void, done: (err?: Error) => void): void { - this.directoriesWalked++; - extfs.readdir(folderQuery.folder.fsPath, (error: Error, files: string[]) => { - if (error || this.isCanceled || this.isLimitHit) { - return done(); - } - - if (this.isCanceled || this.isLimitHit) { - return done(); - } - - return this.doWalk(folderQuery, '', files, onResult, done); - }); - } - getStats(): ISearchEngineStats { return { cmdTime: this.cmdSW && this.cmdSW.elapsed(), fileWalkTime: this.fileWalkSW.elapsed(), - traversal: Traversal[this.traversal], directoriesWalked: this.directoriesWalked, filesWalked: this.filesWalked, cmdResultCount: this.cmdResultCount diff --git a/src/vs/workbench/services/search/node/searchService.ts b/src/vs/workbench/services/search/node/searchService.ts index 8020ff5fd02..8d8fc5f2ef2 100644 --- a/src/vs/workbench/services/search/node/searchService.ts +++ b/src/vs/workbench/services/search/node/searchService.ts @@ -315,14 +315,12 @@ export class SearchService extends Disposable implements ISearchService { "type" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, "endToEndTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "sortingTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "traversal" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, "fileWalkTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "directoriesWalked" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "filesWalked" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "cmdTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "cmdResultCount" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "scheme" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, - "useRipgrep" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } } */ this.telemetryService.publicLog('searchComplete', { @@ -332,14 +330,12 @@ export class SearchService extends Disposable implements ISearchService { type: fileSearchStats.type, endToEndTime: endToEndTime, sortingTime: fileSearchStats.sortingTime, - traversal: searchEngineStats.traversal, fileWalkTime: searchEngineStats.fileWalkTime, directoriesWalked: searchEngineStats.directoriesWalked, filesWalked: searchEngineStats.filesWalked, cmdTime: searchEngineStats.cmdTime, cmdResultCount: searchEngineStats.cmdResultCount, - scheme, - useRipgrep: query.useRipgrep + scheme }); } } else if (query.type === QueryType.Text) { @@ -370,7 +366,6 @@ export class SearchService extends Disposable implements ISearchService { endToEndTime: endToEndTime, scheme, error: errorType, - useRipgrep: query.useRipgrep, usePCRE2: !!query.usePCRE2 }); } diff --git a/src/vs/workbench/services/search/test/node/rawSearchService.test.ts b/src/vs/workbench/services/search/test/node/rawSearchService.test.ts index f70c142c406..de7cf1d7964 100644 --- a/src/vs/workbench/services/search/test/node/rawSearchService.test.ts +++ b/src/vs/workbench/services/search/test/node/rawSearchService.test.ts @@ -25,7 +25,6 @@ const MULTIROOT_QUERIES: IFolderQuery[] = [ ]; const stats: ISearchEngineStats = { - traversal: 'node', fileWalkTime: 0, cmdTime: 1, directoriesWalked: 2, diff --git a/src/vs/workbench/services/search/test/node/search.test.ts b/src/vs/workbench/services/search/test/node/search.test.ts index c72a41992e6..ff89992c919 100644 --- a/src/vs/workbench/services/search/test/node/search.test.ts +++ b/src/vs/workbench/services/search/test/node/search.test.ts @@ -81,7 +81,6 @@ suite('FileSearchEngine', () => { type: QueryType.File, folderQueries: ROOT_FOLDER_QUERY, maxResults: 1, - useRipgrep: false }); let count = 0; @@ -147,7 +146,6 @@ suite('FileSearchEngine', () => { folderQueries: ROOT_FOLDER_QUERY, includePattern: { '**/file.txt': true }, exists: true, - useRipgrep: false }); let count = 0; @@ -170,7 +168,6 @@ suite('FileSearchEngine', () => { folderQueries: ROOT_FOLDER_QUERY, includePattern: { '**/nofile.txt': true }, exists: true, - useRipgrep: false }); let count = 0; @@ -256,7 +253,6 @@ suite('FileSearchEngine', () => { '*.txt': true, '*.js': true }, - useRipgrep: true }); let count = 0; @@ -281,7 +277,6 @@ suite('FileSearchEngine', () => { '*.txt': true, '*.js': true }, - useRipgrep: true }); let count = 0; @@ -777,7 +772,7 @@ suite('FileWalker', () => { excludePattern: { '**/something': true } }); const cmd1 = walker.spawnFindCmd(TEST_ROOT_FOLDER); - walker.readStdout(cmd1, 'utf8', /*isRipgrep=*/false, (err1, stdout1) => { + walker.readStdout(cmd1, 'utf8', (err1, stdout1) => { assert.equal(err1, null); assert.notStrictEqual(stdout1!.split('\n').indexOf(file0), -1, stdout1); assert.notStrictEqual(stdout1!.split('\n').indexOf(file1), -1, stdout1); @@ -788,7 +783,7 @@ suite('FileWalker', () => { excludePattern: { '**/subfolder': true } }); const cmd2 = walker.spawnFindCmd(TEST_ROOT_FOLDER); - walker.readStdout(cmd2, 'utf8', /*isRipgrep=*/false, (err2, stdout2) => { + walker.readStdout(cmd2, 'utf8', (err2, stdout2) => { assert.equal(err2, null); assert.notStrictEqual(stdout1!.split('\n').indexOf(file0), -1, stdout1); assert.strictEqual(stdout2!.split('\n').indexOf(file1), -1, stdout2); @@ -816,7 +811,7 @@ suite('FileWalker', () => { const walker = new FileWalker({ type: QueryType.File, folderQueries }); const cmd1 = walker.spawnFindCmd(folderQueries[0]); - walker.readStdout(cmd1, 'utf8', /*isRipgrep=*/false, (err1, stdout1) => { + walker.readStdout(cmd1, 'utf8', (err1, stdout1) => { assert.equal(err1, null); assert(outputContains(stdout1!, file0), stdout1); assert(!outputContains(stdout1!, file1), stdout1); @@ -837,7 +832,7 @@ suite('FileWalker', () => { const walker = new FileWalker({ type: QueryType.File, folderQueries: ROOT_FOLDER_QUERY, excludePattern: { '**/something': true } }); const cmd1 = walker.spawnFindCmd(TEST_ROOT_FOLDER); - walker.readStdout(cmd1, 'utf8', /*isRipgrep=*/false, (err1, stdout1) => { + walker.readStdout(cmd1, 'utf8', (err1, stdout1) => { assert.equal(err1, null); assert.notStrictEqual(stdout1!.split('\n').indexOf(file0), -1, stdout1); assert.notStrictEqual(stdout1!.split('\n').indexOf(file1), -1, stdout1); @@ -845,7 +840,7 @@ suite('FileWalker', () => { const walker = new FileWalker({ type: QueryType.File, folderQueries: ROOT_FOLDER_QUERY, excludePattern: { '{**/examples,**/more}': true } }); const cmd2 = walker.spawnFindCmd(TEST_ROOT_FOLDER); - walker.readStdout(cmd2, 'utf8', /*isRipgrep=*/false, (err2, stdout2) => { + walker.readStdout(cmd2, 'utf8', (err2, stdout2) => { assert.equal(err2, null); assert.notStrictEqual(stdout1!.split('\n').indexOf(file0), -1, stdout1); assert.strictEqual(stdout2!.split('\n').indexOf(file1), -1, stdout2); @@ -867,14 +862,14 @@ suite('FileWalker', () => { const walker = new FileWalker({ type: QueryType.File, folderQueries: ROOT_FOLDER_QUERY, excludePattern: { '**/examples/something': true } }); const cmd1 = walker.spawnFindCmd(TEST_ROOT_FOLDER); - walker.readStdout(cmd1, 'utf8', /*isRipgrep=*/false, (err1, stdout1) => { + walker.readStdout(cmd1, 'utf8', (err1, stdout1) => { assert.equal(err1, null); assert.notStrictEqual(stdout1!.split('\n').indexOf(file0), -1, stdout1); assert.notStrictEqual(stdout1!.split('\n').indexOf(file1), -1, stdout1); const walker = new FileWalker({ type: QueryType.File, folderQueries: ROOT_FOLDER_QUERY, excludePattern: { '**/examples/subfolder': true } }); const cmd2 = walker.spawnFindCmd(TEST_ROOT_FOLDER); - walker.readStdout(cmd2, 'utf8', /*isRipgrep=*/false, (err2, stdout2) => { + walker.readStdout(cmd2, 'utf8', (err2, stdout2) => { assert.equal(err2, null); assert.notStrictEqual(stdout1!.split('\n').indexOf(file0), -1, stdout1); assert.strictEqual(stdout2!.split('\n').indexOf(file1), -1, stdout2); @@ -895,14 +890,14 @@ suite('FileWalker', () => { const walker = new FileWalker({ type: QueryType.File, folderQueries: ROOT_FOLDER_QUERY, excludePattern: { '**/subfolder/something': true } }); const cmd1 = walker.spawnFindCmd(TEST_ROOT_FOLDER); - walker.readStdout(cmd1, 'utf8', /*isRipgrep=*/false, (err1, stdout1) => { + walker.readStdout(cmd1, 'utf8', (err1, stdout1) => { assert.equal(err1, null); assert.notStrictEqual(stdout1!.split('\n').indexOf(file0), -1, stdout1); assert.notStrictEqual(stdout1!.split('\n').indexOf(file1), -1, stdout1); const walker = new FileWalker({ type: QueryType.File, folderQueries: ROOT_FOLDER_QUERY, excludePattern: { '**/subfolder/anotherfolder': true } }); const cmd2 = walker.spawnFindCmd(TEST_ROOT_FOLDER); - walker.readStdout(cmd2, 'utf8', /*isRipgrep=*/false, (err2, stdout2) => { + walker.readStdout(cmd2, 'utf8', (err2, stdout2) => { assert.equal(err2, null); assert.notStrictEqual(stdout1!.split('\n').indexOf(file0), -1, stdout1); assert.strictEqual(stdout2!.split('\n').indexOf(file1), -1, stdout2); @@ -923,14 +918,14 @@ suite('FileWalker', () => { const walker = new FileWalker({ type: QueryType.File, folderQueries: ROOT_FOLDER_QUERY, excludePattern: { 'examples/something': true } }); const cmd1 = walker.spawnFindCmd(TEST_ROOT_FOLDER); - walker.readStdout(cmd1, 'utf8', /*isRipgrep=*/false, (err1, stdout1) => { + walker.readStdout(cmd1, 'utf8', (err1, stdout1) => { assert.equal(err1, null); assert.notStrictEqual(stdout1!.split('\n').indexOf(file0), -1, stdout1); assert.notStrictEqual(stdout1!.split('\n').indexOf(file1), -1, stdout1); const walker = new FileWalker({ type: QueryType.File, folderQueries: ROOT_FOLDER_QUERY, excludePattern: { 'examples/subfolder': true } }); const cmd2 = walker.spawnFindCmd(TEST_ROOT_FOLDER); - walker.readStdout(cmd2, 'utf8', /*isRipgrep=*/false, (err2, stdout2) => { + walker.readStdout(cmd2, 'utf8', (err2, stdout2) => { assert.equal(err2, null); assert.notStrictEqual(stdout1!.split('\n').indexOf(file0), -1, stdout1); assert.strictEqual(stdout2!.split('\n').indexOf(file1), -1, stdout2); @@ -967,7 +962,7 @@ suite('FileWalker', () => { } }); const cmd1 = walker.spawnFindCmd(TEST_ROOT_FOLDER); - walker.readStdout(cmd1, 'utf8', /*isRipgrep=*/false, (err1, stdout1) => { + walker.readStdout(cmd1, 'utf8', (err1, stdout1) => { assert.equal(err1, null); for (const fileIn of filesIn) { assert.notStrictEqual(stdout1!.split('\n').indexOf(fileIn), -1, stdout1); From d13e23bd299411454827cb89735091f7366288be Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 24 Jan 2019 08:53:26 +0100 Subject: [PATCH 052/274] debt - remove unused events --- src/vs/code/electron-main/window.ts | 1 + src/vs/code/electron-main/windows.ts | 18 +----------------- .../platform/windows/electron-main/windows.ts | 2 -- 3 files changed, 2 insertions(+), 19 deletions(-) diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index d72458428f7..2546f98ec5b 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -559,6 +559,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { } reload(configurationIn?: IWindowConfiguration, cli?: ParsedArgs): void { + // If config is not provided, copy our current one const configuration = configurationIn ? configurationIn : objects.mixin({}, this.currentConfig); diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 563bb5f2d8f..81efe601472 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -13,7 +13,7 @@ import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/ import { IStateService } from 'vs/platform/state/common/state'; import { CodeWindow, defaultWindowState } from 'vs/code/electron-main/window'; import { hasArgs, asArray } from 'vs/platform/environment/node/argv'; -import { ipcMain as ipc, screen, BrowserWindow, dialog, systemPreferences, app } from 'electron'; +import { ipcMain as ipc, screen, BrowserWindow, dialog, systemPreferences } from 'electron'; import { IPathWithLineAndColumn, parseLineAndColumnAware } from 'vs/code/node/paths'; import { ILifecycleService, UnloadReason, IWindowUnloadEvent, LifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -145,12 +145,6 @@ export class WindowsManager implements IWindowsMainService { private _onWindowLoad = new Emitter(); onWindowLoad: CommonEvent = this._onWindowLoad.event; - private _onActiveWindowChanged = new Emitter(); - onActiveWindowChanged: CommonEvent = this._onActiveWindowChanged.event; - - private _onWindowReload = new Emitter(); - onWindowReload: CommonEvent = this._onWindowReload.event; - private _onWindowsCountChanged = new Emitter(); onWindowsCountChanged: CommonEvent = this._onWindowsCountChanged.event; @@ -208,13 +202,6 @@ export class WindowsManager implements IWindowsMainService { private registerListeners(): void { - // React to windows focus changes - app.on('browser-window-focus', () => { - setTimeout(() => { - this._onActiveWindowChanged.fire(this.getLastActiveWindow()); - }); - }); - // React to workbench ready events from windows ipc.on('vscode:workbenchReady', (event: any, windowId: number) => { this.logService.trace('IPC#vscode-workbenchReady'); @@ -1471,9 +1458,6 @@ export class WindowsManager implements IWindowsMainService { this.lifecycleService.unload(win, UnloadReason.RELOAD).then(veto => { if (!veto) { win.reload(undefined, cli); - - // Emit - this._onWindowReload.fire(win.id); } }); } diff --git a/src/vs/platform/windows/electron-main/windows.ts b/src/vs/platform/windows/electron-main/windows.ts index 4edc86ab24f..839ba5b7b6f 100644 --- a/src/vs/platform/windows/electron-main/windows.ts +++ b/src/vs/platform/windows/electron-main/windows.ts @@ -88,10 +88,8 @@ export interface IWindowsMainService { // events readonly onWindowReady: Event; - readonly onActiveWindowChanged: Event; readonly onWindowsCountChanged: Event; readonly onWindowClose: Event; - readonly onWindowReload: Event; // methods ready(initialUserEnv: IProcessEnvironment): void; From 1b9fe77490918d613ce3e023db4d8e6f5b3a5c2e Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 24 Jan 2019 08:53:32 +0100 Subject: [PATCH 053/274] fix build --- src/vs/editor/browser/editorExtensions.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/browser/editorExtensions.ts b/src/vs/editor/browser/editorExtensions.ts index 15365b311a9..071893e858b 100644 --- a/src/vs/editor/browser/editorExtensions.ts +++ b/src/vs/editor/browser/editorExtensions.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IPosition } from 'vs/base/browser/ui/contextview/contextview'; +import { always } from 'vs/base/common/async'; import { illegalArgument } from 'vs/base/common/errors'; import { URI } from 'vs/base/common/uri'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; @@ -265,14 +266,14 @@ export function registerDefaultLanguageCommand(id: string, handler: (model: ITex } return accessor.get(ITextModelService).createModelReference(resource).then(reference => { - return new Promise((resolve, reject) => { + return always(new Promise((resolve, reject) => { try { let result = handler(reference.object.textEditorModel, Position.lift(position), args); resolve(result); } catch (err) { reject(err); } - }).finally(() => { + }), () => { reference.dispose(); }); }); From ad93f02b970bb2a1fac436ecfbbeaaf3ae4b67c2 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 24 Jan 2019 09:11:34 +0100 Subject: [PATCH 054/274] remove legacy search from strict null checks --- src/tsconfig.strictNullChecks.json | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index 81cb5ed54c5..716b43d9406 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -842,13 +842,6 @@ "./vs/workbench/services/search/common/searchHelpers.ts", "./vs/workbench/services/search/node/fileSearch.ts", "./vs/workbench/services/search/node/fileSearchManager.ts", - "./vs/workbench/services/search/node/legacy/rawLegacyTextSearchService.ts", - "./vs/workbench/services/search/node/legacy/search.ts", - "./vs/workbench/services/search/node/legacy/textSearch.ts", - "./vs/workbench/services/search/node/legacy/textSearchWorkerProvider.ts", - "./vs/workbench/services/search/node/legacy/worker/searchWorker.ts", - "./vs/workbench/services/search/node/legacy/worker/searchWorkerApp.ts", - "./vs/workbench/services/search/node/legacy/worker/searchWorkerIpc.ts", "./vs/workbench/services/search/node/rawSearchService.ts", "./vs/workbench/services/search/node/ripgrepFileSearch.ts", "./vs/workbench/services/search/node/ripgrepSearchProvider.ts", From 8f8e1fb2664c02a409ad77fb6dbeb0c9be84af72 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 24 Jan 2019 09:16:14 +0100 Subject: [PATCH 055/274] fix CancelablePromise.finally related to #67027 --- src/vs/base/common/async.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/common/async.ts b/src/vs/base/common/async.ts index e6c85e8319d..f040bb82d82 100644 --- a/src/vs/base/common/async.ts +++ b/src/vs/base/common/async.ts @@ -45,7 +45,7 @@ export function createCancelablePromise(callback: (token: CancellationToken) return this.then(undefined, reject); } finally(onfinally?: (() => void) | undefined | null): Promise { - return this.finally(onfinally); + return promise.finally(onfinally); } }; } From 1cbdd8286350405d2f35770668552bfe460bb3cf Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 24 Jan 2019 09:18:40 +0100 Subject: [PATCH 056/274] use promise.finally related to #67027 --- .../parts/scm/electron-browser/dirtydiffDecorator.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.ts b/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.ts index a2683e8d6a8..d0a303be842 100644 --- a/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.ts +++ b/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import 'vs/css!./media/dirtydiffDecorator'; -import { ThrottledDelayer, always, first } from 'vs/base/common/async'; +import { ThrottledDelayer, first } from 'vs/base/common/async'; import { IDisposable, dispose, toDisposable, Disposable, combinedDisposable } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; import * as ext from 'vs/workbench/common/contributions'; @@ -1101,7 +1101,7 @@ export class DirtyDiffModel { }); }); - return always(this._originalURIPromise!, () => { + return this._originalURIPromise.finally(() => { this._originalURIPromise = undefined; }); } From cee47d0f3ed06407dcf1710b5e2d77c48a4ef5ae Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 24 Jan 2019 09:31:49 +0100 Subject: [PATCH 057/274] adopt Promise.finally (for #67027) --- src/vs/platform/lifecycle/electron-main/lifecycleMain.ts | 3 +-- src/vs/workbench/browser/parts/editor/editorPart.ts | 3 +-- src/vs/workbench/services/history/electron-browser/history.ts | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/vs/platform/lifecycle/electron-main/lifecycleMain.ts b/src/vs/platform/lifecycle/electron-main/lifecycleMain.ts index a396d295560..28697f4c745 100644 --- a/src/vs/platform/lifecycle/electron-main/lifecycleMain.ts +++ b/src/vs/platform/lifecycle/electron-main/lifecycleMain.ts @@ -12,7 +12,6 @@ import { ICodeWindow } from 'vs/platform/windows/electron-main/windows'; import { handleVetos } from 'vs/platform/lifecycle/common/lifecycle'; import { isMacintosh, isWindows } from 'vs/base/common/platform'; import { Disposable } from 'vs/base/common/lifecycle'; -import { always } from 'vs/base/common/async'; export const ILifecycleService = createDecorator('lifecycleService'); @@ -202,7 +201,7 @@ export class LifecycleService extends Disposable implements ILifecycleService { const shutdownPromise = this.beginOnWillShutdown(); // Wait until shutdown is signaled to be complete - always(shutdownPromise, () => { + shutdownPromise.finally(() => { // Resolve pending quit promise now without veto this.resolvePendingQuitPromise(false /* no veto */); diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index 7a1fbaf6c46..d64cb6ecce4 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -23,7 +23,6 @@ import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; import { assign } from 'vs/base/common/objects'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { ISerializedEditorGroup, isSerializedEditorGroup } from 'vs/workbench/common/editor/editorGroup'; -import { always } from 'vs/base/common/async'; import { EditorDropTarget } from 'vs/workbench/browser/parts/editor/editorDropTarget'; import { localize } from 'vs/nls'; import { Color } from 'vs/base/common/color'; @@ -829,7 +828,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor } // Signal restored - always(Promise.all(this.groups.map(group => group.whenRestored)), () => this.whenRestoredResolve()); + Promise.all(this.groups.map(group => group.whenRestored)).finally(() => this.whenRestoredResolve()); // Update container this.updateContainer(); diff --git a/src/vs/workbench/services/history/electron-browser/history.ts b/src/vs/workbench/services/history/electron-browser/history.ts index ca31a8508d2..e1cae269b8b 100644 --- a/src/vs/workbench/services/history/electron-browser/history.ts +++ b/src/vs/workbench/services/history/electron-browser/history.ts @@ -30,7 +30,6 @@ import { EditorServiceImpl } from 'vs/workbench/browser/parts/editor/editor'; import { IPartService } from 'vs/workbench/services/part/common/partService'; import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { coalesce } from 'vs/base/common/arrays'; -import { always } from 'vs/base/common/async'; /** * Stores the selection & view state of an editor and allows to compare it to other selection states. @@ -408,7 +407,7 @@ export class HistoryService extends Disposable implements IHistoryService { private navigate(acrossEditors?: boolean): void { this.navigatingInStack = true; - always(this.doNavigate(this.stack[this.index], !acrossEditors), () => this.navigatingInStack = false); + this.doNavigate(this.stack[this.index], !acrossEditors).finally(() => this.navigatingInStack = false); } private doNavigate(location: IStackEntry, withSelection: boolean): Promise { From 8db14cea90274bffe110ac374297dc117a7982e8 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 24 Jan 2019 09:41:07 +0100 Subject: [PATCH 058/274] use always for cancelablepromise.finally --- src/vs/base/common/async.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/common/async.ts b/src/vs/base/common/async.ts index f040bb82d82..407570e9838 100644 --- a/src/vs/base/common/async.ts +++ b/src/vs/base/common/async.ts @@ -45,7 +45,7 @@ export function createCancelablePromise(callback: (token: CancellationToken) return this.then(undefined, reject); } finally(onfinally?: (() => void) | undefined | null): Promise { - return promise.finally(onfinally); + return always(promise, onfinally); } }; } From aba7b7c3bc50f8de9d2d500b6ece2aa8a19dd55b Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 24 Jan 2019 09:42:20 +0100 Subject: [PATCH 059/274] fix strict null checks --- src/vs/base/common/async.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/common/async.ts b/src/vs/base/common/async.ts index 407570e9838..79c7a1c8071 100644 --- a/src/vs/base/common/async.ts +++ b/src/vs/base/common/async.ts @@ -45,7 +45,7 @@ export function createCancelablePromise(callback: (token: CancellationToken) return this.then(undefined, reject); } finally(onfinally?: (() => void) | undefined | null): Promise { - return always(promise, onfinally); + return always(promise, onfinally || (() => { })); } }; } From 50854c0a705745bbf50982fab3c30fd6bd1f7b19 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 24 Jan 2019 09:55:47 +0100 Subject: [PATCH 060/274] remove more search legacy --- src/vs/workbench/buildfile.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/buildfile.js b/src/vs/workbench/buildfile.js index b9f60119de5..caa36aa632b 100644 --- a/src/vs/workbench/buildfile.js +++ b/src/vs/workbench/buildfile.js @@ -22,7 +22,6 @@ exports.collectModules = function () { createModuleDescription('vs/workbench/parts/debug/node/telemetryApp', []), createModuleDescription('vs/workbench/services/search/node/searchApp', []), - createModuleDescription('vs/workbench/services/search/node/legacy/worker/searchWorkerApp', []), createModuleDescription('vs/workbench/services/files/node/watcher/unix/watcherApp', []), createModuleDescription('vs/workbench/services/files/node/watcher/nsfw/watcherApp', []), From 4ce7061f9b6ac8c3907ff41292c82472dd3925ce Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 24 Jan 2019 09:59:23 +0100 Subject: [PATCH 061/274] :lipstick: --- src/vs/code/electron-main/windows.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 81efe601472..7eff695f79a 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -2006,7 +2006,6 @@ class WorkspacesManager { return this.doSaveAndOpenWorkspace(window, workspace, path); }); }); - } private isValidTargetWorkspacePath(window: ICodeWindow, path?: string): Promise { From f6e44e88a3ce63aeabaf7f7b11a0de7c38e09628 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 24 Jan 2019 10:09:23 +0100 Subject: [PATCH 062/274] fixes #66926 --- src/vs/base/browser/ui/tree/abstractTree.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 85da0ba4eb9..a5bcd071511 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -405,7 +405,7 @@ class TypeFilterController implements IDisposable { const onKeyDown = Event.chain(domEvent(this.view.getHTMLElement(), 'keydown')) .filter(e => !isInputElement(e.target as HTMLElement) || e.target === this.filterOnTypeDomNode) .map(e => new StandardKeyboardEvent(e)) - .filter(e => e.keyCode === KeyCode.Backspace || e.keyCode === KeyCode.Escape || isPrintableCharEvent(e)) + .filter(e => isPrintableCharEvent(e) || (this._pattern.length > 0 && (e.keyCode === KeyCode.Escape || e.keyCode === KeyCode.Backspace))) .forEach(e => { e.stopPropagation(); e.preventDefault(); }) .event; From c5bc694fb3e3a6bdf19bb730ee2ab5677488c6ea Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 24 Jan 2019 10:42:03 +0100 Subject: [PATCH 063/274] fixes #67001 --- src/vs/workbench/electron-browser/commands.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/electron-browser/commands.ts b/src/vs/workbench/electron-browser/commands.ts index fcc3b93ce06..8c61db77d49 100644 --- a/src/vs/workbench/electron-browser/commands.ts +++ b/src/vs/workbench/electron-browser/commands.ts @@ -233,7 +233,7 @@ export function registerCommands(): void { const focused = accessor.get(IListService).lastFocusedList; // List - if (focused instanceof List || focused instanceof PagedList) { + if (focused instanceof List || focused instanceof PagedList || focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) { const list = focused; // Focus up first From 7aa98647394e32bf2f01f7a44f395a4834ed685b Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 24 Jan 2019 10:38:09 +0100 Subject: [PATCH 064/274] register color for remote server --- src/vs/workbench/common/theme.ts | 5 +++++ .../parts/extensions/electron-browser/extensionsWidgets.ts | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/common/theme.ts b/src/vs/workbench/common/theme.ts index a5ffddb8e59..c5fc0beab31 100644 --- a/src/vs/workbench/common/theme.ts +++ b/src/vs/workbench/common/theme.ts @@ -298,6 +298,11 @@ export const STATUS_BAR_PROMINENT_ITEM_HOVER_BACKGROUND = registerColor('statusB hc: '#369432' }, nls.localize('statusBarProminentItemHoverBackground', "Status bar prominent items background color when hovering. Prominent items stand out from other status bar entries to indicate importance. Change mode `Toggle Tab Key Moves Focus` from command palette to see an example. The status bar is shown in the bottom of the window.")); +export const STATUS_BAR_HOST_NAME_BACKGROUND = registerColor('statusBarItem.hostBackground', { + dark: STATUS_BAR_PROMINENT_ITEM_BACKGROUND, + light: STATUS_BAR_PROMINENT_ITEM_BACKGROUND, + hc: STATUS_BAR_PROMINENT_ITEM_BACKGROUND +}, nls.localize('statusBarItemHostBackground', "Background color for the remote host name on the status bar.")); // < --- Activity Bar --- > diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsWidgets.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsWidgets.ts index 821ba4d90ae..e162af7cc05 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsWidgets.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsWidgets.ts @@ -13,7 +13,7 @@ import { IExtensionManagementServerService, IExtensionTipsService } from 'vs/pla import { ILabelService } from 'vs/platform/label/common/label'; import { extensionButtonProminentBackground, extensionButtonProminentForeground } from 'vs/workbench/parts/extensions/electron-browser/extensionsActions'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { STATUS_BAR_PROMINENT_ITEM_BACKGROUND } from 'vs/workbench/common/theme'; +import { STATUS_BAR_HOST_NAME_BACKGROUND } from 'vs/workbench/common/theme'; export abstract class ExtensionWidget extends Disposable implements IExtensionContainer { private _extension: IExtension; @@ -229,7 +229,7 @@ export class RemoteBadgeWidget extends ExtensionWidget { append(this.element, $('span.octicon.octicon-file-symlink-directory')); const applyBadgeStyle = () => { - const bgColor = this.themeService.getTheme().getColor(STATUS_BAR_PROMINENT_ITEM_BACKGROUND); + const bgColor = this.themeService.getTheme().getColor(STATUS_BAR_HOST_NAME_BACKGROUND); this.element.style.backgroundColor = bgColor ? bgColor.toString() : ''; }; applyBadgeStyle(); From 52004d297586e31b5b53afe8bcfc367e9e85ddf3 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 24 Jan 2019 10:55:38 +0100 Subject: [PATCH 065/274] use finally #67027 --- .../workbench/parts/files/electron-browser/fileActions.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/parts/files/electron-browser/fileActions.ts b/src/vs/workbench/parts/files/electron-browser/fileActions.ts index 05b535dbfc3..cce911a6104 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileActions.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileActions.ts @@ -7,7 +7,6 @@ import 'vs/css!./media/fileactions'; import * as nls from 'vs/nls'; import * as types from 'vs/base/common/types'; import { isWindows, isLinux } from 'vs/base/common/platform'; -import { always } from 'vs/base/common/async'; import * as paths from 'vs/base/common/paths'; import * as resources from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; @@ -979,11 +978,9 @@ export class CompareWithClipboardAction extends Action { const name = resources.basename(resource); const editorLabel = nls.localize('clipboardComparisonLabel', "Clipboard ↔ {0}", name); - const cleanUp = () => { + return this.editorService.openEditor({ leftResource: resource.with({ scheme: CompareWithClipboardAction.SCHEME }), rightResource: resource, label: editorLabel }).finally(() => { this.registrationDisposal = dispose(this.registrationDisposal); - }; - - return always(this.editorService.openEditor({ leftResource: resource.with({ scheme: CompareWithClipboardAction.SCHEME }), rightResource: resource, label: editorLabel }), cleanUp); + }); } return Promise.resolve(true); From 15b279c3d6fa88fd99c0cf62ed0327045b419959 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 24 Jan 2019 10:56:58 +0100 Subject: [PATCH 066/274] fixes #66863 --- src/vs/base/browser/ui/tree/abstractTree.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index a5bcd071511..b1203073fbc 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -179,6 +179,8 @@ interface ITreeRendererOptions { class TreeRenderer implements IListRenderer, ITreeListTemplateData> { + private static DefaultIndent = 8; + readonly templateId: string; private renderedElements = new Map>(); private renderedNodes = new Map, ITreeListTemplateData>(); @@ -201,7 +203,7 @@ class TreeRenderer implements IListRenderer { templateData.twistie.style.marginLeft = `${node.depth * this.indent}px`; @@ -221,7 +223,8 @@ class TreeRenderer implements IListRenderer Date: Thu, 24 Jan 2019 11:04:30 +0100 Subject: [PATCH 067/274] Adopt to use Promise#finally --- .../node/extensionManagementService.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index 4d8a6db6c3c..04c2c55614d 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -24,7 +24,7 @@ import { import { areSameExtensions, getGalleryExtensionId, groupByExtension, getMaliciousExtensionsSet, getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { localizeManifest } from '../common/extensionNls'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { Limiter, always, createCancelablePromise, CancelablePromise, Queue } from 'vs/base/common/async'; +import { Limiter, createCancelablePromise, CancelablePromise, Queue } from 'vs/base/common/async'; import { Event, Emitter } from 'vs/base/common/event'; import * as semver from 'semver'; import { URI } from 'vs/base/common/uri'; @@ -302,7 +302,7 @@ export class ExtensionManagementService extends Disposable implements IExtension this.downloadInstallableExtension(extension, operation) .then(installableExtension => this.installExtension(installableExtension, ExtensionType.User, cancellationToken) - .then(local => always(pfs.rimraf(installableExtension.zipPath), () => null).then(() => local))) + .then(local => pfs.rimraf(installableExtension.zipPath).finally(() => null).then(() => local))) .then(local => this.installDependenciesAndPackExtensions(local, existingExtension) .then(() => local, error => this.uninstall(local, true).then(() => Promise.reject(error), () => Promise.reject(error)))) .then( @@ -453,7 +453,7 @@ export class ExtensionManagementService extends Disposable implements IExtension () => this.logService.info('Renamed to', renamePath), e => { this.logService.info('Rename failed. Deleting from extracted location', extractPath); - return always(pfs.rimraf(extractPath), () => null).then(() => Promise.reject(e)); + return pfs.rimraf(extractPath).finally(() => null).then(() => Promise.reject(e)); })); } @@ -464,7 +464,7 @@ export class ExtensionManagementService extends Disposable implements IExtension () => extract(zipPath, extractPath, { sourcePath: 'extension', overwrite: true }, this.logService, token) .then( () => this.logService.info(`Extracted extension to ${extractPath}:`, identifier.id), - e => always(pfs.rimraf(extractPath), () => null) + e => pfs.rimraf(extractPath).finally(() => null) .then(() => Promise.reject(new ExtensionManagementError(e.message, e instanceof ExtractError && e.type ? e.type : INSTALL_ERROR_EXTRACTING)))), e => Promise.reject(new ExtensionManagementError(this.joinErrors(e).message, INSTALL_ERROR_DELETING))); } From 2de50be7ce2e3fc37512d9f897f499690dc8a23c Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 24 Jan 2019 11:10:43 +0100 Subject: [PATCH 068/274] fixes #65959 --- .../ipc/electron-main/ipc.electron-main.ts | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/vs/base/parts/ipc/electron-main/ipc.electron-main.ts b/src/vs/base/parts/ipc/electron-main/ipc.electron-main.ts index e90f23301a4..17ae7bb81b6 100644 --- a/src/vs/base/parts/ipc/electron-main/ipc.electron-main.ts +++ b/src/vs/base/parts/ipc/electron-main/ipc.electron-main.ts @@ -3,10 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event } from 'vs/base/common/event'; +import { Event, Emitter } from 'vs/base/common/event'; import { IPCServer, ClientConnectionEvent } from 'vs/base/parts/ipc/node/ipc'; import { Protocol } from 'vs/base/parts/ipc/node/ipc.electron'; import { ipcMain } from 'electron'; +import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; interface IIPCEvent { event: { sender: Electron.WebContents; }; @@ -21,12 +22,24 @@ function createScopedOnMessageEvent(senderId: number, eventName: string): Event< export class Server extends IPCServer { + private static Clients = new Map(); + private static getOnDidClientConnect(): Event { const onHello = Event.fromNodeEventEmitter(ipcMain, 'ipc:hello', ({ sender }) => sender); return Event.map(onHello, webContents => { - const onMessage = createScopedOnMessageEvent(webContents.id, 'ipc:message'); - const onDidClientDisconnect = Event.signal(createScopedOnMessageEvent(webContents.id, 'ipc:disconnect')); + const id = webContents.id; + const client = Server.Clients.get(id); + + if (client) { + client.dispose(); + } + + const onDidClientReconnect = new Emitter(); + Server.Clients.set(id, toDisposable(() => onDidClientReconnect.fire())); + + const onMessage = createScopedOnMessageEvent(id, 'ipc:message'); + const onDidClientDisconnect = Event.any(Event.signal(createScopedOnMessageEvent(id, 'ipc:disconnect')), onDidClientReconnect.event); const protocol = new Protocol(webContents, onMessage); return { protocol, onDidClientDisconnect }; From 9e50f698d606e5ecfa9b668f9276ae1af842a1ca Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 24 Jan 2019 11:10:41 +0100 Subject: [PATCH 069/274] reveal in explorer pass focus fixes #67024 --- src/vs/workbench/parts/files/electron-browser/fileCommands.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/files/electron-browser/fileCommands.ts b/src/vs/workbench/parts/files/electron-browser/fileCommands.ts index 2ecfe05bd08..0bd9b93fcc5 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileCommands.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileCommands.ts @@ -463,12 +463,13 @@ CommandsRegistry.registerCommand({ const explorerView = viewlet.getExplorerView(); if (explorerView) { explorerView.setExpanded(true); - explorerService.select(uri, true).then(undefined, onUnexpectedError); + explorerService.select(uri, true).then(() => explorerView.focus(), onUnexpectedError); } } else { const openEditorsView = viewlet.getOpenEditorsView(); if (openEditorsView) { openEditorsView.setExpanded(true); + openEditorsView.focus(); } } }); From 5796b6b98d162c42030c46b99e369fa5dfca6f05 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 24 Jan 2019 11:30:59 +0100 Subject: [PATCH 070/274] fix #66977 --- src/vs/base/browser/ui/list/listWidget.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 3620125f0aa..32b806ca6b1 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -723,11 +723,11 @@ export class DefaultStyleController implements IStyleController { } if (styles.listHoverBackground) { - content.push(`.monaco-list${suffix}:not(.drop-target) .monaco-list-row:hover { background-color: ${styles.listHoverBackground}; }`); + content.push(`.monaco-list${suffix}:not(.drop-target) .monaco-list-row:hover:not(.selected):not(.focused) { background-color: ${styles.listHoverBackground}; }`); } if (styles.listHoverForeground) { - content.push(`.monaco-list${suffix} .monaco-list-row:hover { color: ${styles.listHoverForeground}; }`); + content.push(`.monaco-list${suffix} .monaco-list-row:hover:not(.selected):not(.focused) { color: ${styles.listHoverForeground}; }`); } if (styles.listSelectionOutline) { From 26d688fa9f3a875f3d2b04732f729319f6b68a29 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 24 Jan 2019 11:34:11 +0100 Subject: [PATCH 071/274] fixes #66955 --- src/vs/base/browser/ui/tree/abstractTree.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index b1203073fbc..9b23289cb1f 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -408,7 +408,7 @@ class TypeFilterController implements IDisposable { const onKeyDown = Event.chain(domEvent(this.view.getHTMLElement(), 'keydown')) .filter(e => !isInputElement(e.target as HTMLElement) || e.target === this.filterOnTypeDomNode) .map(e => new StandardKeyboardEvent(e)) - .filter(e => isPrintableCharEvent(e) || (this._pattern.length > 0 && (e.keyCode === KeyCode.Escape || e.keyCode === KeyCode.Backspace))) + .filter(e => isPrintableCharEvent(e) || (this._pattern.length > 0 && (e.keyCode === KeyCode.Escape || e.keyCode === KeyCode.Backspace) && !e.altKey && !e.ctrlKey && !e.metaKey)) .forEach(e => { e.stopPropagation(); e.preventDefault(); }) .event; From 389e4283c0b3839aa3717346d745cd85cfd8efa3 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 24 Jan 2019 12:21:21 +0100 Subject: [PATCH 072/274] fixes #67041 --- .../electron-browser/views/openEditorsView.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts b/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts index 9f12476d434..d0ad0774f01 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts @@ -146,8 +146,11 @@ export class OpenEditorsView extends ViewletPanel { break; } case GroupChangeKind.EDITOR_OPEN: { - this.list.splice(index, 0, [new OpenEditor(e.editor, group)]); - setTimeout(() => this.updateSize(), this.structuralRefreshDelay); + setTimeout(() => { + this.list.splice(index, 0, [new OpenEditor(e.editor, group)]); + this.updateSize(); + this.focusActiveEditor(); + }, this.structuralRefreshDelay); break; } case GroupChangeKind.EDITOR_CLOSE: { @@ -395,9 +398,11 @@ export class OpenEditorsView extends ViewletPanel { private focusActiveEditor(): void { if (this.list.length && this.editorGroupService.activeGroup) { const index = this.getIndex(this.editorGroupService.activeGroup, this.editorGroupService.activeGroup.activeEditor); - this.list.setFocus([index]); - this.list.setSelection([index]); - this.list.reveal(index); + if (index < this.list.length) { + this.list.setFocus([index]); + this.list.setSelection([index]); + this.list.reveal(index); + } } else { this.list.setFocus([]); this.list.setSelection([]); From d351f7e23069f129dc9520b893ec2878ffa21dcb Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 24 Jan 2019 12:23:40 +0100 Subject: [PATCH 073/274] Fix #67003 --- src/vs/code/node/cliProcessMain.ts | 2 +- .../common/extensionManagement.ts | 4 +- .../node/extensionGalleryService.ts | 97 ++++++------------- .../node/extensionManagementService.ts | 2 +- .../platform/extensions/common/extensions.ts | 7 ++ .../node/extensionsWorkbenchService.ts | 4 +- .../inactiveExtensionUrlHandler.ts | 2 +- 7 files changed, 46 insertions(+), 72 deletions(-) diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index 50b202a6a64..17624961e5f 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -136,7 +136,7 @@ export class Main { const [id, version] = getIdAndVersion(extension); return this.extensionManagementService.getInstalled(ExtensionType.User) - .then(installed => this.extensionGalleryService.getExtension({ id }, version) + .then(installed => this.extensionGalleryService.getCompatibleExtension({ id }, version) .then(null, err => { if (err.responseText) { try { diff --git a/src/vs/platform/extensionManagement/common/extensionManagement.ts b/src/vs/platform/extensionManagement/common/extensionManagement.ts index 5607fad1477..49b9378bb91 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagement.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagement.ts @@ -156,11 +156,11 @@ export interface IExtensionGalleryService { getManifest(extension: IGalleryExtension, token: CancellationToken): Promise; getChangelog(extension: IGalleryExtension, token: CancellationToken): Promise; getCoreTranslation(extension: IGalleryExtension, languageId: string): Promise; - loadCompatibleVersion(extension: IGalleryExtension, fromVersion?: string): Promise; getAllVersions(extension: IGalleryExtension, compatible: boolean): Promise; loadAllDependencies(dependencies: IExtensionIdentifier[], token: CancellationToken): Promise; getExtensionsReport(): Promise; - getExtension(id: IExtensionIdentifier, version?: string): Promise; + getCompatibleExtension(extension: IGalleryExtension): Promise; + getCompatibleExtension(id: IExtensionIdentifier, version?: string): Promise; } export interface InstallExtensionEvent { diff --git a/src/vs/platform/extensionManagement/node/extensionGalleryService.ts b/src/vs/platform/extensionManagement/node/extensionGalleryService.ts index ef888757750..a72c577218f 100644 --- a/src/vs/platform/extensionManagement/node/extensionGalleryService.ts +++ b/src/vs/platform/extensionManagement/node/extensionGalleryService.ts @@ -7,7 +7,7 @@ import { tmpdir } from 'os'; import * as path from 'path'; import { distinct } from 'vs/base/common/arrays'; import { getErrorMessage, isPromiseCanceledError, canceled } from 'vs/base/common/errors'; -import { StatisticType, IGalleryExtension, IExtensionGalleryService, IGalleryExtensionAsset, IQueryOptions, SortBy, SortOrder, IExtensionIdentifier, IReportedExtension, InstallOperation, ITranslation, IGalleryExtensionVersion, IGalleryExtensionAssets } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { StatisticType, IGalleryExtension, IExtensionGalleryService, IGalleryExtensionAsset, IQueryOptions, SortBy, SortOrder, IExtensionIdentifier, IReportedExtension, InstallOperation, ITranslation, IGalleryExtensionVersion, IGalleryExtensionAssets, isIExtensionIdentifier } from 'vs/platform/extensionManagement/common/extensionManagement'; import { getGalleryExtensionId, getGalleryExtensionTelemetryData, adoptToGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { assign, getOrDefault } from 'vs/base/common/objects'; import { IRequestService } from 'vs/platform/request/node/request'; @@ -351,12 +351,18 @@ export class ExtensionGalleryService implements IExtensionGalleryService { return !!this.extensionsGalleryUrl; } - getExtension({ id, uuid }: IExtensionIdentifier, version?: string): Promise { + getCompatibleExtension(arg1: IExtensionIdentifier | IGalleryExtension, version?: string): Promise { + const extension: IGalleryExtension | null = isIExtensionIdentifier(arg1) ? null : arg1; + if (extension && extension.properties.engine && isEngineValid(extension.properties.engine)) { + return Promise.resolve(extension); + } + const { id, uuid } = extension ? extension.identifier : arg1; let query = new Query() .withFlags(Flags.IncludeAssetUri, Flags.IncludeStatistics, Flags.IncludeFiles, Flags.IncludeVersionProperties, Flags.ExcludeNonValidated) .withPage(1, 1) .withFilter(FilterType.Target, 'Microsoft.VisualStudio.Code') - .withFilter(FilterType.ExcludeWithFlags, flagsToString(Flags.Unpublished)); + .withFilter(FilterType.ExcludeWithFlags, flagsToString(Flags.Unpublished)) + .withAssetTypes(AssetType.Manifest, AssetType.VSIX); if (uuid) { query = query.withFilter(FilterType.ExtensionId, uuid); @@ -364,16 +370,30 @@ export class ExtensionGalleryService implements IExtensionGalleryService { query = query.withFilter(FilterType.ExtensionName, id); } - return this.queryGallery(query, CancellationToken.None).then(({ galleryExtensions }) => { - if (galleryExtensions.length) { - const galleryExtension = galleryExtensions[0]; - const versionAsset = version ? galleryExtension.versions.filter(v => v.version === version)[0] : galleryExtension.versions[0]; - if (versionAsset) { - return toExtension(galleryExtension, versionAsset, 0, query); + return this.queryGallery(query, CancellationToken.None) + .then(({ galleryExtensions }) => { + const [rawExtension] = galleryExtensions; + if (!rawExtension || !rawExtension.versions.length) { + return null; } - } - return null; - }); + if (version) { + const versionAsset = rawExtension.versions.filter(v => v.version === version)[0]; + if (versionAsset) { + const extension = toExtension(rawExtension, versionAsset, 0, query); + if (extension.properties.engine && isEngineValid(extension.properties.engine)) { + return extension; + } + } + return null; + } + return this.getLastValidExtensionVersion(rawExtension, rawExtension.versions) + .then(rawVersion => { + if (rawVersion) { + return toExtension(rawExtension, rawVersion, 0, query); + } + return null; + }); + }); } query(options: IQueryOptions = {}): Promise> { @@ -604,59 +624,6 @@ export class ExtensionGalleryService implements IExtensionGalleryService { }); } - loadCompatibleVersion(extension: IGalleryExtension, fromVersion: string = extension.version): Promise { - if (extension.version === fromVersion && extension.properties.engine && isEngineValid(extension.properties.engine)) { - return Promise.resolve(extension); - } - const query = new Query() - .withFlags(Flags.IncludeVersions, Flags.IncludeFiles, Flags.IncludeVersionProperties, Flags.ExcludeNonValidated) - .withPage(1, 1) - .withFilter(FilterType.Target, 'Microsoft.VisualStudio.Code') - .withFilter(FilterType.ExcludeWithFlags, flagsToString(Flags.Unpublished)) - .withAssetTypes(AssetType.Manifest, AssetType.VSIX) - .withFilter(FilterType.ExtensionId, extension.identifier.uuid); - - return this.queryGallery(query, CancellationToken.None) - .then(({ galleryExtensions }) => { - const [rawExtension] = galleryExtensions; - - if (!rawExtension) { - return null; - } - - const versions: IRawGalleryExtensionVersion[] = this.getVersionsFrom(rawExtension.versions, fromVersion); - if (!versions.length) { - return null; - } - - return this.getLastValidExtensionVersion(rawExtension, versions) - .then(rawVersion => { - if (rawVersion) { - return toExtension(rawExtension, rawVersion, 0, query); - } - return null; - }); - }); - } - - private getVersionsFrom(versions: IRawGalleryExtensionVersion[], version: string): IRawGalleryExtensionVersion[] { - if (versions[0].version === version) { - return versions; - } - const result: IRawGalleryExtensionVersion[] = []; - let currentVersion: IRawGalleryExtensionVersion | null = null; - for (const v of versions) { - if (!currentVersion) { - if (v.version === version) { - currentVersion = v; - } - } - if (currentVersion) { - result.push(v); - } - } - return result; - } private loadDependencies(extensionNames: string[], token: CancellationToken): Promise { if (!extensionNames || extensionNames.length === 0) { diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index 04c2c55614d..48f35949f1d 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -333,7 +333,7 @@ export class ExtensionManagementService extends Disposable implements IExtension return Promise.reject(new ExtensionManagementError(nls.localize('malicious extension', "Can't install extension since it was reported to be problematic."), INSTALL_ERROR_MALICIOUS)); } - const compatibleExtension = await this.galleryService.loadCompatibleVersion(extension); + const compatibleExtension = await this.galleryService.getCompatibleExtension(extension); if (!compatibleExtension) { return Promise.reject(new ExtensionManagementError(nls.localize('notFoundCompatibleDependency', "Unable to install because, the extension '{0}' compatible with current version '{1}' of VS Code is not found.", extension.identifier.id, pkg.version), INSTALL_ERROR_INCOMPATIBLE)); diff --git a/src/vs/platform/extensions/common/extensions.ts b/src/vs/platform/extensions/common/extensions.ts index ec9209e929f..e00d3ed9d5f 100644 --- a/src/vs/platform/extensions/common/extensions.ts +++ b/src/vs/platform/extensions/common/extensions.ts @@ -129,6 +129,13 @@ export class ExtensionIdentifierWithVersion { } } +export function isIExtensionIdentifier(thing: any): thing is IExtensionIdentifier { + return thing + && typeof thing === 'object' + && typeof thing.id === 'string' + && (!thing.uuid || typeof thing.uuid === 'string'); +} + export interface IExtensionIdentifier { id: string; uuid?: string; diff --git a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts index 4aee8a6eac0..58ed50763e3 100644 --- a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts +++ b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts @@ -507,7 +507,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, // Loading the compatible version only there is an engine property // Otherwise falling back to old way so that we will not make many roundtrips if (gallery.properties.engine) { - this.galleryService.loadCompatibleVersion(gallery) + this.galleryService.getCompatibleExtension(gallery) .then(compatible => compatible ? this.syncLocalWithGalleryExtension(result!, compatible) : null); } else { this.syncLocalWithGalleryExtension(result, gallery); @@ -679,7 +679,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, return Promise.reject(new Error('Missing gallery')); } - return this.galleryService.getExtension(extension.gallery.identifier, version) + return this.galleryService.getCompatibleExtension(extension.gallery.identifier, version) .then(gallery => { if (!gallery) { return undefined; diff --git a/src/vs/workbench/services/extensions/electron-browser/inactiveExtensionUrlHandler.ts b/src/vs/workbench/services/extensions/electron-browser/inactiveExtensionUrlHandler.ts index 0b63147da65..f8afb7bc4f5 100644 --- a/src/vs/workbench/services/extensions/electron-browser/inactiveExtensionUrlHandler.ts +++ b/src/vs/workbench/services/extensions/electron-browser/inactiveExtensionUrlHandler.ts @@ -190,7 +190,7 @@ export class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { // Extension is not installed else { - const galleryExtension = await this.galleryService.getExtension(extensionIdentifier); + const galleryExtension = await this.galleryService.getCompatibleExtension(extensionIdentifier); if (!galleryExtension) { return; From 48493c21ee509e5536999eb4ce3a1a20b8756d89 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 24 Jan 2019 12:39:40 +0100 Subject: [PATCH 074/274] Esc should clear what is cut in the explorer fixes #66993 --- src/vs/workbench/parts/files/common/files.ts | 2 ++ .../electron-browser/fileActions.contribution.ts | 14 +++++++++++++- .../files/electron-browser/views/explorerView.ts | 7 ++++++- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/parts/files/common/files.ts b/src/vs/workbench/parts/files/common/files.ts index a9e24086881..10877a56430 100644 --- a/src/vs/workbench/parts/files/common/files.ts +++ b/src/vs/workbench/parts/files/common/files.ts @@ -73,12 +73,14 @@ const explorerViewletFocusId = 'explorerViewletFocus'; const explorerResourceIsFolderId = 'explorerResourceIsFolder'; const explorerResourceReadonly = 'explorerResourceReadonly'; const explorerResourceIsRootId = 'explorerResourceIsRoot'; +const explorerResourceCutId = 'explorerResourceCut'; export const ExplorerViewletVisibleContext = new RawContextKey(explorerViewletVisibleId, true); export const ExplorerFolderContext = new RawContextKey(explorerResourceIsFolderId, false); export const ExplorerResourceReadonlyContext = new RawContextKey(explorerResourceReadonly, false); export const ExplorerResourceNotReadonlyContext = ExplorerResourceReadonlyContext.toNegated(); export const ExplorerRootContext = new RawContextKey(explorerResourceIsRootId, false); +export const ExplorerResourceCut = new RawContextKey(explorerResourceCutId, false); export const FilesExplorerFocusedContext = new RawContextKey(filesExplorerFocusId, true); export const OpenEditorsVisibleContext = new RawContextKey(openEditorsVisibleId, false); export const OpenEditorsFocusedContext = new RawContextKey(openEditorsFocusId, true); diff --git a/src/vs/workbench/parts/files/electron-browser/fileActions.contribution.ts b/src/vs/workbench/parts/files/electron-browser/fileActions.contribution.ts index 13a91fe33f6..a47d263259c 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileActions.contribution.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileActions.contribution.ts @@ -15,7 +15,7 @@ import { CommandsRegistry, ICommandHandler } from 'vs/platform/commands/common/c import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { isWindows, isMacintosh } from 'vs/base/common/platform'; -import { FilesExplorerFocusCondition, ExplorerRootContext, ExplorerFolderContext, ExplorerResourceNotReadonlyContext } from 'vs/workbench/parts/files/common/files'; +import { FilesExplorerFocusCondition, ExplorerRootContext, ExplorerFolderContext, ExplorerResourceNotReadonlyContext, ExplorerResourceCut, IExplorerService } from 'vs/workbench/parts/files/common/files'; import { ADD_ROOT_FOLDER_COMMAND_ID, ADD_ROOT_FOLDER_LABEL } from 'vs/workbench/browser/actions/workspaceCommands'; import { CLOSE_SAVED_EDITORS_COMMAND_ID, CLOSE_EDITORS_IN_GROUP_COMMAND_ID, CLOSE_EDITOR_COMMAND_ID, CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID } from 'vs/workbench/browser/parts/editor/editorCommands'; import { OPEN_FOLDER_SETTINGS_COMMAND, OPEN_FOLDER_SETTINGS_LABEL } from 'vs/workbench/parts/preferences/browser/preferencesActions'; @@ -24,6 +24,7 @@ import { ResourceContextKey } from 'vs/workbench/common/resources'; import { WorkbenchListDoubleSelection } from 'vs/platform/list/browser/listService'; import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; // Contribute Global Actions const category = nls.localize('filesCategory', "File"); @@ -120,6 +121,17 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ handler: pasteFileHandler }); +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'filesExplorer.cancelCut', + weight: KeybindingWeight.WorkbenchContrib + explorerCommandsWeightBonus, + when: ContextKeyExpr.and(FilesExplorerFocusCondition, ExplorerResourceCut), + primary: KeyCode.Escape, + handler: (accessor: ServicesAccessor) => { + const explorerService = accessor.get(IExplorerService); + explorerService.setToCopy([], true); + } +}); + const copyPathCommand = { id: COPY_PATH_COMMAND_ID, title: nls.localize('copyPath', "Copy Path") diff --git a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts index 47eeceef1ad..cdfa5fa056e 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -9,7 +9,7 @@ import * as perf from 'vs/base/common/performance'; import { sequence } from 'vs/base/common/async'; import { Action, IAction } from 'vs/base/common/actions'; import { memoize } from 'vs/base/common/decorators'; -import { IFilesConfiguration, ExplorerFolderContext, FilesExplorerFocusedContext, ExplorerFocusedContext, ExplorerRootContext, ExplorerResourceReadonlyContext, IExplorerService } from 'vs/workbench/parts/files/common/files'; +import { IFilesConfiguration, ExplorerFolderContext, FilesExplorerFocusedContext, ExplorerFocusedContext, ExplorerRootContext, ExplorerResourceReadonlyContext, IExplorerService, ExplorerResourceCut } from 'vs/workbench/parts/files/common/files'; import { NewFolderAction, NewFileAction, FileCopiedContext, RefreshExplorerView } from 'vs/workbench/parts/files/electron-browser/fileActions'; import { toResource } from 'vs/workbench/common/editor'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; @@ -122,6 +122,10 @@ export class ExplorerView extends ViewletPanel { return FileCopiedContext.bindTo(this.contextKeyService); } + @memoize private get resourceCutContextKey(): IContextKey { + return ExplorerResourceCut.bindTo(this.contextKeyService); + } + // Split view methods protected renderHeader(container: HTMLElement): void { @@ -484,6 +488,7 @@ export class ExplorerView extends ViewletPanel { private onCopyItems(stats: ExplorerItem[], cut: boolean, previousCut: ExplorerItem[]): void { this.fileCopiedContextKey.set(stats.length > 0); + this.resourceCutContextKey.set(cut && stats.length > 0); if (previousCut) { previousCut.forEach(item => this.tree.refresh(item)); } From f82c0f12aab27c44bd7e4c04db3c5be31f81fe28 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 24 Jan 2019 12:41:18 +0100 Subject: [PATCH 075/274] Exand custom views by default --- src/vs/workbench/api/browser/viewsExtensionPoint.ts | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts index 6ab333bf1e4..6ad174d40e8 100644 --- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts @@ -382,7 +382,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution { ctor: CustomTreeViewPanel, when: ContextKeyExpr.deserialize(item.when), canToggleVisibility: true, - collapsed: this.showCollapsed(container), + collapsed: false, treeView: this.instantiationService.createInstance(CustomTreeView, item.id, container), order: ExtensionIdentifier.equals(extension.description.identifier, container.extensionId) ? index + 1 : undefined, extensionId: extension.description.identifier, @@ -443,16 +443,6 @@ class ViewsExtensionHandler implements IWorkbenchContribution { default: return this.viewContainersRegistry.get(`workbench.view.extension.${value}`); } } - - private showCollapsed(container: ViewContainer): boolean { - switch (container.id) { - case EXPLORER: - case SCM: - case DEBUG: - return true; - } - return false; - } } const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); From 5e4219bfe012680c5672d99b793e77293fc94e68 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 24 Jan 2019 14:36:22 +0100 Subject: [PATCH 076/274] :lipstick: --- .../dialogs/electron-browser/dialogService.ts | 40 ++++++++++++------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts b/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts index 89bb593da04..f34d75f6120 100644 --- a/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts +++ b/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts @@ -163,7 +163,8 @@ export class FileDialogService implements IFileDialogService { @IEnvironmentService private readonly environmentService: IEnvironmentService ) { } - public defaultFilePath(schemeFilter: string): URI | undefined { + defaultFilePath(schemeFilter: string): URI | undefined { + // Check for last active file first... let candidate = this.historyService.getLastActiveFile(schemeFilter); @@ -175,7 +176,8 @@ export class FileDialogService implements IFileDialogService { return candidate && resources.dirname(candidate) || undefined; } - public defaultFolderPath(schemeFilter: string): URI | undefined { + defaultFolderPath(schemeFilter: string): URI | undefined { + // Check for last active file root first... let candidate = this.historyService.getLastActiveWorkspaceRoot(schemeFilter); @@ -187,7 +189,7 @@ export class FileDialogService implements IFileDialogService { return candidate && resources.dirname(candidate) || undefined; } - public defaultWorkspacePath(schemeFilter: string): URI | undefined { + defaultWorkspacePath(schemeFilter: string): URI | undefined { // Check for current workspace config file first... if (schemeFilter === Schemas.file && this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) { @@ -211,36 +213,39 @@ export class FileDialogService implements IFileDialogService { }; } - public pickFileFolderAndOpen(options: IPickAndOpenOptions): Promise { - let defaultUri = options.defaultUri; + pickFileFolderAndOpen(options: IPickAndOpenOptions): Promise { + const defaultUri = options.defaultUri; if (!defaultUri) { options.defaultUri = this.defaultFilePath(Schemas.file); } - return this.windowService.pickFileFolderAndOpen(this.toNativeOpenDialogOptions(options)); + return this.windowService.pickFileFolderAndOpen(this.toNativeOpenDialogOptions(options)); } - public pickFileAndOpen(options: IPickAndOpenOptions): Promise { - let defaultUri = options.defaultUri; + pickFileAndOpen(options: IPickAndOpenOptions): Promise { + const defaultUri = options.defaultUri; if (!defaultUri) { options.defaultUri = this.defaultFilePath(Schemas.file); } + return this.windowService.pickFileAndOpen(this.toNativeOpenDialogOptions(options)); } - public pickFolderAndOpen(options: IPickAndOpenOptions): Promise { - let defaultUri = options.defaultUri; + pickFolderAndOpen(options: IPickAndOpenOptions): Promise { + const defaultUri = options.defaultUri; if (!defaultUri) { options.defaultUri = this.defaultFolderPath(Schemas.file); } + return this.windowService.pickFolderAndOpen(this.toNativeOpenDialogOptions(options)); } - public pickWorkspaceAndOpen(options: IPickAndOpenOptions): Promise { - let defaultUri = options.defaultUri; + pickWorkspaceAndOpen(options: IPickAndOpenOptions): Promise { + const defaultUri = options.defaultUri; if (!defaultUri) { options.defaultUri = this.defaultWorkspacePath(Schemas.file); } + return this.windowService.pickWorkspaceAndOpen(this.toNativeOpenDialogOptions(options)); } @@ -253,20 +258,22 @@ export class FileDialogService implements IFileDialogService { }; } - public showSaveDialog(options: ISaveDialogOptions): Promise { + showSaveDialog(options: ISaveDialogOptions): Promise { const defaultUri = options.defaultUri; if (defaultUri && defaultUri.scheme !== Schemas.file) { return Promise.reject(new Error('Not supported - Save-dialogs can only be opened on `file`-uris.')); } + return this.windowService.showSaveDialog(this.toNativeSaveDialogOptions(options)).then(result => { if (result) { return URI.file(result); } + return undefined; }); } - public showOpenDialog(options: IOpenDialogOptions): Promise { + showOpenDialog(options: IOpenDialogOptions): Promise { const defaultUri = options.defaultUri; if (defaultUri && defaultUri.scheme !== Schemas.file) { return Promise.reject(new Error('Not supported - Open-dialogs can only be opened on `file`-uris.')); @@ -279,16 +286,21 @@ export class FileDialogService implements IFileDialogService { filters: options.filters, properties: [] }; + newOptions.properties!.push('createDirectory'); + if (options.canSelectFiles) { newOptions.properties!.push('openFile'); } + if (options.canSelectFolders) { newOptions.properties!.push('openDirectory'); } + if (options.canSelectMany) { newOptions.properties!.push('multiSelections'); } + return this.windowService.showOpenDialog(newOptions).then(result => result ? result.map(URI.file) : undefined); } } From 966262038d8848773ec87a0db4ee814c1f3a8cc6 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 24 Jan 2019 14:44:50 +0100 Subject: [PATCH 077/274] debt - remove monaco-shell class from places that now live in monaco-workbench --- .../parts/editor/media/editorstatus.css | 20 ++-- .../feedback/electron-browser/feedback.ts | 4 +- .../electron-browser/media/feedback.css | 101 +++++++++--------- 3 files changed, 63 insertions(+), 62 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/media/editorstatus.css b/src/vs/workbench/browser/parts/editor/media/editorstatus.css index 204d01c7f16..6770963641e 100644 --- a/src/vs/workbench/browser/parts/editor/media/editorstatus.css +++ b/src/vs/workbench/browser/parts/editor/media/editorstatus.css @@ -22,7 +22,7 @@ cursor: default !important; } -.monaco-shell .screen-reader-detected-explanation { +.monaco-workbench .screen-reader-detected-explanation { width: 420px; top: 30px; right: 6px; @@ -30,7 +30,7 @@ cursor: default; } -.monaco-shell .screen-reader-detected-explanation .cancel { +.monaco-workbench .screen-reader-detected-explanation .cancel { position: absolute; top: 0; right: 0; @@ -41,27 +41,27 @@ cursor: pointer; } -.monaco-shell .screen-reader-detected-explanation h2 { +.monaco-workbench .screen-reader-detected-explanation h2 { margin: 0; padding: 0; font-weight: 400; font-size: 1.8em; } -.monaco-shell .screen-reader-detected-explanation p { +.monaco-workbench .screen-reader-detected-explanation p { font-size: 1.2em; } -.monaco-shell .screen-reader-detected-explanation hr { +.monaco-workbench .screen-reader-detected-explanation hr { border: 0; height: 2px; } -.monaco-shell .screen-reader-detected-explanation .buttons { +.monaco-workbench .screen-reader-detected-explanation .buttons { display: flex; } -.monaco-shell .screen-reader-detected-explanation .buttons a { +.monaco-workbench .screen-reader-detected-explanation .buttons a { font-size: 13px; padding-left: 12px; padding-right: 12px; @@ -69,11 +69,11 @@ max-width: fit-content; } -.monaco-shell.vs .screen-reader-detected-explanation .cancel { +.vs .monaco-workbench .screen-reader-detected-explanation .cancel { background: url('close-statusview.svg') center center no-repeat; } -.monaco-shell.vs-dark .screen-reader-detected-explanation .cancel, -.monaco-shell.hc-black .screen-reader-detected-explanation .cancel { +.vs-dark .monaco-workbench .screen-reader-detected-explanation .cancel, +.hc-black .monaco-workbench .screen-reader-detected-explanation .cancel { background: url('close-statusview-inverse.svg') center center no-repeat; } \ No newline at end of file diff --git a/src/vs/workbench/parts/feedback/electron-browser/feedback.ts b/src/vs/workbench/parts/feedback/electron-browser/feedback.ts index 405ea26a556..5d3f0f1f67b 100644 --- a/src/vs/workbench/parts/feedback/electron-browser/feedback.ts +++ b/src/vs/workbench/parts/feedback/electron-browser/feedback.ts @@ -429,12 +429,12 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { // Sentiment Buttons const inputActiveOptionBorderColor = theme.getColor(inputActiveOptionBorder); if (inputActiveOptionBorderColor) { - collector.addRule(`.monaco-shell .feedback-form .sentiment.checked { border: 1px solid ${inputActiveOptionBorderColor}; }`); + collector.addRule(`.monaco-workbench .feedback-form .sentiment.checked { border: 1px solid ${inputActiveOptionBorderColor}; }`); } // Links const linkColor = theme.getColor(buttonBackground) || theme.getColor(contrastBorder); if (linkColor) { - collector.addRule(`.monaco-shell .feedback-form .content .channels a { color: ${linkColor}; }`); + collector.addRule(`.monaco-workbench .feedback-form .content .channels a { color: ${linkColor}; }`); } }); \ No newline at end of file diff --git a/src/vs/workbench/parts/feedback/electron-browser/media/feedback.css b/src/vs/workbench/parts/feedback/electron-browser/media/feedback.css index 3c69f00d995..041ae4a8776 100644 --- a/src/vs/workbench/parts/feedback/electron-browser/media/feedback.css +++ b/src/vs/workbench/parts/feedback/electron-browser/media/feedback.css @@ -2,7 +2,8 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-shell .feedback-form { + +.monaco-workbench .feedback-form { width: 420px; top: 30px; right: 6px; @@ -10,53 +11,53 @@ cursor: default; } -.monaco-shell .feedback-form h2 { +.monaco-workbench .feedback-form h2 { margin: 0; padding: 0; font-weight: normal; font-size: 1.8em; } -.monaco-shell .feedback-form h3 { +.monaco-workbench .feedback-form h3 { margin: 1em 0 0; padding: 0; font-weight: normal; font-size: 1.2em; } -.monaco-shell .feedback-form .content { +.monaco-workbench .feedback-form .content { font-size: 1.2em; } -.monaco-shell .feedback-form .content > div { +.monaco-workbench .feedback-form .content > div { display: inline-block; vertical-align: top; margin-top: 20px; } -.monaco-shell .feedback-form .content .contactus { +.monaco-workbench .feedback-form .content .contactus { padding: 10px; float: right; } -.monaco-shell .feedback-form .content .channels { +.monaco-workbench .feedback-form .content .channels { margin-top: 5px; font-size: 0.9em; } -.monaco-shell .char-counter { +.monaco-workbench .char-counter { padding-left: 3px; } -.monaco-shell .feedback-form .content .channels a { +.monaco-workbench .feedback-form .content .channels a { padding: 2px 0; } -.monaco-shell .feedback-form .content .channels a:hover { +.monaco-workbench .feedback-form .content .channels a:hover { text-decoration: underline; } -.monaco-shell .feedback-form .feedback-alias, .monaco-shell .feedback-form .feedback-description { +.monaco-workbench .feedback-form .feedback-alias, .monaco-workbench .feedback-form .feedback-description { resize: none; font-size: 1.1em; margin: 6px 0 0; @@ -65,25 +66,25 @@ box-sizing: border-box; } -.monaco-shell .feedback-form .feedback-empty { +.monaco-workbench .feedback-form .feedback-empty { margin-top: .2em; } -.monaco-shell .feedback-form .feedback-alias-checkbox { +.monaco-workbench .feedback-form .feedback-alias-checkbox { display: inline-block; vertical-align: text-top; margin-left: 0; } -.monaco-shell .feedback-form .feedback-alias { +.monaco-workbench .feedback-form .feedback-alias { height: 26px; } -.monaco-shell .feedback-form .feedback-alias:disabled { +.monaco-workbench .feedback-form .feedback-alias:disabled { opacity: 0.6; } -.monaco-shell .feedback-form .cancel { +.monaco-workbench .feedback-form .cancel { position: absolute; top: 0; right: 0; @@ -94,12 +95,12 @@ cursor: pointer; } -.monaco-shell .feedback-form .form-buttons { +.monaco-workbench .feedback-form .form-buttons { margin-top: 18px; text-align: right; } -.monaco-shell .feedback-form .sentiment { +.monaco-workbench .feedback-form .sentiment { height: 32px; width: 32px; display: inline-block; @@ -108,45 +109,45 @@ box-sizing: border-box; } -.monaco-shell .feedback-form .sentiment:hover { +.monaco-workbench .feedback-form .sentiment:hover { background-color: #eaeaea; } /* Statusbar */ -.monaco-shell .statusbar-item > .monaco-dropdown.send-feedback { +.monaco-workbench .statusbar-item > .monaco-dropdown.send-feedback { display: inline-block; } -.monaco-shell .statusbar-item > .monaco-dropdown.send-feedback > .dropdown-label.send-feedback { +.monaco-workbench .statusbar-item > .monaco-dropdown.send-feedback > .dropdown-label.send-feedback { -webkit-mask: url('smiley.svg') no-repeat 50% 50%; /* use mask to be able to change color dynamically */ width: 26px; } /* Theming */ -.monaco-shell.vs .feedback-form .feedback-alias, .monaco-shell.vs .feedback-form .feedback-description { +.vs .monaco-workbench .feedback-form .feedback-alias, .vs .monaco-workbench .feedback-form .feedback-description { font-family: inherit; border: 1px solid transparent; } -.monaco-shell.vs .feedback-form .cancel { +.vs .monaco-workbench .feedback-form .cancel { background: url('close.svg') center center no-repeat; } -.monaco-shell .feedback-form .form-buttons { +.monaco-workbench .feedback-form .form-buttons { display: flex; } -.monaco-shell .feedback-form .form-buttons .hide-button-container { +.monaco-workbench .feedback-form .form-buttons .hide-button-container { display: flex; } -.monaco-shell .feedback-form .form-buttons .hide-button-container input, -.monaco-shell .feedback-form .form-buttons .hide-button-container label { +.monaco-workbench .feedback-form .form-buttons .hide-button-container input, +.monaco-workbench .feedback-form .form-buttons .hide-button-container label { align-self: center; } -.monaco-shell .feedback-form .form-buttons .send { +.monaco-workbench .feedback-form .form-buttons .send { width: auto; background-image: url('twitter.svg'); background-position: 12px center; @@ -157,56 +158,56 @@ transition: width 200ms ease-out; } -.monaco-shell .feedback-form .form-buttons .send.in-progress, -.monaco-shell .feedback-form .form-buttons .send:hover { +.monaco-workbench .feedback-form .form-buttons .send.in-progress, +.monaco-workbench .feedback-form .form-buttons .send:hover { background-color: #006BB3; } -.monaco-shell .feedback-form .form-buttons .send:disabled { +.monaco-workbench .feedback-form .form-buttons .send:disabled { pointer-events: none; cursor: not-allowed; opacity: .65; } -.monaco-shell .feedback-form .form-buttons .send.success { +.monaco-workbench .feedback-form .form-buttons .send.success { background-color: #2d883e; } -.monaco-shell .feedback-form .form-buttons .send.error { +.monaco-workbench .feedback-form .form-buttons .send.error { background-color: #E51400; } -.monaco-shell.vs-dark .feedback-form h3 { +.vs-dark .monaco-workbench .feedback-form h3 { font-weight: normal; font-size: 1.2em; } -.monaco-shell.vs-dark .feedback-form .sentiment:hover { +.vs-dark .monaco-workbench .feedback-form .sentiment:hover { background-color: rgba(30,30,30,0.8); } -.monaco-shell.vs-dark .feedback-form .feedback-alias, .monaco-shell.vs-dark .feedback-form .feedback-description { +.vs-dark .monaco-workbench .feedback-form .feedback-alias, .vs-dark .monaco-workbench .feedback-form .feedback-description { font-family: inherit; } -.monaco-shell.vs-dark .feedback-form .cancel, -.monaco-shell.hc-black .feedback-form .cancel { +.vs-dark .monaco-workbench .feedback-form .cancel, +.hc-black .monaco-workbench .feedback-form .cancel { background: url('close-dark.svg') center center no-repeat; } -.monaco-shell .feedback-form .sentiment.smile { +.monaco-workbench .feedback-form .sentiment.smile { background-image: url('happy.svg'); background-position: center; background-repeat: no-repeat; } -.monaco-shell .feedback-form .sentiment.frown { +.monaco-workbench .feedback-form .sentiment.frown { background-image: url('sad.svg'); background-position: center; background-repeat: no-repeat; } -.monaco-shell .feedback-form .infotip { +.monaco-workbench .feedback-form .infotip { background-image: url('info.svg'); background-position: center; background-repeat: no-repeat; @@ -220,38 +221,38 @@ } /* High Contrast Theming */ -.monaco-shell.hc-black .feedback-form { +.hc-black .monaco-workbench .feedback-form { outline: 2px solid #6fc3df; outline-offset: -2px; } -.monaco-shell.hc-black .feedback-form .feedback-alias, .monaco-shell.hc-black .feedback-form .feedback-description { +.hc-black .monaco-workbench .feedback-form .feedback-alias, .hc-black .monaco-workbench .feedback-form .feedback-description { font-family: inherit; } -.monaco-shell.hc-black .feedback-form .content .contactus { +.hc-black .monaco-workbench .feedback-form .content .contactus { padding: 10px; float: right; } -.monaco-shell.hc-black .feedback-form .form-buttons .send, -.monaco-shell.hc-black .feedback-form .form-buttons .send.in-progress, -.monaco-shell.hc-black .feedback-form .form-buttons .send.success { +.hc-black .monaco-workbench .feedback-form .form-buttons .send, +.hc-black .monaco-workbench .feedback-form .form-buttons .send.in-progress, +.hc-black .monaco-workbench .feedback-form .form-buttons .send.success { background-color: #0C141F; color: #D4D4D4; border: 1px solid #6FC3DF; } -.monaco-shell.hc-black .feedback-form .form-buttons .send:hover { +.hc-black .monaco-workbench .feedback-form .form-buttons .send:hover { background-color: #0C141F; } -.monaco-shell .feedback-form .infotip { +.monaco-workbench .feedback-form .infotip { background: none; } -.monaco-shell .feedback-form .infotip:before { +.monaco-workbench .feedback-form .infotip:before { content: url('info.svg'); height: 16px; width: 16px; From dbfe0e1b8cd21b452edd5cdbe0412e381be1291d Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 24 Jan 2019 13:01:42 +0100 Subject: [PATCH 078/274] Apply foreground color to remote badge --- .../parts/extensions/electron-browser/extensionsWidgets.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsWidgets.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsWidgets.ts index e162af7cc05..6a49044725b 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsWidgets.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsWidgets.ts @@ -13,7 +13,8 @@ import { IExtensionManagementServerService, IExtensionTipsService } from 'vs/pla import { ILabelService } from 'vs/platform/label/common/label'; import { extensionButtonProminentBackground, extensionButtonProminentForeground } from 'vs/workbench/parts/extensions/electron-browser/extensionsActions'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { STATUS_BAR_HOST_NAME_BACKGROUND } from 'vs/workbench/common/theme'; +import { STATUS_BAR_HOST_NAME_BACKGROUND, STATUS_BAR_FOREGROUND, STATUS_BAR_NO_FOLDER_FOREGROUND } from 'vs/workbench/common/theme'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; export abstract class ExtensionWidget extends Disposable implements IExtensionContainer { private _extension: IExtension; @@ -204,6 +205,7 @@ export class RemoteBadgeWidget extends ExtensionWidget { @ILabelService private readonly labelService: ILabelService, @IThemeService private readonly themeService: IThemeService, @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, + @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, ) { super(); this.render(); @@ -230,10 +232,13 @@ export class RemoteBadgeWidget extends ExtensionWidget { const applyBadgeStyle = () => { const bgColor = this.themeService.getTheme().getColor(STATUS_BAR_HOST_NAME_BACKGROUND); + const fgColor = this.workspaceContextService.getWorkbenchState() === WorkbenchState.EMPTY ? this.themeService.getTheme().getColor(STATUS_BAR_NO_FOLDER_FOREGROUND) : this.themeService.getTheme().getColor(STATUS_BAR_FOREGROUND); this.element.style.backgroundColor = bgColor ? bgColor.toString() : ''; + this.element.style.color = fgColor ? fgColor.toString() : ''; }; applyBadgeStyle(); this.themeService.onThemeChange(applyBadgeStyle, this, this.disposables); + this.workspaceContextService.onDidChangeWorkbenchState(applyBadgeStyle, this, this.disposables); const updateTitle = () => this.element.title = this.labelService.getHostLabel(); this.labelService.onDidChangeFormatters(() => updateTitle(), this, this.disposables); From 5036c3eb6b4e9b34d137293169608430aaf18cfc Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 24 Jan 2019 14:52:08 +0100 Subject: [PATCH 079/274] Show remote badge in extension editor --- .../extensions/electron-browser/extensionEditor.ts | 8 ++++++-- .../electron-browser/media/extensionEditor.css | 12 +++++++++++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts index 1e1fba41b8b..763454e1e94 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts @@ -25,7 +25,7 @@ import { IExtensionManifest, IKeyBinding, IView, IViewContainer, ExtensionType } import { ResolvedKeybinding, KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { ExtensionsInput } from 'vs/workbench/parts/extensions/common/extensionsInput'; import { IExtensionsWorkbenchService, IExtensionsViewlet, VIEWLET_ID, IExtension, IExtensionDependencies, ExtensionContainers } from 'vs/workbench/parts/extensions/common/extensions'; -import { RatingsWidget, InstallCountWidget } from 'vs/workbench/parts/extensions/electron-browser/extensionsWidgets'; +import { RatingsWidget, InstallCountWidget, RemoteBadgeWidget } from 'vs/workbench/parts/extensions/electron-browser/extensionsWidgets'; import { EditorOptions } from 'vs/workbench/common/editor'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { CombinedInstallAction, UpdateAction, ExtensionEditorDropDownAction, ReloadAction, MaliciousStatusLabelAction, IgnoreExtensionRecommendationAction, UndoIgnoreExtensionRecommendationAction, EnableDropDownAction, DisableDropDownAction } from 'vs/workbench/parts/extensions/electron-browser/extensionsActions'; @@ -150,6 +150,7 @@ export class ExtensionEditor extends BaseEditor { static readonly ID: string = 'workbench.editor.extension'; + private iconContainer: HTMLElement; private icon: HTMLImageElement; private name: HTMLElement; private identifier: HTMLElement; @@ -207,7 +208,8 @@ export class ExtensionEditor extends BaseEditor { const root = append(parent, $('.extension-editor')); this.header = append(root, $('.header')); - this.icon = append(this.header, $('img.icon', { draggable: false })); + this.iconContainer = append(this.header, $('.icon-container')); + this.icon = append(this.iconContainer, $('img.icon', { draggable: false })); const details = append(this.header, $('.details')); const title = append(details, $('.title')); @@ -285,6 +287,7 @@ export class ExtensionEditor extends BaseEditor { this.extensionManifest = new Cache(() => createCancelablePromise(token => extension.getManifest(token))); this.extensionDependencies = new Cache(() => createCancelablePromise(token => this.extensionsWorkbenchService.loadDependencies(extension, token))); + const remoteBadge = this.instantiationService.createInstance(RemoteBadgeWidget, this.iconContainer); const onError = Event.once(domEvent(this.icon, 'error')); onError(() => this.icon.src = extension.iconUrlFallback, null, this.transientDisposables); this.icon.src = extension.iconUrl; @@ -350,6 +353,7 @@ export class ExtensionEditor extends BaseEditor { } const widgets = [ + remoteBadge, this.instantiationService.createInstance(InstallCountWidget, this.installCount, false), this.instantiationService.createInstance(RatingsWidget, this.rating, false) ]; diff --git a/src/vs/workbench/parts/extensions/electron-browser/media/extensionEditor.css b/src/vs/workbench/parts/extensions/electron-browser/media/extensionEditor.css index 9041a268d23..f9b18fc7a83 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/media/extensionEditor.css +++ b/src/vs/workbench/parts/extensions/electron-browser/media/extensionEditor.css @@ -22,12 +22,22 @@ font-size: 14px; } -.extension-editor > .header > .icon { +.extension-editor > .header > .icon-container { + position: relative; +} + +.extension-editor > .header > .icon-container .icon { height: 128px; width: 128px; object-fit: contain; } +.extension-editor > .header > .icon-container .extension-remote-badge { + position: absolute; + right: 0px; + top: 94px; +} + .extension-editor > .header > .details { padding-left: 20px; overflow: hidden; From 0ea58800dc3322940a7e4042352f7b5ff8b70435 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 24 Jan 2019 14:53:29 +0100 Subject: [PATCH 080/274] Revert "Exand custom views by default" This reverts commit f82c0f12aab27c44bd7e4c04db3c5be31f81fe28. --- src/vs/workbench/api/browser/viewsExtensionPoint.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts index 6ad174d40e8..6ab333bf1e4 100644 --- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts @@ -382,7 +382,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution { ctor: CustomTreeViewPanel, when: ContextKeyExpr.deserialize(item.when), canToggleVisibility: true, - collapsed: false, + collapsed: this.showCollapsed(container), treeView: this.instantiationService.createInstance(CustomTreeView, item.id, container), order: ExtensionIdentifier.equals(extension.description.identifier, container.extensionId) ? index + 1 : undefined, extensionId: extension.description.identifier, @@ -443,6 +443,16 @@ class ViewsExtensionHandler implements IWorkbenchContribution { default: return this.viewContainersRegistry.get(`workbench.view.extension.${value}`); } } + + private showCollapsed(container: ViewContainer): boolean { + switch (container.id) { + case EXPLORER: + case SCM: + case DEBUG: + return true; + } + return false; + } } const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); From a4b18cbccba3fcc924399ad6beda5c082db63d4b Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 24 Jan 2019 15:02:20 +0100 Subject: [PATCH 081/274] debt - inline layout actions --- src/tsconfig.strictNullChecks.json | 9 +- .../browser/actions/layoutActions.ts | 339 ++++++++++++++++++ .../actions/toggleActivityBarVisibility.ts | 50 --- .../browser/actions/toggleCenteredLayout.ts | 44 --- .../browser/actions/toggleEditorLayout.ts | 83 ----- .../browser/actions/toggleSidebarPosition.ts | 54 --- .../actions/toggleSidebarVisibility.ts | 47 --- .../actions/toggleStatusbarVisibility.ts | 50 --- .../browser/actions/toggleTabsVisibility.ts | 38 -- .../browser/actions/toggleZenMode.ts | 45 --- .../parts/activitybar/activitybarPart.ts | 2 +- src/vs/workbench/browser/viewlet.ts | 3 +- src/vs/workbench/electron-browser/main.ts | 4 +- src/vs/workbench/electron-browser/shell.ts | 11 +- .../electron-browser/views/openEditorsView.ts | 2 +- src/vs/workbench/workbench.main.ts | 9 +- 16 files changed, 352 insertions(+), 438 deletions(-) create mode 100644 src/vs/workbench/browser/actions/layoutActions.ts delete mode 100644 src/vs/workbench/browser/actions/toggleActivityBarVisibility.ts delete mode 100644 src/vs/workbench/browser/actions/toggleCenteredLayout.ts delete mode 100644 src/vs/workbench/browser/actions/toggleEditorLayout.ts delete mode 100644 src/vs/workbench/browser/actions/toggleSidebarPosition.ts delete mode 100644 src/vs/workbench/browser/actions/toggleSidebarVisibility.ts delete mode 100644 src/vs/workbench/browser/actions/toggleStatusbarVisibility.ts delete mode 100644 src/vs/workbench/browser/actions/toggleTabsVisibility.ts delete mode 100644 src/vs/workbench/browser/actions/toggleZenMode.ts diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index 716b43d9406..196efef48d9 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -514,14 +514,7 @@ "./vs/workbench/api/shared/editor.ts", "./vs/workbench/api/shared/tasks.ts", "./vs/workbench/browser/actions.ts", - "./vs/workbench/browser/actions/toggleActivityBarVisibility.ts", - "./vs/workbench/browser/actions/toggleCenteredLayout.ts", - "./vs/workbench/browser/actions/toggleEditorLayout.ts", - "./vs/workbench/browser/actions/toggleSidebarPosition.ts", - "./vs/workbench/browser/actions/toggleSidebarVisibility.ts", - "./vs/workbench/browser/actions/toggleStatusbarVisibility.ts", - "./vs/workbench/browser/actions/toggleTabsVisibility.ts", - "./vs/workbench/browser/actions/toggleZenMode.ts", + "./vs/workbench/browser/actions/layoutActions.ts", "./vs/workbench/browser/actions/workspaceActions.ts", "./vs/workbench/browser/actions/workspaceCommands.ts", "./vs/workbench/browser/composite.ts", diff --git a/src/vs/workbench/browser/actions/layoutActions.ts b/src/vs/workbench/browser/actions/layoutActions.ts new file mode 100644 index 00000000000..22c8006cfef --- /dev/null +++ b/src/vs/workbench/browser/actions/layoutActions.ts @@ -0,0 +1,339 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { Action } from 'vs/base/common/actions'; +import { SyncActionDescriptor, MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; +import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; +import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; +import { IPartService, Parts, Position } from 'vs/workbench/services/part/common/partService'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { IEditorGroupsService, GroupOrientation } from 'vs/workbench/services/group/common/editorGroupsService'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { KeyMod, KeyCode, KeyChord } from 'vs/base/common/keyCodes'; +import { dispose, IDisposable } from 'vs/base/common/lifecycle'; + +const registry = Registry.as(Extensions.WorkbenchActions); + +// --- Toggle Activity Bar + +export class ToggleActivityBarVisibilityAction extends Action { + + static readonly ID = 'workbench.action.toggleActivityBarVisibility'; + static readonly LABEL = nls.localize('toggleActivityBar', "Toggle Activity Bar Visibility"); + + private static readonly activityBarVisibleKey = 'workbench.activityBar.visible'; + + constructor( + id: string, + label: string, + @IPartService private readonly partService: IPartService, + @IConfigurationService private readonly configurationService: IConfigurationService + ) { + super(id, label); + + this.enabled = !!this.partService; + } + + run(): Promise { + const visibility = this.partService.isVisible(Parts.ACTIVITYBAR_PART); + const newVisibilityValue = !visibility; + + return this.configurationService.updateValue(ToggleActivityBarVisibilityAction.activityBarVisibleKey, newVisibilityValue, ConfigurationTarget.USER); + } +} + +registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleActivityBarVisibilityAction, ToggleActivityBarVisibilityAction.ID, ToggleActivityBarVisibilityAction.LABEL), 'View: Toggle Activity Bar Visibility', nls.localize('view', "View")); + +MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { + group: '2_workbench_layout', + command: { + id: ToggleActivityBarVisibilityAction.ID, + title: nls.localize({ key: 'miToggleActivityBar', comment: ['&& denotes a mnemonic'] }, "Toggle &&Activity Bar") + }, + order: 4 +}); + +// --- Toggle Centered Layout + +class ToggleCenteredLayout extends Action { + + static readonly ID = 'workbench.action.toggleCenteredLayout'; + static readonly LABEL = nls.localize('toggleCenteredLayout', "Toggle Centered Layout"); + + constructor( + id: string, + label: string, + @IPartService private readonly partService: IPartService + ) { + super(id, label); + this.enabled = !!this.partService; + } + + run(): Promise { + this.partService.centerEditorLayout(!this.partService.isEditorLayoutCentered()); + + return Promise.resolve(null); + } +} + +registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleCenteredLayout, ToggleCenteredLayout.ID, ToggleCenteredLayout.LABEL), 'View: Toggle Centered Layout', nls.localize('view', "View")); + +MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { + group: '1_toggle_view', + command: { + id: ToggleCenteredLayout.ID, + title: nls.localize('miToggleCenteredLayout', "Toggle Centered Layout") + }, + order: 3 +}); + +// --- Toggle Editor Layout + +export class ToggleEditorLayoutAction extends Action { + + static readonly ID = 'workbench.action.toggleEditorGroupLayout'; + static readonly LABEL = nls.localize('flipLayout', "Toggle Vertical/Horizontal Editor Layout"); + + private toDispose: IDisposable[]; + + constructor( + id: string, + label: string, + @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService + ) { + super(id, label); + + this.toDispose = []; + + this.class = 'flip-editor-layout'; + this.updateEnablement(); + + this.registerListeners(); + } + + private registerListeners(): void { + this.toDispose.push(this.editorGroupService.onDidAddGroup(() => this.updateEnablement())); + this.toDispose.push(this.editorGroupService.onDidRemoveGroup(() => this.updateEnablement())); + } + + private updateEnablement(): void { + this.enabled = this.editorGroupService.count > 1; + } + + run(): Promise { + const newOrientation = (this.editorGroupService.orientation === GroupOrientation.VERTICAL) ? GroupOrientation.HORIZONTAL : GroupOrientation.VERTICAL; + this.editorGroupService.setGroupOrientation(newOrientation); + + return Promise.resolve(null); + } + + dispose(): void { + this.toDispose = dispose(this.toDispose); + + super.dispose(); + } +} + +CommandsRegistry.registerCommand('_workbench.editor.setGroupOrientation', function (accessor: ServicesAccessor, args: [GroupOrientation]) { + const editorGroupService = accessor.get(IEditorGroupsService); + const [orientation] = args; + + editorGroupService.setGroupOrientation(orientation); + + return Promise.resolve(null); +}); + +const group = nls.localize('view', "View"); +registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleEditorLayoutAction, ToggleEditorLayoutAction.ID, ToggleEditorLayoutAction.LABEL, { primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_0, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_0 } }), 'View: Flip Editor Group Layout', group); + +MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { + group: 'z_flip', + command: { + id: ToggleEditorLayoutAction.ID, + title: nls.localize({ key: 'miToggleEditorLayout', comment: ['&& denotes a mnemonic'] }, "Flip &&Layout") + }, + order: 1 +}); + +// --- Toggle Sidebar Position + +export class ToggleSidebarPositionAction extends Action { + + static readonly ID = 'workbench.action.toggleSidebarPosition'; + static readonly LABEL = nls.localize('toggleSidebarPosition', "Toggle Side Bar Position"); + + private static readonly sidebarPositionConfigurationKey = 'workbench.sideBar.location'; + + constructor( + id: string, + label: string, + @IPartService private readonly partService: IPartService, + @IConfigurationService private readonly configurationService: IConfigurationService + ) { + super(id, label); + + this.enabled = !!this.partService && !!this.configurationService; + } + + run(): Promise { + const position = this.partService.getSideBarPosition(); + const newPositionValue = (position === Position.LEFT) ? 'right' : 'left'; + + return this.configurationService.updateValue(ToggleSidebarPositionAction.sidebarPositionConfigurationKey, newPositionValue, ConfigurationTarget.USER); + } + + static getLabel(partService: IPartService): string { + return partService.getSideBarPosition() === Position.LEFT ? nls.localize('moveSidebarRight', "Move Side Bar Right") : nls.localize('moveSidebarLeft', "Move Side Bar Left"); + } +} + +registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleSidebarPositionAction, ToggleSidebarPositionAction.ID, ToggleSidebarPositionAction.LABEL), 'View: Toggle Side Bar Position', nls.localize('view', "View")); + +MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { + group: '2_workbench_layout', + command: { + id: ToggleSidebarPositionAction.ID, + title: nls.localize({ key: 'miMoveSidebarLeftRight', comment: ['&& denotes a mnemonic'] }, "&&Move Side Bar Left/Right") + }, + order: 2 +}); + +// --- Toggle Sidebar Visibility + +export class ToggleSidebarVisibilityAction extends Action { + + static readonly ID = 'workbench.action.toggleSidebarVisibility'; + static readonly LABEL = nls.localize('toggleSidebar', "Toggle Side Bar Visibility"); + + constructor( + id: string, + label: string, + @IPartService private readonly partService: IPartService + ) { + super(id, label); + + this.enabled = !!this.partService; + } + + run(): Promise { + const hideSidebar = this.partService.isVisible(Parts.SIDEBAR_PART); + this.partService.setSideBarHidden(hideSidebar); + + return Promise.resolve(null); + } +} + +registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleSidebarVisibilityAction, ToggleSidebarVisibilityAction.ID, ToggleSidebarVisibilityAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_B }), 'View: Toggle Side Bar Visibility', nls.localize('view', "View")); + +MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { + group: '2_workbench_layout', + command: { + id: ToggleSidebarVisibilityAction.ID, + title: nls.localize({ key: 'miToggleSidebar', comment: ['&& denotes a mnemonic'] }, "&&Toggle Side Bar") + }, + order: 1 +}); + +// --- Toggle Statusbar Visibility + +class ToggleStatusbarVisibilityAction extends Action { + + static readonly ID = 'workbench.action.toggleStatusbarVisibility'; + static readonly LABEL = nls.localize('toggleStatusbar', "Toggle Status Bar Visibility"); + + private static readonly statusbarVisibleKey = 'workbench.statusBar.visible'; + + constructor( + id: string, + label: string, + @IPartService private readonly partService: IPartService, + @IConfigurationService private readonly configurationService: IConfigurationService + ) { + super(id, label); + + this.enabled = !!this.partService; + } + + run(): Promise { + const visibility = this.partService.isVisible(Parts.STATUSBAR_PART); + const newVisibilityValue = !visibility; + + return this.configurationService.updateValue(ToggleStatusbarVisibilityAction.statusbarVisibleKey, newVisibilityValue, ConfigurationTarget.USER); + } +} + +registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleStatusbarVisibilityAction, ToggleStatusbarVisibilityAction.ID, ToggleStatusbarVisibilityAction.LABEL), 'View: Toggle Status Bar Visibility', nls.localize('view', "View")); + +MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { + group: '2_workbench_layout', + command: { + id: ToggleStatusbarVisibilityAction.ID, + title: nls.localize({ key: 'miToggleStatusbar', comment: ['&& denotes a mnemonic'] }, "&&Toggle Status Bar") + }, + order: 3 +}); + +// --- Toggle Tabs Visibility + +class ToggleTabsVisibilityAction extends Action { + + static readonly ID = 'workbench.action.toggleTabsVisibility'; + static readonly LABEL = nls.localize('toggleTabs', "Toggle Tab Visibility"); + + private static readonly tabsVisibleKey = 'workbench.editor.showTabs'; + + constructor( + id: string, + label: string, + @IConfigurationService private readonly configurationService: IConfigurationService + ) { + super(id, label); + } + + run(): Promise { + const visibility = this.configurationService.getValue(ToggleTabsVisibilityAction.tabsVisibleKey); + const newVisibilityValue = !visibility; + + return this.configurationService.updateValue(ToggleTabsVisibilityAction.tabsVisibleKey, newVisibilityValue); + } +} + +registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleTabsVisibilityAction, ToggleTabsVisibilityAction.ID, ToggleTabsVisibilityAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_W }), 'View: Toggle Tab Visibility', nls.localize('view', "View")); + +// --- Toggle Zen Mode + +class ToggleZenMode extends Action { + + static readonly ID = 'workbench.action.toggleZenMode'; + static readonly LABEL = nls.localize('toggleZenMode', "Toggle Zen Mode"); + + constructor( + id: string, + label: string, + @IPartService private readonly partService: IPartService + ) { + super(id, label); + this.enabled = !!this.partService; + } + + run(): Promise { + this.partService.toggleZenMode(); + + return Promise.resolve(null); + } +} + +registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleZenMode, ToggleZenMode.ID, ToggleZenMode.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_Z) }), 'View: Toggle Zen Mode', nls.localize('view', "View")); + +MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { + group: '1_toggle_view', + command: { + id: ToggleZenMode.ID, + title: nls.localize('miToggleZenMode', "Toggle Zen Mode") + }, + order: 2 +}); diff --git a/src/vs/workbench/browser/actions/toggleActivityBarVisibility.ts b/src/vs/workbench/browser/actions/toggleActivityBarVisibility.ts deleted file mode 100644 index f28a090fa36..00000000000 --- a/src/vs/workbench/browser/actions/toggleActivityBarVisibility.ts +++ /dev/null @@ -1,50 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as nls from 'vs/nls'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { Action } from 'vs/base/common/actions'; -import { SyncActionDescriptor, MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; -import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; -import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; -import { IPartService, Parts } from 'vs/workbench/services/part/common/partService'; - -export class ToggleActivityBarVisibilityAction extends Action { - - static readonly ID = 'workbench.action.toggleActivityBarVisibility'; - static readonly LABEL = nls.localize('toggleActivityBar', "Toggle Activity Bar Visibility"); - - private static readonly activityBarVisibleKey = 'workbench.activityBar.visible'; - - constructor( - id: string, - label: string, - @IPartService private readonly partService: IPartService, - @IConfigurationService private readonly configurationService: IConfigurationService - ) { - super(id, label); - - this.enabled = !!this.partService; - } - - run(): Promise { - const visibility = this.partService.isVisible(Parts.ACTIVITYBAR_PART); - const newVisibilityValue = !visibility; - - return this.configurationService.updateValue(ToggleActivityBarVisibilityAction.activityBarVisibleKey, newVisibilityValue, ConfigurationTarget.USER); - } -} - -const registry = Registry.as(Extensions.WorkbenchActions); -registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleActivityBarVisibilityAction, ToggleActivityBarVisibilityAction.ID, ToggleActivityBarVisibilityAction.LABEL), 'View: Toggle Activity Bar Visibility', nls.localize('view', "View")); - -MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { - group: '2_workbench_layout', - command: { - id: ToggleActivityBarVisibilityAction.ID, - title: nls.localize({ key: 'miToggleActivityBar', comment: ['&& denotes a mnemonic'] }, "Toggle &&Activity Bar") - }, - order: 4 -}); diff --git a/src/vs/workbench/browser/actions/toggleCenteredLayout.ts b/src/vs/workbench/browser/actions/toggleCenteredLayout.ts deleted file mode 100644 index 9e664ceec23..00000000000 --- a/src/vs/workbench/browser/actions/toggleCenteredLayout.ts +++ /dev/null @@ -1,44 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as nls from 'vs/nls'; -import { Action } from 'vs/base/common/actions'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; -import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; -import { IPartService } from 'vs/workbench/services/part/common/partService'; - -class ToggleCenteredLayout extends Action { - - static readonly ID = 'workbench.action.toggleCenteredLayout'; - static readonly LABEL = nls.localize('toggleCenteredLayout', "Toggle Centered Layout"); - - constructor( - id: string, - label: string, - @IPartService private readonly partService: IPartService - ) { - super(id, label); - this.enabled = !!this.partService; - } - - run(): Promise { - this.partService.centerEditorLayout(!this.partService.isEditorLayoutCentered()); - - return Promise.resolve(null); - } -} - -const registry = Registry.as(Extensions.WorkbenchActions); -registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleCenteredLayout, ToggleCenteredLayout.ID, ToggleCenteredLayout.LABEL), 'View: Toggle Centered Layout', nls.localize('view', "View")); - -MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { - group: '1_toggle_view', - command: { - id: ToggleCenteredLayout.ID, - title: nls.localize('miToggleCenteredLayout', "Toggle Centered Layout") - }, - order: 3 -}); diff --git a/src/vs/workbench/browser/actions/toggleEditorLayout.ts b/src/vs/workbench/browser/actions/toggleEditorLayout.ts deleted file mode 100644 index 3e31b9b481c..00000000000 --- a/src/vs/workbench/browser/actions/toggleEditorLayout.ts +++ /dev/null @@ -1,83 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import 'vs/css!./media/actions'; -import * as nls from 'vs/nls'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { Action } from 'vs/base/common/actions'; -import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; -import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; -import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; -import { dispose, IDisposable } from 'vs/base/common/lifecycle'; -import { CommandsRegistry } from 'vs/platform/commands/common/commands'; -import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { IEditorGroupsService, GroupOrientation } from 'vs/workbench/services/group/common/editorGroupsService'; - -export class ToggleEditorLayoutAction extends Action { - - static readonly ID = 'workbench.action.toggleEditorGroupLayout'; - static readonly LABEL = nls.localize('flipLayout', "Toggle Vertical/Horizontal Editor Layout"); - - private toDispose: IDisposable[]; - - constructor( - id: string, - label: string, - @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService - ) { - super(id, label); - - this.toDispose = []; - - this.class = 'flip-editor-layout'; - this.updateEnablement(); - - this.registerListeners(); - } - - private registerListeners(): void { - this.toDispose.push(this.editorGroupService.onDidAddGroup(() => this.updateEnablement())); - this.toDispose.push(this.editorGroupService.onDidRemoveGroup(() => this.updateEnablement())); - } - - private updateEnablement(): void { - this.enabled = this.editorGroupService.count > 1; - } - - run(): Promise { - const newOrientation = (this.editorGroupService.orientation === GroupOrientation.VERTICAL) ? GroupOrientation.HORIZONTAL : GroupOrientation.VERTICAL; - this.editorGroupService.setGroupOrientation(newOrientation); - - return Promise.resolve(null); - } - - dispose(): void { - this.toDispose = dispose(this.toDispose); - - super.dispose(); - } -} - -CommandsRegistry.registerCommand('_workbench.editor.setGroupOrientation', function (accessor: ServicesAccessor, args: [GroupOrientation]) { - const editorGroupService = accessor.get(IEditorGroupsService); - const [orientation] = args; - - editorGroupService.setGroupOrientation(orientation); - - return Promise.resolve(null); -}); - -const registry = Registry.as(Extensions.WorkbenchActions); -const group = nls.localize('view', "View"); -registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleEditorLayoutAction, ToggleEditorLayoutAction.ID, ToggleEditorLayoutAction.LABEL, { primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_0, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_0 } }), 'View: Flip Editor Group Layout', group); - -MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { - group: 'z_flip', - command: { - id: ToggleEditorLayoutAction.ID, - title: nls.localize({ key: 'miToggleEditorLayout', comment: ['&& denotes a mnemonic'] }, "Flip &&Layout") - }, - order: 1 -}); diff --git a/src/vs/workbench/browser/actions/toggleSidebarPosition.ts b/src/vs/workbench/browser/actions/toggleSidebarPosition.ts deleted file mode 100644 index 9eb8bd8ae8d..00000000000 --- a/src/vs/workbench/browser/actions/toggleSidebarPosition.ts +++ /dev/null @@ -1,54 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as nls from 'vs/nls'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { Action } from 'vs/base/common/actions'; -import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; -import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; -import { IPartService, Position } from 'vs/workbench/services/part/common/partService'; -import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; - -export class ToggleSidebarPositionAction extends Action { - - static readonly ID = 'workbench.action.toggleSidebarPosition'; - static readonly LABEL = nls.localize('toggleSidebarPosition', "Toggle Side Bar Position"); - - private static readonly sidebarPositionConfigurationKey = 'workbench.sideBar.location'; - - constructor( - id: string, - label: string, - @IPartService private readonly partService: IPartService, - @IConfigurationService private readonly configurationService: IConfigurationService - ) { - super(id, label); - - this.enabled = !!this.partService && !!this.configurationService; - } - - run(): Promise { - const position = this.partService.getSideBarPosition(); - const newPositionValue = (position === Position.LEFT) ? 'right' : 'left'; - - return this.configurationService.updateValue(ToggleSidebarPositionAction.sidebarPositionConfigurationKey, newPositionValue, ConfigurationTarget.USER); - } - - static getLabel(partService: IPartService): string { - return partService.getSideBarPosition() === Position.LEFT ? nls.localize('moveSidebarRight', "Move Side Bar Right") : nls.localize('moveSidebarLeft', "Move Side Bar Left"); - } -} - -const registry = Registry.as(Extensions.WorkbenchActions); -registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleSidebarPositionAction, ToggleSidebarPositionAction.ID, ToggleSidebarPositionAction.LABEL), 'View: Toggle Side Bar Position', nls.localize('view', "View")); - -MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { - group: '2_workbench_layout', - command: { - id: ToggleSidebarPositionAction.ID, - title: nls.localize({ key: 'miMoveSidebarLeftRight', comment: ['&& denotes a mnemonic'] }, "&&Move Side Bar Left/Right") - }, - order: 2 -}); diff --git a/src/vs/workbench/browser/actions/toggleSidebarVisibility.ts b/src/vs/workbench/browser/actions/toggleSidebarVisibility.ts deleted file mode 100644 index 48a2d0a95b8..00000000000 --- a/src/vs/workbench/browser/actions/toggleSidebarVisibility.ts +++ /dev/null @@ -1,47 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as nls from 'vs/nls'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { Action } from 'vs/base/common/actions'; -import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; -import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; -import { IPartService, Parts } from 'vs/workbench/services/part/common/partService'; -import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; - -export class ToggleSidebarVisibilityAction extends Action { - - static readonly ID = 'workbench.action.toggleSidebarVisibility'; - static readonly LABEL = nls.localize('toggleSidebar', "Toggle Side Bar Visibility"); - - constructor( - id: string, - label: string, - @IPartService private readonly partService: IPartService - ) { - super(id, label); - - this.enabled = !!this.partService; - } - - run(): Promise { - const hideSidebar = this.partService.isVisible(Parts.SIDEBAR_PART); - this.partService.setSideBarHidden(hideSidebar); - - return Promise.resolve(null); - } -} - -const registry = Registry.as(Extensions.WorkbenchActions); -registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleSidebarVisibilityAction, ToggleSidebarVisibilityAction.ID, ToggleSidebarVisibilityAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_B }), 'View: Toggle Side Bar Visibility', nls.localize('view', "View")); - -MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { - group: '2_workbench_layout', - command: { - id: ToggleSidebarVisibilityAction.ID, - title: nls.localize({ key: 'miToggleSidebar', comment: ['&& denotes a mnemonic'] }, "&&Toggle Side Bar") - }, - order: 1 -}); diff --git a/src/vs/workbench/browser/actions/toggleStatusbarVisibility.ts b/src/vs/workbench/browser/actions/toggleStatusbarVisibility.ts deleted file mode 100644 index 277eff7fcf7..00000000000 --- a/src/vs/workbench/browser/actions/toggleStatusbarVisibility.ts +++ /dev/null @@ -1,50 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as nls from 'vs/nls'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { Action } from 'vs/base/common/actions'; -import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; -import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; -import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; -import { IPartService, Parts } from 'vs/workbench/services/part/common/partService'; - -export class ToggleStatusbarVisibilityAction extends Action { - - static readonly ID = 'workbench.action.toggleStatusbarVisibility'; - static readonly LABEL = nls.localize('toggleStatusbar', "Toggle Status Bar Visibility"); - - private static readonly statusbarVisibleKey = 'workbench.statusBar.visible'; - - constructor( - id: string, - label: string, - @IPartService private readonly partService: IPartService, - @IConfigurationService private readonly configurationService: IConfigurationService - ) { - super(id, label); - - this.enabled = !!this.partService; - } - - run(): Promise { - const visibility = this.partService.isVisible(Parts.STATUSBAR_PART); - const newVisibilityValue = !visibility; - - return this.configurationService.updateValue(ToggleStatusbarVisibilityAction.statusbarVisibleKey, newVisibilityValue, ConfigurationTarget.USER); - } -} - -const registry = Registry.as(Extensions.WorkbenchActions); -registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleStatusbarVisibilityAction, ToggleStatusbarVisibilityAction.ID, ToggleStatusbarVisibilityAction.LABEL), 'View: Toggle Status Bar Visibility', nls.localize('view', "View")); - -MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { - group: '2_workbench_layout', - command: { - id: ToggleStatusbarVisibilityAction.ID, - title: nls.localize({ key: 'miToggleStatusbar', comment: ['&& denotes a mnemonic'] }, "&&Toggle Status Bar") - }, - order: 3 -}); diff --git a/src/vs/workbench/browser/actions/toggleTabsVisibility.ts b/src/vs/workbench/browser/actions/toggleTabsVisibility.ts deleted file mode 100644 index bde2adca0a6..00000000000 --- a/src/vs/workbench/browser/actions/toggleTabsVisibility.ts +++ /dev/null @@ -1,38 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as nls from 'vs/nls'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { Action } from 'vs/base/common/actions'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; -import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; - -export class ToggleTabsVisibilityAction extends Action { - - static readonly ID = 'workbench.action.toggleTabsVisibility'; - static readonly LABEL = nls.localize('toggleTabs', "Toggle Tab Visibility"); - - private static readonly tabsVisibleKey = 'workbench.editor.showTabs'; - - constructor( - id: string, - label: string, - @IConfigurationService private readonly configurationService: IConfigurationService - ) { - super(id, label); - } - - run(): Promise { - const visibility = this.configurationService.getValue(ToggleTabsVisibilityAction.tabsVisibleKey); - const newVisibilityValue = !visibility; - - return this.configurationService.updateValue(ToggleTabsVisibilityAction.tabsVisibleKey, newVisibilityValue); - } -} - -const registry = Registry.as(Extensions.WorkbenchActions); -registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleTabsVisibilityAction, ToggleTabsVisibilityAction.ID, ToggleTabsVisibilityAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_W }), 'View: Toggle Tab Visibility', nls.localize('view', "View")); \ No newline at end of file diff --git a/src/vs/workbench/browser/actions/toggleZenMode.ts b/src/vs/workbench/browser/actions/toggleZenMode.ts deleted file mode 100644 index 73ed632b037..00000000000 --- a/src/vs/workbench/browser/actions/toggleZenMode.ts +++ /dev/null @@ -1,45 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as nls from 'vs/nls'; -import { Action } from 'vs/base/common/actions'; -import { KeyCode, KeyMod, KeyChord } from 'vs/base/common/keyCodes'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; -import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; -import { IPartService } from 'vs/workbench/services/part/common/partService'; - -class ToggleZenMode extends Action { - - static readonly ID = 'workbench.action.toggleZenMode'; - static readonly LABEL = nls.localize('toggleZenMode', "Toggle Zen Mode"); - - constructor( - id: string, - label: string, - @IPartService private readonly partService: IPartService - ) { - super(id, label); - this.enabled = !!this.partService; - } - - run(): Promise { - this.partService.toggleZenMode(); - - return Promise.resolve(null); - } -} - -const registry = Registry.as(Extensions.WorkbenchActions); -registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleZenMode, ToggleZenMode.ID, ToggleZenMode.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_Z) }), 'View: Toggle Zen Mode', nls.localize('view', "View")); - -MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { - group: '1_toggle_view', - command: { - id: ToggleZenMode.ID, - title: nls.localize('miToggleZenMode', "Toggle Zen Mode") - }, - order: 2 -}); diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts index ec3397b9066..1f55bd25236 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts @@ -17,7 +17,7 @@ import { IBadge } from 'vs/workbench/services/activity/common/activity'; import { IPartService, Parts, Position as SideBarPosition } from 'vs/workbench/services/part/common/partService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IDisposable, toDisposable, dispose } from 'vs/base/common/lifecycle'; -import { ToggleActivityBarVisibilityAction } from 'vs/workbench/browser/actions/toggleActivityBarVisibility'; +import { ToggleActivityBarVisibilityAction } from 'vs/workbench/browser/actions/layoutActions'; import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService'; import { ACTIVITY_BAR_BACKGROUND, ACTIVITY_BAR_BORDER, ACTIVITY_BAR_FOREGROUND, ACTIVITY_BAR_BADGE_BACKGROUND, ACTIVITY_BAR_BADGE_FOREGROUND, ACTIVITY_BAR_DRAG_AND_DROP_BACKGROUND, ACTIVITY_BAR_INACTIVE_FOREGROUND } from 'vs/workbench/common/theme'; import { contrastBorder } from 'vs/platform/theme/common/colorRegistry'; diff --git a/src/vs/workbench/browser/viewlet.ts b/src/vs/workbench/browser/viewlet.ts index f843c9bc95f..f071f08339a 100644 --- a/src/vs/workbench/browser/viewlet.ts +++ b/src/vs/workbench/browser/viewlet.ts @@ -12,13 +12,12 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IViewlet } from 'vs/workbench/common/viewlet'; import { Composite, CompositeDescriptor, CompositeRegistry } from 'vs/workbench/browser/composite'; import { IConstructorSignature0 } from 'vs/platform/instantiation/common/instantiation'; -import { ToggleSidebarVisibilityAction } from 'vs/workbench/browser/actions/toggleSidebarVisibility'; +import { ToggleSidebarVisibilityAction, ToggleSidebarPositionAction } from 'vs/workbench/browser/actions/layoutActions'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IPartService, Parts } from 'vs/workbench/services/part/common/partService'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; import { URI } from 'vs/base/common/uri'; -import { ToggleSidebarPositionAction } from 'vs/workbench/browser/actions/toggleSidebarPosition'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { AsyncDataTree } from 'vs/base/browser/ui/tree/asyncDataTree'; diff --git a/src/vs/workbench/electron-browser/main.ts b/src/vs/workbench/electron-browser/main.ts index 0a7fc9f8db8..1e6333c7314 100644 --- a/src/vs/workbench/electron-browser/main.ts +++ b/src/vs/workbench/electron-browser/main.ts @@ -5,7 +5,7 @@ import * as nls from 'vs/nls'; import * as perf from 'vs/base/common/performance'; -import { WorkbenchShell } from 'vs/workbench/electron-browser/shell'; +import { Shell } from 'vs/workbench/electron-browser/shell'; import * as browser from 'vs/base/browser/browser'; import { domContentLoaded } from 'vs/base/browser/dom'; import { onUnexpectedError } from 'vs/base/common/errors'; @@ -127,7 +127,7 @@ function openWorkbench(configuration: IWindowConfiguration): Promise { perf.mark('willStartWorkbench'); // Create Shell - const shell = new WorkbenchShell(document.body, { + const shell = new Shell(document.body, { contextService: workspaceService, configurationService: workspaceService, environmentService, diff --git a/src/vs/workbench/electron-browser/shell.ts b/src/vs/workbench/electron-browser/shell.ts index 3b28eced7e7..fd959395067 100644 --- a/src/vs/workbench/electron-browser/shell.ts +++ b/src/vs/workbench/electron-browser/shell.ts @@ -123,7 +123,7 @@ export interface ICoreServices { * The workbench shell contains the workbench with a rich header containing navigation and the activity bar. * With the Shell being the top level element in the page, it is also responsible for driving the layouting. */ -export class WorkbenchShell extends Disposable { +export class Shell extends Disposable { private readonly _onWillShutdown = this._register(new Emitter()); get onWillShutdown(): Event { return this._onWillShutdown.event; } @@ -416,11 +416,12 @@ export class WorkbenchShell extends Disposable { serviceCollection.set(IWindowService, new SyncDescriptor(WindowService, [this.configuration.windowId, this.configuration])); const sharedProcess = (serviceCollection.get(IWindowsService)).whenSharedProcessReady() - .then(() => connectNet(this.environmentService.sharedIPCHandle, `window:${this.configuration.windowId}`)); + .then(() => connectNet(this.environmentService.sharedIPCHandle, `window:${this.configuration.windowId}`)) + .then(client => { + client.registerChannel('dialog', instantiationService.createInstance(DialogChannel)); - sharedProcess.then(client => { - client.registerChannel('dialog', instantiationService.createInstance(DialogChannel)); - }); + return client; + }); // Hash serviceCollection.set(IHashService, new SyncDescriptor(HashService, undefined, true)); diff --git a/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts b/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts index d0ad0774f01..ab9376bce14 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts @@ -18,7 +18,7 @@ import { OpenEditorsFocusedContext, ExplorerFocusedContext, IFilesConfiguration, import { ITextFileService, AutoSaveMode } from 'vs/workbench/services/textfile/common/textfiles'; import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; import { CloseAllEditorsAction, CloseEditorAction } from 'vs/workbench/browser/parts/editor/editorActions'; -import { ToggleEditorLayoutAction } from 'vs/workbench/browser/actions/toggleEditorLayout'; +import { ToggleEditorLayoutAction } from 'vs/workbench/browser/actions/layoutActions'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { attachStylerCallback } from 'vs/platform/theme/common/styler'; import { IThemeService } from 'vs/platform/theme/common/themeService'; diff --git a/src/vs/workbench/workbench.main.ts b/src/vs/workbench/workbench.main.ts index 1cb536f7767..6b5b6430a84 100644 --- a/src/vs/workbench/workbench.main.ts +++ b/src/vs/workbench/workbench.main.ts @@ -26,14 +26,7 @@ import 'vs/workbench/api/browser/viewsExtensionPoint'; import 'vs/workbench/parts/localizations/electron-browser/localizations.contribution'; // Workbench -import 'vs/workbench/browser/actions/toggleActivityBarVisibility'; -import 'vs/workbench/browser/actions/toggleStatusbarVisibility'; -import 'vs/workbench/browser/actions/toggleSidebarVisibility'; -import 'vs/workbench/browser/actions/toggleSidebarPosition'; -import 'vs/workbench/browser/actions/toggleEditorLayout'; -import 'vs/workbench/browser/actions/toggleZenMode'; -import 'vs/workbench/browser/actions/toggleCenteredLayout'; -import 'vs/workbench/browser/actions/toggleTabsVisibility'; +import 'vs/workbench/browser/actions/layoutActions'; import 'vs/workbench/parts/preferences/electron-browser/preferences.contribution'; import 'vs/workbench/parts/preferences/browser/keybindingsEditorContribution'; import 'vs/workbench/parts/logs/electron-browser/logs.contribution'; From b7de40e874730fa402d7378862dc4647e42cb0db Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 24 Jan 2019 15:32:33 +0100 Subject: [PATCH 082/274] Add telemetry to problems interaction --- .../markers/electron-browser/markersModel.ts | 2 +- .../markers/electron-browser/markersPanel.ts | 34 +++++++++++++++++-- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/parts/markers/electron-browser/markersModel.ts b/src/vs/workbench/parts/markers/electron-browser/markersModel.ts index fec2e56c60d..cf207bf5e22 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markersModel.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markersModel.ts @@ -111,7 +111,7 @@ export class RelatedInformation { constructor( private resource: URI, - private marker: IMarker, + readonly marker: IMarker, readonly raw: IRelatedInformation ) { } } diff --git a/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts b/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts index bd0486b90ee..312517f9f86 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts @@ -43,6 +43,7 @@ import { KeyCode } from 'vs/base/common/keyCodes'; import { domEvent } from 'vs/base/browser/event'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { ResourceLabels } from 'vs/workbench/browser/labels'; +import { IMarker } from 'vs/platform/markers/common/markers'; function createModelIterator(model: MarkersModel): Iterator> { const resourcesIt = Iterator.fromArray(model.resourceMarkers); @@ -178,9 +179,22 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { } public openFileAtElement(element: any, preserveFocus: boolean, sideByside: boolean, pinned: boolean): boolean { - const { resource, selection } = element instanceof Marker ? { resource: element.resource, selection: element.range } : - element instanceof RelatedInformation ? { resource: element.raw.resource, selection: element.raw } : { resource: null, selection: null }; + const { resource, selection, event, data } = element instanceof Marker ? { resource: element.resource, selection: element.range, event: 'problems.selectDiagnostic', data: this.getTelemetryData(element.marker) } : + element instanceof RelatedInformation ? { resource: element.raw.resource, selection: element.raw, event: 'problems.selectRelatedInformation', data: this.getTelemetryData(element.marker) } : { resource: null, selection: null, event: null, data: null }; if (resource && selection) { + /* __GDPR__ + "problems.selectDiagnostic" : { + "source": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, + "code" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" } + } + */ + /* __GDPR__ + "problems.selectRelatedInformation" : { + "source": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, + "code" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" } + } + */ + this.telemetryService.publicLog(event, data); this.editorService.openEditor({ resource, options: { @@ -336,6 +350,18 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { this._register(Event.debounce(markersNavigator.openResource, (last, event) => event, 75, true)(options => { this.openFileAtElement(options.element, options.editorOptions.preserveFocus, options.sideBySide, options.editorOptions.pinned); })); + this._register(this.tree.onDidChangeCollapseState(({ node }) => { + const { element } = node; + if (element instanceof RelatedInformation && !node.collapsed) { + /* __GDPR__ + "problems.expandRelatedInformation" : { + "source": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, + "code" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" } + } + */ + this.telemetryService.publicLog('problems.expandRelatedInformation', this.getTelemetryData(element.marker)); + } + })); this.tree.onContextMenu(this.onContextMenu, this, this._toDispose); @@ -668,6 +694,10 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { return { total, filtered }; } + private getTelemetryData({ source, code }: IMarker): any { + return { source, code }; + } + protected saveState(): void { this.panelState['filter'] = this.filterAction.filterText; this.panelState['filterHistory'] = this.filterAction.filterHistory; From d88425ebbdbc80616e8b713c4aec9b242b08b596 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 24 Jan 2019 15:52:37 +0100 Subject: [PATCH 083/274] debt - untangle actions/commands (part 1) --- src/vs/workbench/browser/actions.ts | 1 + .../browser/actions/layoutActions.ts | 70 +- .../workbench/browser/actions/listCommands.ts | 713 ++++++++++++++++++ .../browser/actions/workspaceActions.ts | 27 + src/vs/workbench/electron-browser/actions.ts | 518 +------------ .../{ => actions}/media/actions.css | 0 .../{ => actions}/media/remove-dark.svg | 0 .../{ => actions}/media/remove.svg | 0 .../electron-browser/actions/windowActions.ts | 466 ++++++++++++ src/vs/workbench/electron-browser/commands.ts | 707 +---------------- .../electron-browser/main.contribution.ts | 18 +- .../electron-browser/media/clear.svg | 1 - .../workbench/electron-browser/workbench.ts | 3 +- .../watermark/electron-browser/watermark.ts | 2 +- src/vs/workbench/workbench.main.ts | 1 + 15 files changed, 1280 insertions(+), 1247 deletions(-) create mode 100644 src/vs/workbench/browser/actions/listCommands.ts rename src/vs/workbench/electron-browser/{ => actions}/media/actions.css (100%) rename src/vs/workbench/electron-browser/{ => actions}/media/remove-dark.svg (100%) rename src/vs/workbench/electron-browser/{ => actions}/media/remove.svg (100%) create mode 100644 src/vs/workbench/electron-browser/actions/windowActions.ts delete mode 100644 src/vs/workbench/electron-browser/media/clear.svg diff --git a/src/vs/workbench/browser/actions.ts b/src/vs/workbench/browser/actions.ts index 628fd519627..c6355e52f00 100644 --- a/src/vs/workbench/browser/actions.ts +++ b/src/vs/workbench/browser/actions.ts @@ -54,6 +54,7 @@ export class ActionBarContributor { * Some predefined scopes to contribute actions to */ export const Scope = { + /** * Actions inside tree widgets. */ diff --git a/src/vs/workbench/browser/actions/layoutActions.ts b/src/vs/workbench/browser/actions/layoutActions.ts index 22c8006cfef..4625ea6e742 100644 --- a/src/vs/workbench/browser/actions/layoutActions.ts +++ b/src/vs/workbench/browser/actions/layoutActions.ts @@ -15,8 +15,12 @@ import { IEditorGroupsService, GroupOrientation } from 'vs/workbench/services/gr import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { KeyMod, KeyCode, KeyChord } from 'vs/base/common/keyCodes'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { MenuBarVisibility } from 'vs/platform/windows/common/windows'; +import { isWindows, isLinux } from 'vs/base/common/platform'; +import { IsMacContext } from 'vs/platform/workbench/common/contextkeys'; const registry = Registry.as(Extensions.WorkbenchActions); +const viewCategory = nls.localize('view', "View"); // --- Toggle Activity Bar @@ -46,7 +50,7 @@ export class ToggleActivityBarVisibilityAction extends Action { } } -registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleActivityBarVisibilityAction, ToggleActivityBarVisibilityAction.ID, ToggleActivityBarVisibilityAction.LABEL), 'View: Toggle Activity Bar Visibility', nls.localize('view', "View")); +registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleActivityBarVisibilityAction, ToggleActivityBarVisibilityAction.ID, ToggleActivityBarVisibilityAction.LABEL), 'View: Toggle Activity Bar Visibility', viewCategory); MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { group: '2_workbench_layout', @@ -80,7 +84,7 @@ class ToggleCenteredLayout extends Action { } } -registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleCenteredLayout, ToggleCenteredLayout.ID, ToggleCenteredLayout.LABEL), 'View: Toggle Centered Layout', nls.localize('view', "View")); +registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleCenteredLayout, ToggleCenteredLayout.ID, ToggleCenteredLayout.LABEL), 'View: Toggle Centered Layout', viewCategory); MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { group: '1_toggle_view', @@ -147,7 +151,7 @@ CommandsRegistry.registerCommand('_workbench.editor.setGroupOrientation', functi return Promise.resolve(null); }); -const group = nls.localize('view', "View"); +const group = viewCategory; registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleEditorLayoutAction, ToggleEditorLayoutAction.ID, ToggleEditorLayoutAction.LABEL, { primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_0, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_0 } }), 'View: Flip Editor Group Layout', group); MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { @@ -191,7 +195,7 @@ export class ToggleSidebarPositionAction extends Action { } } -registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleSidebarPositionAction, ToggleSidebarPositionAction.ID, ToggleSidebarPositionAction.LABEL), 'View: Toggle Side Bar Position', nls.localize('view', "View")); +registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleSidebarPositionAction, ToggleSidebarPositionAction.ID, ToggleSidebarPositionAction.LABEL), 'View: Toggle Side Bar Position', viewCategory); MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { group: '2_workbench_layout', @@ -227,7 +231,7 @@ export class ToggleSidebarVisibilityAction extends Action { } } -registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleSidebarVisibilityAction, ToggleSidebarVisibilityAction.ID, ToggleSidebarVisibilityAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_B }), 'View: Toggle Side Bar Visibility', nls.localize('view', "View")); +registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleSidebarVisibilityAction, ToggleSidebarVisibilityAction.ID, ToggleSidebarVisibilityAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_B }), 'View: Toggle Side Bar Visibility', viewCategory); MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { group: '2_workbench_layout', @@ -266,7 +270,7 @@ class ToggleStatusbarVisibilityAction extends Action { } } -registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleStatusbarVisibilityAction, ToggleStatusbarVisibilityAction.ID, ToggleStatusbarVisibilityAction.LABEL), 'View: Toggle Status Bar Visibility', nls.localize('view', "View")); +registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleStatusbarVisibilityAction, ToggleStatusbarVisibilityAction.ID, ToggleStatusbarVisibilityAction.LABEL), 'View: Toggle Status Bar Visibility', viewCategory); MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { group: '2_workbench_layout', @@ -302,7 +306,7 @@ class ToggleTabsVisibilityAction extends Action { } } -registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleTabsVisibilityAction, ToggleTabsVisibilityAction.ID, ToggleTabsVisibilityAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_W }), 'View: Toggle Tab Visibility', nls.localize('view', "View")); +registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleTabsVisibilityAction, ToggleTabsVisibilityAction.ID, ToggleTabsVisibilityAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_W }), 'View: Toggle Tab Visibility', viewCategory); // --- Toggle Zen Mode @@ -327,7 +331,7 @@ class ToggleZenMode extends Action { } } -registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleZenMode, ToggleZenMode.ID, ToggleZenMode.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_Z) }), 'View: Toggle Zen Mode', nls.localize('view', "View")); +registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleZenMode, ToggleZenMode.ID, ToggleZenMode.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_Z) }), 'View: Toggle Zen Mode', viewCategory); MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { group: '1_toggle_view', @@ -337,3 +341,53 @@ MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { }, order: 2 }); + +// --- Toggle Menu Bar + +export class ToggleMenuBarAction extends Action { + + static readonly ID = 'workbench.action.toggleMenuBar'; + static LABEL = nls.localize('toggleMenuBar', "Toggle Menu Bar"); + + private static readonly menuBarVisibilityKey = 'window.menuBarVisibility'; + + constructor( + id: string, + label: string, + @IConfigurationService private readonly configurationService: IConfigurationService + ) { + super(id, label); + } + + run(): Promise { + let currentVisibilityValue = this.configurationService.getValue(ToggleMenuBarAction.menuBarVisibilityKey); + if (typeof currentVisibilityValue !== 'string') { + currentVisibilityValue = 'default'; + } + + let newVisibilityValue: string; + if (currentVisibilityValue === 'visible' || currentVisibilityValue === 'default') { + newVisibilityValue = 'toggle'; + } else { + newVisibilityValue = 'default'; + } + + this.configurationService.updateValue(ToggleMenuBarAction.menuBarVisibilityKey, newVisibilityValue, ConfigurationTarget.USER); + + return Promise.resolve(); + } +} + +if (isWindows || isLinux) { + registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleMenuBarAction, ToggleMenuBarAction.ID, ToggleMenuBarAction.LABEL), 'View: Toggle Menu Bar', viewCategory); +} + +MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { + group: '1_toggle_view', + command: { + id: ToggleMenuBarAction.ID, + title: nls.localize({ key: 'miToggleMenuBar', comment: ['&& denotes a mnemonic'] }, "Toggle Menu &&Bar") + }, + when: IsMacContext.toNegated(), + order: 4 +}); \ No newline at end of file diff --git a/src/vs/workbench/browser/actions/listCommands.ts b/src/vs/workbench/browser/actions/listCommands.ts new file mode 100644 index 00000000000..6e22af2d2d9 --- /dev/null +++ b/src/vs/workbench/browser/actions/listCommands.ts @@ -0,0 +1,713 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { List } from 'vs/base/browser/ui/list/listWidget'; +import { WorkbenchListFocusContextKey, IListService, WorkbenchListSupportsMultiSelectContextKey, ListWidget, WorkbenchListHasSelectionOrFocus } from 'vs/platform/list/browser/listService'; +import { PagedList } from 'vs/base/browser/ui/list/listPaging'; +import { range } from 'vs/base/common/arrays'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { ITree } from 'vs/base/parts/tree/browser/tree'; +import { ObjectTree } from 'vs/base/browser/ui/tree/objectTree'; +import { AsyncDataTree } from 'vs/base/browser/ui/tree/asyncDataTree'; +import { DataTree } from 'vs/base/browser/ui/tree/dataTree'; +import { ITreeNode } from 'vs/base/browser/ui/tree/tree'; + +// --- List Commands + +function ensureDOMFocus(widget: ListWidget): void { + // it can happen that one of the commands is executed while + // DOM focus is within another focusable control within the + // list/tree item. therefor we should ensure that the + // list/tree has DOM focus again after the command ran. + if (widget && widget.getHTMLElement() !== document.activeElement) { + widget.domFocus(); + } +} + +function focusDown(accessor: ServicesAccessor, arg2?: number): void { + const focused = accessor.get(IListService).lastFocusedList; + const count = typeof arg2 === 'number' ? arg2 : 1; + + // Ensure DOM Focus + ensureDOMFocus(focused); + + // List + if (focused instanceof List || focused instanceof PagedList) { + const list = focused; + + list.focusNext(count); + const listFocus = list.getFocus(); + if (listFocus.length) { + list.reveal(listFocus[0]); + } + } + + // ObjectTree + else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) { + const tree = focused; + + const fakeKeyboardEvent = new KeyboardEvent('keydown'); + tree.focusNext(count, true, fakeKeyboardEvent); + + const listFocus = tree.getFocus(); + if (listFocus.length) { + tree.reveal(listFocus[0]); + } + } + + // Tree + else if (focused) { + const tree = focused; + + tree.focusNext(count, { origin: 'keyboard' }); + tree.reveal(tree.getFocus()); + } +} + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'list.focusDown', + weight: KeybindingWeight.WorkbenchContrib, + when: WorkbenchListFocusContextKey, + primary: KeyCode.DownArrow, + mac: { + primary: KeyCode.DownArrow, + secondary: [KeyMod.WinCtrl | KeyCode.KEY_N] + }, + handler: (accessor, arg2) => focusDown(accessor, arg2) +}); + +function expandMultiSelection(focused: List | PagedList | ITree | ObjectTree | DataTree | AsyncDataTree, previousFocus: any): void { + + // List + if (focused instanceof List || focused instanceof PagedList) { + const list = focused; + + const focus = list.getFocus() ? list.getFocus()[0] : undefined; + const selection = list.getSelection(); + if (selection && selection.indexOf(focus) >= 0) { + list.setSelection(selection.filter(s => s !== previousFocus)); + } else { + list.setSelection(selection.concat(focus)); + } + } + + // ObjectTree + else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) { + const list = focused; + + const focus = list.getFocus() ? list.getFocus()[0] : undefined; + const selection = list.getSelection(); + const fakeKeyboardEvent = new KeyboardEvent('keydown', { shiftKey: true }); + + if (selection && selection.indexOf(focus) >= 0) { + list.setSelection(selection.filter(s => s !== previousFocus), fakeKeyboardEvent); + } else { + list.setSelection(selection.concat(focus), fakeKeyboardEvent); + } + } + + // Tree + else if (focused) { + const tree = focused; + + const focus = tree.getFocus(); + const selection = tree.getSelection(); + if (selection && selection.indexOf(focus) >= 0) { + tree.setSelection(selection.filter(s => s !== previousFocus)); + } else { + tree.setSelection(selection.concat(focus)); + } + } +} + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'list.expandSelectionDown', + weight: KeybindingWeight.WorkbenchContrib, + when: ContextKeyExpr.and(WorkbenchListFocusContextKey, WorkbenchListSupportsMultiSelectContextKey), + primary: KeyMod.Shift | KeyCode.DownArrow, + handler: (accessor, arg2) => { + const focused = accessor.get(IListService).lastFocusedList; + + // List + if (focused instanceof List || focused instanceof PagedList || focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) { + const list = focused; + + // Focus down first + const previousFocus = list.getFocus() ? list.getFocus()[0] : undefined; + focusDown(accessor, arg2); + + // Then adjust selection + expandMultiSelection(focused, previousFocus); + } + + // Tree + else if (focused) { + const tree = focused; + + // Focus down first + const previousFocus = tree.getFocus(); + focusDown(accessor, arg2); + + // Then adjust selection + expandMultiSelection(focused, previousFocus); + } + } +}); + +function focusUp(accessor: ServicesAccessor, arg2?: number): void { + const focused = accessor.get(IListService).lastFocusedList; + const count = typeof arg2 === 'number' ? arg2 : 1; + + // Ensure DOM Focus + ensureDOMFocus(focused); + + // List + if (focused instanceof List || focused instanceof PagedList) { + const list = focused; + + list.focusPrevious(count); + const listFocus = list.getFocus(); + if (listFocus.length) { + list.reveal(listFocus[0]); + } + } + + // ObjectTree + else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) { + const tree = focused; + + const fakeKeyboardEvent = new KeyboardEvent('keydown'); + tree.focusPrevious(count, true, fakeKeyboardEvent); + + const listFocus = tree.getFocus(); + if (listFocus.length) { + tree.reveal(listFocus[0]); + } + } + + // Tree + else if (focused) { + const tree = focused; + + tree.focusPrevious(count, { origin: 'keyboard' }); + tree.reveal(tree.getFocus()); + } +} + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'list.focusUp', + weight: KeybindingWeight.WorkbenchContrib, + when: WorkbenchListFocusContextKey, + primary: KeyCode.UpArrow, + mac: { + primary: KeyCode.UpArrow, + secondary: [KeyMod.WinCtrl | KeyCode.KEY_P] + }, + handler: (accessor, arg2) => focusUp(accessor, arg2) +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'list.expandSelectionUp', + weight: KeybindingWeight.WorkbenchContrib, + when: ContextKeyExpr.and(WorkbenchListFocusContextKey, WorkbenchListSupportsMultiSelectContextKey), + primary: KeyMod.Shift | KeyCode.UpArrow, + handler: (accessor, arg2) => { + const focused = accessor.get(IListService).lastFocusedList; + + // List + if (focused instanceof List || focused instanceof PagedList || focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) { + const list = focused; + + // Focus up first + const previousFocus = list.getFocus() ? list.getFocus()[0] : undefined; + focusUp(accessor, arg2); + + // Then adjust selection + expandMultiSelection(focused, previousFocus); + } + + // Tree + else if (focused) { + const tree = focused; + + // Focus up first + const previousFocus = tree.getFocus(); + focusUp(accessor, arg2); + + // Then adjust selection + expandMultiSelection(focused, previousFocus); + } + } +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'list.collapse', + weight: KeybindingWeight.WorkbenchContrib, + when: WorkbenchListFocusContextKey, + primary: KeyCode.LeftArrow, + mac: { + primary: KeyCode.LeftArrow, + secondary: [KeyMod.CtrlCmd | KeyCode.UpArrow] + }, + handler: (accessor) => { + const focused = accessor.get(IListService).lastFocusedList; + + // Tree only + if (focused && !(focused instanceof List || focused instanceof PagedList)) { + if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) { + const tree = focused; + const focusedElements = tree.getFocus(); + + if (focusedElements.length === 0) { + return; + } + + const focus = focusedElements[0]; + + if (!tree.collapse(focus)) { + const parent = tree.getParentElement(focus); + + if (parent) { + const fakeKeyboardEvent = new KeyboardEvent('keydown'); + tree.setFocus([parent], fakeKeyboardEvent); + tree.reveal(parent); + } + } + } else { + const tree = focused; + const focus = tree.getFocus(); + + tree.collapse(focus).then(didCollapse => { + if (focus && !didCollapse) { + tree.focusParent({ origin: 'keyboard' }); + + return tree.reveal(tree.getFocus()); + } + + return undefined; + }); + } + } + } +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'list.expand', + weight: KeybindingWeight.WorkbenchContrib, + when: WorkbenchListFocusContextKey, + primary: KeyCode.RightArrow, + handler: (accessor) => { + const focused = accessor.get(IListService).lastFocusedList; + + // Tree only + if (focused && !(focused instanceof List || focused instanceof PagedList)) { + if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) { + const tree = focused; + const focusedElements = tree.getFocus(); + + if (focusedElements.length === 0) { + return; + } + + const focus = focusedElements[0]; + + if (!tree.expand(focus)) { + const child = tree.getFirstElementChild(focus); + + if (child) { + const fakeKeyboardEvent = new KeyboardEvent('keydown'); + tree.setFocus([child], fakeKeyboardEvent); + tree.reveal(child); + } + } + } else { + const tree = focused; + const focus = tree.getFocus(); + + tree.expand(focus).then(didExpand => { + if (focus && !didExpand) { + tree.focusFirstChild({ origin: 'keyboard' }); + + return tree.reveal(tree.getFocus()); + } + + return undefined; + }); + } + } + } +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'list.focusPageUp', + weight: KeybindingWeight.WorkbenchContrib, + when: WorkbenchListFocusContextKey, + primary: KeyCode.PageUp, + handler: (accessor) => { + const focused = accessor.get(IListService).lastFocusedList; + + // Ensure DOM Focus + ensureDOMFocus(focused); + + // List + if (focused instanceof List || focused instanceof PagedList) { + const list = focused; + + list.focusPreviousPage(); + list.reveal(list.getFocus()[0]); + } + + // ObjectTree + else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) { + const list = focused; + + const fakeKeyboardEvent = new KeyboardEvent('keydown'); + list.focusPreviousPage(fakeKeyboardEvent); + list.reveal(list.getFocus()[0]); + } + + // Tree + else if (focused) { + const tree = focused; + + tree.focusPreviousPage({ origin: 'keyboard' }); + tree.reveal(tree.getFocus()); + } + } +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'list.focusPageDown', + weight: KeybindingWeight.WorkbenchContrib, + when: WorkbenchListFocusContextKey, + primary: KeyCode.PageDown, + handler: (accessor) => { + const focused = accessor.get(IListService).lastFocusedList; + + // Ensure DOM Focus + ensureDOMFocus(focused); + + // List + if (focused instanceof List || focused instanceof PagedList) { + const list = focused; + + list.focusNextPage(); + list.reveal(list.getFocus()[0]); + } + + // ObjectTree + else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) { + const list = focused; + + const fakeKeyboardEvent = new KeyboardEvent('keydown'); + list.focusNextPage(fakeKeyboardEvent); + list.reveal(list.getFocus()[0]); + } + + // Tree + else if (focused) { + const tree = focused; + + tree.focusNextPage({ origin: 'keyboard' }); + tree.reveal(tree.getFocus()); + } + } +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'list.focusFirst', + weight: KeybindingWeight.WorkbenchContrib, + when: WorkbenchListFocusContextKey, + primary: KeyCode.Home, + handler: accessor => listFocusFirst(accessor) +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'list.focusFirstChild', + weight: KeybindingWeight.WorkbenchContrib, + when: WorkbenchListFocusContextKey, + primary: 0, + handler: accessor => listFocusFirst(accessor, { fromFocused: true }) +}); + +function listFocusFirst(accessor: ServicesAccessor, options?: { fromFocused: boolean }): void { + const focused = accessor.get(IListService).lastFocusedList; + + // Ensure DOM Focus + ensureDOMFocus(focused); + + // List + if (focused instanceof List || focused instanceof PagedList) { + const list = focused; + + list.setFocus([0]); + list.reveal(0); + } + + // ObjectTree + else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) { + const tree = focused; + const fakeKeyboardEvent = new KeyboardEvent('keydown'); + tree.focusFirst(fakeKeyboardEvent); + + const focus = tree.getFocus(); + + if (focus.length > 0) { + tree.reveal(focus[0]); + } + } + + // Tree + else if (focused) { + const tree = focused; + + tree.focusFirst({ origin: 'keyboard' }, options && options.fromFocused ? tree.getFocus() : undefined); + tree.reveal(tree.getFocus()); + } +} + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'list.focusLast', + weight: KeybindingWeight.WorkbenchContrib, + when: WorkbenchListFocusContextKey, + primary: KeyCode.End, + handler: accessor => listFocusLast(accessor) +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'list.focusLastChild', + weight: KeybindingWeight.WorkbenchContrib, + when: WorkbenchListFocusContextKey, + primary: 0, + handler: accessor => listFocusLast(accessor, { fromFocused: true }) +}); + +function listFocusLast(accessor: ServicesAccessor, options?: { fromFocused: boolean }): void { + const focused = accessor.get(IListService).lastFocusedList; + + // Ensure DOM Focus + ensureDOMFocus(focused); + + // List + if (focused instanceof List || focused instanceof PagedList) { + const list = focused; + + list.setFocus([list.length - 1]); + list.reveal(list.length - 1); + } + + // ObjectTree + else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) { + const tree = focused; + const fakeKeyboardEvent = new KeyboardEvent('keydown'); + tree.focusLast(fakeKeyboardEvent); + + const focus = tree.getFocus(); + + if (focus.length > 0) { + tree.reveal(focus[0]); + } + } + + // Tree + else if (focused) { + const tree = focused; + + tree.focusLast({ origin: 'keyboard' }, options && options.fromFocused ? tree.getFocus() : undefined); + tree.reveal(tree.getFocus()); + } +} + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'list.select', + weight: KeybindingWeight.WorkbenchContrib, + when: WorkbenchListFocusContextKey, + primary: KeyCode.Enter, + mac: { + primary: KeyCode.Enter, + secondary: [KeyMod.CtrlCmd | KeyCode.DownArrow] + }, + handler: (accessor) => { + const focused = accessor.get(IListService).lastFocusedList; + + // List + if (focused instanceof List || focused instanceof PagedList) { + const list = focused; + list.setSelection(list.getFocus()); + list.open(list.getFocus()); + } + + // ObjectTree + else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) { + const list = focused; + const fakeKeyboardEvent = new KeyboardEvent('keydown'); + list.setSelection(list.getFocus(), fakeKeyboardEvent); + list.open(list.getFocus()); + } + + // Tree + else if (focused) { + const tree = focused; + const focus = tree.getFocus(); + + if (focus) { + tree.setSelection([focus], { origin: 'keyboard' }); + } + } + } +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'list.selectAll', + weight: KeybindingWeight.WorkbenchContrib, + when: ContextKeyExpr.and(WorkbenchListFocusContextKey, WorkbenchListSupportsMultiSelectContextKey), + primary: KeyMod.CtrlCmd | KeyCode.KEY_A, + handler: (accessor) => { + const focused = accessor.get(IListService).lastFocusedList; + + // List + if (focused instanceof List || focused instanceof PagedList) { + const list = focused; + list.setSelection(range(list.length)); + } + + // Trees + else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) { + const tree = focused; + const focus = tree.getFocus(); + const selection = tree.getSelection(); + + // Which element should be considered to start selecting all? + let start: any | undefined = undefined; + + if (focus.length > 0 && (selection.length === 0 || selection.indexOf(focus[0]) === -1)) { + start = focus[0]; + } + + if (!start && selection.length > 0) { + start = selection[0]; + } + + // What is the scope of select all? + let scope: any | undefined = undefined; + + if (!start) { + scope = undefined; + } else { + const selectedNode = tree.getNode(start); + const parentNode = selectedNode.parent; + + if (!parentNode.parent) { // root + scope = undefined; + } else { + scope = parentNode.element; + } + } + + const newSelection: any[] = []; + + // If the scope isn't the tree root, it should be part of the new selection + if (scope) { + newSelection.push(scope); + } + + const visit = (node: ITreeNode) => { + for (const child of node.children) { + if (child.visible) { + newSelection.push(child.element); + + if (!child.collapsed) { + visit(child); + } + } + } + }; + + // Add the whole scope subtree to the new selection + visit(tree.getNode(scope)); + + const fakeKeyboardEvent = new KeyboardEvent('keydown'); + tree.setSelection(newSelection, fakeKeyboardEvent); + } + } +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'list.toggleExpand', + weight: KeybindingWeight.WorkbenchContrib, + when: WorkbenchListFocusContextKey, + primary: KeyCode.Space, + handler: (accessor) => { + const focused = accessor.get(IListService).lastFocusedList; + + // Tree only + if (focused && !(focused instanceof List || focused instanceof PagedList)) { + if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) { + const tree = focused; + const focus = tree.getFocus(); + + if (focus.length === 0) { + return; + } + + tree.toggleCollapsed(focus[0]); + } else { + const tree = focused; + const focus = tree.getFocus(); + + if (focus) { + tree.toggleExpansion(focus); + } + } + } + } +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'list.clear', + weight: KeybindingWeight.WorkbenchContrib, + when: ContextKeyExpr.and(WorkbenchListFocusContextKey, WorkbenchListHasSelectionOrFocus), + primary: KeyCode.Escape, + handler: (accessor) => { + const focused = accessor.get(IListService).lastFocusedList; + + // List + if (focused instanceof List || focused instanceof PagedList) { + const list = focused; + + if (list.getSelection().length > 0) { + list.setSelection([]); + } else if (list.getFocus().length > 0) { + list.setFocus([]); + } + } + + // ObjectTree + else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) { + const list = focused; + const fakeKeyboardEvent = new KeyboardEvent('keydown'); + + if (list.getSelection().length > 0) { + list.setSelection([], fakeKeyboardEvent); + } else if (list.getFocus().length > 0) { + list.setFocus([], fakeKeyboardEvent); + } + } + + // Tree + else if (focused) { + const tree = focused; + + if (tree.getSelection().length) { + tree.clearSelection({ origin: 'keyboard' }); + } else if (tree.getFocus()) { + tree.clearFocus({ origin: 'keyboard' }); + } + } + } +}); \ No newline at end of file diff --git a/src/vs/workbench/browser/actions/workspaceActions.ts b/src/vs/workbench/browser/actions/workspaceActions.ts index a2b9b6e36cf..e37b7cdeb80 100644 --- a/src/vs/workbench/browser/actions/workspaceActions.ts +++ b/src/vs/workbench/browser/actions/workspaceActions.ts @@ -17,6 +17,7 @@ import { ADD_ROOT_FOLDER_COMMAND_ID, ADD_ROOT_FOLDER_LABEL, PICK_WORKSPACE_FOLDE import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { INotificationService } from 'vs/platform/notification/common/notification'; export class OpenFileAction extends Action { @@ -184,6 +185,32 @@ export class OpenWorkspaceAction extends Action { } } +export class CloseWorkspaceAction extends Action { + + static readonly ID = 'workbench.action.closeFolder'; + static LABEL = nls.localize('closeWorkspace', "Close Workspace"); + + constructor( + id: string, + label: string, + @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, + @INotificationService private readonly notificationService: INotificationService, + @IWindowService private readonly windowService: IWindowService + ) { + super(id, label); + } + + run(): Promise { + if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) { + this.notificationService.info(nls.localize('noWorkspaceOpened', "There is currently no workspace opened in this instance to close.")); + + return Promise.resolve(undefined); + } + + return this.windowService.closeWorkspace(); + } +} + export class OpenWorkspaceConfigFileAction extends Action { static readonly ID = 'workbench.action.openWorkspaceConfigFile'; diff --git a/src/vs/workbench/electron-browser/actions.ts b/src/vs/workbench/electron-browser/actions.ts index a2538660366..d13c1a42739 100644 --- a/src/vs/workbench/electron-browser/actions.ts +++ b/src/vs/workbench/electron-browser/actions.ts @@ -5,27 +5,19 @@ import 'vs/css!./media/actions'; -import { URI } from 'vs/base/common/uri'; import { Action } from 'vs/base/common/actions'; -import { IWindowService, IWindowsService, MenuBarVisibility } from 'vs/platform/windows/common/windows'; +import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows'; import * as nls from 'vs/nls'; import product from 'vs/platform/node/product'; -import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; -import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; -import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { isMacintosh, isLinux, language } from 'vs/base/common/platform'; -import * as browser from 'vs/base/browser/browser'; import { IEditorGroupsService, GroupDirection, GroupLocation, IFindGroupScope } from 'vs/workbench/services/group/common/editorGroupsService'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IPartService, Parts, Position as PartPosition } from 'vs/workbench/services/part/common/partService'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { webFrame, shell } from 'electron'; -import { getBaseLabel } from 'vs/base/common/labels'; +import { shell } from 'electron'; import { IViewlet } from 'vs/workbench/common/viewlet'; import { IPanel } from 'vs/workbench/common/panel'; -import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; -import { FileKind } from 'vs/platform/files/common/files'; import { IssueType } from 'vs/platform/issue/common/issue'; import { domEvent } from 'vs/base/browser/event'; import { Event } from 'vs/base/common/event'; @@ -34,516 +26,11 @@ import { getDomNodePagePosition, createStyleSheet, createCSSRule, append, $ } fr import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { Context } from 'vs/platform/contextkey/browser/contextKeyService'; import { IWorkbenchIssueService } from 'vs/workbench/services/issue/common/issue'; -import { INotificationService } from 'vs/platform/notification/common/notification'; -import { ILabelService } from 'vs/platform/label/common/label'; -import { dirname } from 'vs/base/common/resources'; -import { IModelService } from 'vs/editor/common/services/modelService'; -import { IModeService } from 'vs/editor/common/services/modeService'; -import { IQuickInputService, IQuickPickItem, IQuickInputButton, IQuickPickSeparator, IKeyMods } from 'vs/platform/quickinput/common/quickInput'; -import { getIconClasses } from 'vs/editor/common/services/getIconClasses'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { timeout } from 'vs/base/common/async'; // --- actions -export class CloseCurrentWindowAction extends Action { - - static readonly ID = 'workbench.action.closeWindow'; - static readonly LABEL = nls.localize('closeWindow', "Close Window"); - - constructor(id: string, label: string, @IWindowService private readonly windowService: IWindowService) { - super(id, label); - } - - run(): Promise { - this.windowService.closeWindow(); - - return Promise.resolve(true); - } -} - -export class CloseWorkspaceAction extends Action { - - static readonly ID = 'workbench.action.closeFolder'; - static LABEL = nls.localize('closeWorkspace', "Close Workspace"); - - constructor( - id: string, - label: string, - @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, - @INotificationService private readonly notificationService: INotificationService, - @IWindowService private readonly windowService: IWindowService - ) { - super(id, label); - } - - run(): Promise { - if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) { - this.notificationService.info(nls.localize('noWorkspaceOpened', "There is currently no workspace opened in this instance to close.")); - - return Promise.resolve(undefined); - } - - return this.windowService.closeWorkspace(); - } -} - -export class NewWindowAction extends Action { - - static readonly ID = 'workbench.action.newWindow'; - static LABEL = nls.localize('newWindow', "New Window"); - - constructor( - id: string, - label: string, - @IWindowsService private readonly windowsService: IWindowsService - ) { - super(id, label); - } - - run(): Promise { - return this.windowsService.openNewWindow(); - } -} - -export class ToggleFullScreenAction extends Action { - - static readonly ID = 'workbench.action.toggleFullScreen'; - static LABEL = nls.localize('toggleFullScreen', "Toggle Full Screen"); - - constructor(id: string, label: string, @IWindowService private readonly windowService: IWindowService) { - super(id, label); - } - - run(): Promise { - return this.windowService.toggleFullScreen(); - } -} - -export class ToggleMenuBarAction extends Action { - - static readonly ID = 'workbench.action.toggleMenuBar'; - static LABEL = nls.localize('toggleMenuBar', "Toggle Menu Bar"); - - private static readonly menuBarVisibilityKey = 'window.menuBarVisibility'; - - constructor( - id: string, - label: string, - @IConfigurationService private readonly configurationService: IConfigurationService - ) { - super(id, label); - } - - run(): Promise { - let currentVisibilityValue = this.configurationService.getValue(ToggleMenuBarAction.menuBarVisibilityKey); - if (typeof currentVisibilityValue !== 'string') { - currentVisibilityValue = 'default'; - } - - let newVisibilityValue: string; - if (currentVisibilityValue === 'visible' || currentVisibilityValue === 'default') { - newVisibilityValue = 'toggle'; - } else { - newVisibilityValue = 'default'; - } - - this.configurationService.updateValue(ToggleMenuBarAction.menuBarVisibilityKey, newVisibilityValue, ConfigurationTarget.USER); - - return Promise.resolve(); - } -} - -export class ToggleDevToolsAction extends Action { - - static readonly ID = 'workbench.action.toggleDevTools'; - static LABEL = nls.localize('toggleDevTools', "Toggle Developer Tools"); - - constructor(id: string, label: string, @IWindowService private readonly windowsService: IWindowService) { - super(id, label); - } - - run(): Promise { - return this.windowsService.toggleDevTools(); - } -} - -export abstract class BaseZoomAction extends Action { - private static readonly SETTING_KEY = 'window.zoomLevel'; - - constructor( - id: string, - label: string, - @IWorkspaceConfigurationService private readonly configurationService: IWorkspaceConfigurationService - ) { - super(id, label); - } - - protected setConfiguredZoomLevel(level: number): void { - level = Math.round(level); // when reaching smallest zoom, prevent fractional zoom levels - - const applyZoom = () => { - webFrame.setZoomLevel(level); - browser.setZoomFactor(webFrame.getZoomFactor()); - // See https://github.com/Microsoft/vscode/issues/26151 - // Cannot be trusted because the webFrame might take some time - // until it really applies the new zoom level - browser.setZoomLevel(webFrame.getZoomLevel(), /*isTrusted*/false); - }; - - this.configurationService.updateValue(BaseZoomAction.SETTING_KEY, level).then(() => applyZoom()); - } -} - -export class ZoomInAction extends BaseZoomAction { - - static readonly ID = 'workbench.action.zoomIn'; - static readonly LABEL = nls.localize('zoomIn', "Zoom In"); - - constructor( - id: string, - label: string, - @IWorkspaceConfigurationService configurationService: IWorkspaceConfigurationService - ) { - super(id, label, configurationService); - } - - run(): Promise { - this.setConfiguredZoomLevel(webFrame.getZoomLevel() + 1); - - return Promise.resolve(true); - } -} - -export class ZoomOutAction extends BaseZoomAction { - - static readonly ID = 'workbench.action.zoomOut'; - static readonly LABEL = nls.localize('zoomOut', "Zoom Out"); - - constructor( - id: string, - label: string, - @IWorkspaceConfigurationService configurationService: IWorkspaceConfigurationService - ) { - super(id, label, configurationService); - } - - run(): Promise { - this.setConfiguredZoomLevel(webFrame.getZoomLevel() - 1); - - return Promise.resolve(true); - } -} - -export class ZoomResetAction extends BaseZoomAction { - - static readonly ID = 'workbench.action.zoomReset'; - static readonly LABEL = nls.localize('zoomReset', "Reset Zoom"); - - constructor( - id: string, - label: string, - @IWorkspaceConfigurationService configurationService: IWorkspaceConfigurationService - ) { - super(id, label, configurationService); - } - - run(): Promise { - this.setConfiguredZoomLevel(0); - - return Promise.resolve(true); - } -} - -export class ReloadWindowAction extends Action { - - static readonly ID = 'workbench.action.reloadWindow'; - static LABEL = nls.localize('reloadWindow', "Reload Window"); - - constructor( - id: string, - label: string, - @IWindowService private readonly windowService: IWindowService - ) { - super(id, label); - } - - run(): Promise { - return this.windowService.reloadWindow().then(() => true); - } -} - -export class ReloadWindowWithExtensionsDisabledAction extends Action { - - static readonly ID = 'workbench.action.reloadWindowWithExtensionsDisabled'; - static LABEL = nls.localize('reloadWindowWithExntesionsDisabled', "Reload Window With Extensions Disabled"); - - constructor( - id: string, - label: string, - @IWindowService private readonly windowService: IWindowService - ) { - super(id, label); - } - - run(): Promise { - return this.windowService.reloadWindow({ _: [], 'disable-extensions': true }).then(() => true); - } -} - -export abstract class BaseSwitchWindow extends Action { - - private closeWindowAction: IQuickInputButton = { - iconClass: 'action-remove-from-recently-opened', - tooltip: nls.localize('close', "Close Window") - }; - - constructor( - id: string, - label: string, - private windowsService: IWindowsService, - private windowService: IWindowService, - private quickInputService: IQuickInputService, - private keybindingService: IKeybindingService, - private modelService: IModelService, - private modeService: IModeService, - ) { - super(id, label); - - } - - protected abstract isQuickNavigate(): boolean; - - run(): Promise { - const currentWindowId = this.windowService.getCurrentWindowId(); - - return this.windowsService.getWindows().then(windows => { - const placeHolder = nls.localize('switchWindowPlaceHolder', "Select a window to switch to"); - const picks = windows.map(win => { - const resource = win.filename ? URI.file(win.filename) : win.folderUri ? win.folderUri : win.workspace ? URI.file(win.workspace.configPath) : undefined; - const fileKind = win.filename ? FileKind.FILE : win.workspace ? FileKind.ROOT_FOLDER : win.folderUri ? FileKind.FOLDER : FileKind.FILE; - return { - payload: win.id, - label: win.title, - iconClasses: getIconClasses(this.modelService, this.modeService, resource, fileKind), - description: (currentWindowId === win.id) ? nls.localize('current', "Current Window") : undefined, - buttons: (!this.isQuickNavigate() && currentWindowId !== win.id) ? [this.closeWindowAction] : undefined - } as (IQuickPickItem & { payload: number }); - }); - - const autoFocusIndex = (picks.indexOf(picks.filter(pick => pick.payload === currentWindowId)[0]) + 1) % picks.length; - - return this.quickInputService.pick(picks, { - contextKey: 'inWindowsPicker', - activeItem: picks[autoFocusIndex], - placeHolder, - quickNavigate: this.isQuickNavigate() ? { keybindings: this.keybindingService.lookupKeybindings(this.id) } : undefined, - onDidTriggerItemButton: context => { - this.windowsService.closeWindow(context.item.payload).then(() => { - context.removeItem(); - }); - } - }); - }).then(pick => { - if (pick) { - this.windowsService.showWindow(pick.payload); - } - }); - } -} - -export class SwitchWindow extends BaseSwitchWindow { - - static readonly ID = 'workbench.action.switchWindow'; - static LABEL = nls.localize('switchWindow', "Switch Window..."); - - constructor( - id: string, - label: string, - @IWindowsService windowsService: IWindowsService, - @IWindowService windowService: IWindowService, - @IQuickInputService quickInputService: IQuickInputService, - @IKeybindingService keybindingService: IKeybindingService, - @IModelService modelService: IModelService, - @IModeService modeService: IModeService, - ) { - super(id, label, windowsService, windowService, quickInputService, keybindingService, modelService, modeService); - } - - protected isQuickNavigate(): boolean { - return false; - } -} - -export class QuickSwitchWindow extends BaseSwitchWindow { - - static readonly ID = 'workbench.action.quickSwitchWindow'; - static LABEL = nls.localize('quickSwitchWindow', "Quick Switch Window..."); - - constructor( - id: string, - label: string, - @IWindowsService windowsService: IWindowsService, - @IWindowService windowService: IWindowService, - @IQuickInputService quickInputService: IQuickInputService, - @IKeybindingService keybindingService: IKeybindingService, - @IModelService modelService: IModelService, - @IModeService modeService: IModeService, - ) { - super(id, label, windowsService, windowService, quickInputService, keybindingService, modelService, modeService); - } - - protected isQuickNavigate(): boolean { - return true; - } -} - -export const inRecentFilesPickerContextKey = 'inRecentFilesPicker'; - -export abstract class BaseOpenRecentAction extends Action { - - private removeFromRecentlyOpened: IQuickInputButton = { - iconClass: 'action-remove-from-recently-opened', - tooltip: nls.localize('remove', "Remove from Recently Opened") - }; - - constructor( - id: string, - label: string, - private windowService: IWindowService, - private windowsService: IWindowsService, - private quickInputService: IQuickInputService, - private contextService: IWorkspaceContextService, - private labelService: ILabelService, - private keybindingService: IKeybindingService, - private modelService: IModelService, - private modeService: IModeService, - ) { - super(id, label); - } - - protected abstract isQuickNavigate(): boolean; - - run(): Promise { - return this.windowService.getRecentlyOpened() - .then(({ workspaces, files }) => this.openRecent(workspaces, files)); - } - - private openRecent(recentWorkspaces: Array, recentFiles: URI[]): void { - - const toPick = (workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | URI, fileKind: FileKind, labelService: ILabelService, buttons: IQuickInputButton[] | undefined) => { - let resource: URI; - let label: string; - let description: string; - if (isSingleFolderWorkspaceIdentifier(workspace) && fileKind !== FileKind.FILE) { - resource = workspace; - label = labelService.getWorkspaceLabel(workspace); - description = labelService.getUriLabel(dirname(resource)!); - } else if (isWorkspaceIdentifier(workspace)) { - resource = URI.file(workspace.configPath); - label = labelService.getWorkspaceLabel(workspace); - description = labelService.getUriLabel(dirname(resource)!); - } else { - resource = workspace; - label = getBaseLabel(workspace); - description = labelService.getUriLabel(dirname(resource)!); - } - - return { - iconClasses: getIconClasses(this.modelService, this.modeService, resource, fileKind), - label, - description, - buttons, - workspace, - resource, - fileKind, - }; - }; - - const runPick = (resource: URI, isFile: boolean, keyMods: IKeyMods) => { - const forceNewWindow = keyMods.ctrlCmd; - return this.windowService.openWindow([resource], { forceNewWindow, forceOpenWorkspaceAsFile: isFile }); - }; - - const workspacePicks = recentWorkspaces.map(workspace => toPick(workspace, isSingleFolderWorkspaceIdentifier(workspace) ? FileKind.FOLDER : FileKind.ROOT_FOLDER, this.labelService, !this.isQuickNavigate() ? [this.removeFromRecentlyOpened] : undefined)); - const filePicks = recentFiles.map(p => toPick(p, FileKind.FILE, this.labelService, !this.isQuickNavigate() ? [this.removeFromRecentlyOpened] : undefined)); - - // focus second entry if the first recent workspace is the current workspace - let autoFocusSecondEntry: boolean = recentWorkspaces[0] && this.contextService.isCurrentWorkspace(recentWorkspaces[0]); - - let keyMods: IKeyMods; - const workspaceSeparator: IQuickPickSeparator = { type: 'separator', label: nls.localize('workspaces', "workspaces") }; - const fileSeparator: IQuickPickSeparator = { type: 'separator', label: nls.localize('files', "files") }; - const picks = [workspaceSeparator, ...workspacePicks, fileSeparator, ...filePicks]; - this.quickInputService.pick(picks, { - contextKey: inRecentFilesPickerContextKey, - activeItem: [...workspacePicks, ...filePicks][autoFocusSecondEntry ? 1 : 0], - placeHolder: isMacintosh ? nls.localize('openRecentPlaceHolderMac', "Select to open (hold Cmd-key to open in new window)") : nls.localize('openRecentPlaceHolder', "Select to open (hold Ctrl-key to open in new window)"), - matchOnDescription: true, - onKeyMods: mods => keyMods = mods, - quickNavigate: this.isQuickNavigate() ? { keybindings: this.keybindingService.lookupKeybindings(this.id) } : undefined, - onDidTriggerItemButton: context => { - this.windowsService.removeFromRecentlyOpened([context.item.workspace]).then(() => context.removeItem()); - } - }) - .then((pick): Promise | void => { - if (pick) { - return runPick(pick.resource, pick.fileKind === FileKind.FILE, keyMods); - } - }); - } -} - -export class OpenRecentAction extends BaseOpenRecentAction { - - static readonly ID = 'workbench.action.openRecent'; - static readonly LABEL = nls.localize('openRecent', "Open Recent..."); - - constructor( - id: string, - label: string, - @IWindowService windowService: IWindowService, - @IWindowsService windowsService: IWindowsService, - @IQuickInputService quickInputService: IQuickInputService, - @IWorkspaceContextService contextService: IWorkspaceContextService, - @IKeybindingService keybindingService: IKeybindingService, - @IModelService modelService: IModelService, - @IModeService modeService: IModeService, - @ILabelService labelService: ILabelService - ) { - super(id, label, windowService, windowsService, quickInputService, contextService, labelService, keybindingService, modelService, modeService); - } - - protected isQuickNavigate(): boolean { - return false; - } -} - -export class QuickOpenRecentAction extends BaseOpenRecentAction { - - static readonly ID = 'workbench.action.quickOpenRecent'; - static readonly LABEL = nls.localize('quickOpenRecent', "Quick Open Recent..."); - - constructor( - id: string, - label: string, - @IWindowService windowService: IWindowService, - @IWindowsService windowsService: IWindowsService, - @IQuickInputService quickInputService: IQuickInputService, - @IWorkspaceContextService contextService: IWorkspaceContextService, - @IKeybindingService keybindingService: IKeybindingService, - @IModelService modelService: IModelService, - @IModeService modeService: IModeService, - @ILabelService labelService: ILabelService - ) { - super(id, label, windowService, windowsService, quickInputService, contextService, labelService, keybindingService, modelService, modeService); - } - - protected isQuickNavigate(): boolean { - return true; - } -} - export class OpenIssueReporterAction extends Action { static readonly ID = 'workbench.action.openIssueReporter'; static readonly LABEL = nls.localize({ key: 'reportIssueInEnglish', comment: ['Translate this to "Report Issue in English" in all languages please!'] }, "Report Issue"); @@ -1200,7 +687,6 @@ export class OpenLicenseUrlAction extends Action { } } - export class OpenPrivacyStatementUrlAction extends Action { static readonly ID = 'workbench.action.openPrivacyStatementUrl'; diff --git a/src/vs/workbench/electron-browser/media/actions.css b/src/vs/workbench/electron-browser/actions/media/actions.css similarity index 100% rename from src/vs/workbench/electron-browser/media/actions.css rename to src/vs/workbench/electron-browser/actions/media/actions.css diff --git a/src/vs/workbench/electron-browser/media/remove-dark.svg b/src/vs/workbench/electron-browser/actions/media/remove-dark.svg similarity index 100% rename from src/vs/workbench/electron-browser/media/remove-dark.svg rename to src/vs/workbench/electron-browser/actions/media/remove-dark.svg diff --git a/src/vs/workbench/electron-browser/media/remove.svg b/src/vs/workbench/electron-browser/actions/media/remove.svg similarity index 100% rename from src/vs/workbench/electron-browser/media/remove.svg rename to src/vs/workbench/electron-browser/actions/media/remove.svg diff --git a/src/vs/workbench/electron-browser/actions/windowActions.ts b/src/vs/workbench/electron-browser/actions/windowActions.ts new file mode 100644 index 00000000000..2bea402ef54 --- /dev/null +++ b/src/vs/workbench/electron-browser/actions/windowActions.ts @@ -0,0 +1,466 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'vs/css!./media/actions'; + +import { URI } from 'vs/base/common/uri'; +import { Action } from 'vs/base/common/actions'; +import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows'; +import * as nls from 'vs/nls'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; +import { isMacintosh } from 'vs/base/common/platform'; +import * as browser from 'vs/base/browser/browser'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { webFrame } from 'electron'; +import { getBaseLabel } from 'vs/base/common/labels'; +import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { FileKind } from 'vs/platform/files/common/files'; +import { ILabelService } from 'vs/platform/label/common/label'; +import { dirname } from 'vs/base/common/resources'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { IQuickInputService, IQuickPickItem, IQuickInputButton, IQuickPickSeparator, IKeyMods } from 'vs/platform/quickinput/common/quickInput'; +import { getIconClasses } from 'vs/editor/common/services/getIconClasses'; + +// --- actions + +export class CloseCurrentWindowAction extends Action { + + static readonly ID = 'workbench.action.closeWindow'; + static readonly LABEL = nls.localize('closeWindow', "Close Window"); + + constructor(id: string, label: string, @IWindowService private readonly windowService: IWindowService) { + super(id, label); + } + + run(): Promise { + this.windowService.closeWindow(); + + return Promise.resolve(true); + } +} + +export class NewWindowAction extends Action { + + static readonly ID = 'workbench.action.newWindow'; + static LABEL = nls.localize('newWindow', "New Window"); + + constructor( + id: string, + label: string, + @IWindowsService private readonly windowsService: IWindowsService + ) { + super(id, label); + } + + run(): Promise { + return this.windowsService.openNewWindow(); + } +} + +export class ToggleFullScreenAction extends Action { + + static readonly ID = 'workbench.action.toggleFullScreen'; + static LABEL = nls.localize('toggleFullScreen', "Toggle Full Screen"); + + constructor(id: string, label: string, @IWindowService private readonly windowService: IWindowService) { + super(id, label); + } + + run(): Promise { + return this.windowService.toggleFullScreen(); + } +} + +export class ToggleDevToolsAction extends Action { + + static readonly ID = 'workbench.action.toggleDevTools'; + static LABEL = nls.localize('toggleDevTools', "Toggle Developer Tools"); + + constructor(id: string, label: string, @IWindowService private readonly windowsService: IWindowService) { + super(id, label); + } + + run(): Promise { + return this.windowsService.toggleDevTools(); + } +} + +export abstract class BaseZoomAction extends Action { + private static readonly SETTING_KEY = 'window.zoomLevel'; + + constructor( + id: string, + label: string, + @IWorkspaceConfigurationService private readonly configurationService: IWorkspaceConfigurationService + ) { + super(id, label); + } + + protected setConfiguredZoomLevel(level: number): void { + level = Math.round(level); // when reaching smallest zoom, prevent fractional zoom levels + + const applyZoom = () => { + webFrame.setZoomLevel(level); + browser.setZoomFactor(webFrame.getZoomFactor()); + // See https://github.com/Microsoft/vscode/issues/26151 + // Cannot be trusted because the webFrame might take some time + // until it really applies the new zoom level + browser.setZoomLevel(webFrame.getZoomLevel(), /*isTrusted*/false); + }; + + this.configurationService.updateValue(BaseZoomAction.SETTING_KEY, level).then(() => applyZoom()); + } +} + +export class ZoomInAction extends BaseZoomAction { + + static readonly ID = 'workbench.action.zoomIn'; + static readonly LABEL = nls.localize('zoomIn', "Zoom In"); + + constructor( + id: string, + label: string, + @IWorkspaceConfigurationService configurationService: IWorkspaceConfigurationService + ) { + super(id, label, configurationService); + } + + run(): Promise { + this.setConfiguredZoomLevel(webFrame.getZoomLevel() + 1); + + return Promise.resolve(true); + } +} + +export class ZoomOutAction extends BaseZoomAction { + + static readonly ID = 'workbench.action.zoomOut'; + static readonly LABEL = nls.localize('zoomOut', "Zoom Out"); + + constructor( + id: string, + label: string, + @IWorkspaceConfigurationService configurationService: IWorkspaceConfigurationService + ) { + super(id, label, configurationService); + } + + run(): Promise { + this.setConfiguredZoomLevel(webFrame.getZoomLevel() - 1); + + return Promise.resolve(true); + } +} + +export class ZoomResetAction extends BaseZoomAction { + + static readonly ID = 'workbench.action.zoomReset'; + static readonly LABEL = nls.localize('zoomReset', "Reset Zoom"); + + constructor( + id: string, + label: string, + @IWorkspaceConfigurationService configurationService: IWorkspaceConfigurationService + ) { + super(id, label, configurationService); + } + + run(): Promise { + this.setConfiguredZoomLevel(0); + + return Promise.resolve(true); + } +} + +export class ReloadWindowAction extends Action { + + static readonly ID = 'workbench.action.reloadWindow'; + static LABEL = nls.localize('reloadWindow', "Reload Window"); + + constructor( + id: string, + label: string, + @IWindowService private readonly windowService: IWindowService + ) { + super(id, label); + } + + run(): Promise { + return this.windowService.reloadWindow().then(() => true); + } +} + +export class ReloadWindowWithExtensionsDisabledAction extends Action { + + static readonly ID = 'workbench.action.reloadWindowWithExtensionsDisabled'; + static LABEL = nls.localize('reloadWindowWithExntesionsDisabled', "Reload Window With Extensions Disabled"); + + constructor( + id: string, + label: string, + @IWindowService private readonly windowService: IWindowService + ) { + super(id, label); + } + + run(): Promise { + return this.windowService.reloadWindow({ _: [], 'disable-extensions': true }).then(() => true); + } +} + +export abstract class BaseSwitchWindow extends Action { + + private closeWindowAction: IQuickInputButton = { + iconClass: 'action-remove-from-recently-opened', + tooltip: nls.localize('close', "Close Window") + }; + + constructor( + id: string, + label: string, + private windowsService: IWindowsService, + private windowService: IWindowService, + private quickInputService: IQuickInputService, + private keybindingService: IKeybindingService, + private modelService: IModelService, + private modeService: IModeService, + ) { + super(id, label); + + } + + protected abstract isQuickNavigate(): boolean; + + run(): Promise { + const currentWindowId = this.windowService.getCurrentWindowId(); + + return this.windowsService.getWindows().then(windows => { + const placeHolder = nls.localize('switchWindowPlaceHolder', "Select a window to switch to"); + const picks = windows.map(win => { + const resource = win.filename ? URI.file(win.filename) : win.folderUri ? win.folderUri : win.workspace ? URI.file(win.workspace.configPath) : undefined; + const fileKind = win.filename ? FileKind.FILE : win.workspace ? FileKind.ROOT_FOLDER : win.folderUri ? FileKind.FOLDER : FileKind.FILE; + return { + payload: win.id, + label: win.title, + iconClasses: getIconClasses(this.modelService, this.modeService, resource, fileKind), + description: (currentWindowId === win.id) ? nls.localize('current', "Current Window") : undefined, + buttons: (!this.isQuickNavigate() && currentWindowId !== win.id) ? [this.closeWindowAction] : undefined + } as (IQuickPickItem & { payload: number }); + }); + + const autoFocusIndex = (picks.indexOf(picks.filter(pick => pick.payload === currentWindowId)[0]) + 1) % picks.length; + + return this.quickInputService.pick(picks, { + contextKey: 'inWindowsPicker', + activeItem: picks[autoFocusIndex], + placeHolder, + quickNavigate: this.isQuickNavigate() ? { keybindings: this.keybindingService.lookupKeybindings(this.id) } : undefined, + onDidTriggerItemButton: context => { + this.windowsService.closeWindow(context.item.payload).then(() => { + context.removeItem(); + }); + } + }); + }).then(pick => { + if (pick) { + this.windowsService.showWindow(pick.payload); + } + }); + } +} + +export class SwitchWindow extends BaseSwitchWindow { + + static readonly ID = 'workbench.action.switchWindow'; + static LABEL = nls.localize('switchWindow', "Switch Window..."); + + constructor( + id: string, + label: string, + @IWindowsService windowsService: IWindowsService, + @IWindowService windowService: IWindowService, + @IQuickInputService quickInputService: IQuickInputService, + @IKeybindingService keybindingService: IKeybindingService, + @IModelService modelService: IModelService, + @IModeService modeService: IModeService, + ) { + super(id, label, windowsService, windowService, quickInputService, keybindingService, modelService, modeService); + } + + protected isQuickNavigate(): boolean { + return false; + } +} + +export class QuickSwitchWindow extends BaseSwitchWindow { + + static readonly ID = 'workbench.action.quickSwitchWindow'; + static LABEL = nls.localize('quickSwitchWindow', "Quick Switch Window..."); + + constructor( + id: string, + label: string, + @IWindowsService windowsService: IWindowsService, + @IWindowService windowService: IWindowService, + @IQuickInputService quickInputService: IQuickInputService, + @IKeybindingService keybindingService: IKeybindingService, + @IModelService modelService: IModelService, + @IModeService modeService: IModeService, + ) { + super(id, label, windowsService, windowService, quickInputService, keybindingService, modelService, modeService); + } + + protected isQuickNavigate(): boolean { + return true; + } +} + +export const inRecentFilesPickerContextKey = 'inRecentFilesPicker'; + +export abstract class BaseOpenRecentAction extends Action { + + private removeFromRecentlyOpened: IQuickInputButton = { + iconClass: 'action-remove-from-recently-opened', + tooltip: nls.localize('remove', "Remove from Recently Opened") + }; + + constructor( + id: string, + label: string, + private windowService: IWindowService, + private windowsService: IWindowsService, + private quickInputService: IQuickInputService, + private contextService: IWorkspaceContextService, + private labelService: ILabelService, + private keybindingService: IKeybindingService, + private modelService: IModelService, + private modeService: IModeService, + ) { + super(id, label); + } + + protected abstract isQuickNavigate(): boolean; + + run(): Promise { + return this.windowService.getRecentlyOpened() + .then(({ workspaces, files }) => this.openRecent(workspaces, files)); + } + + private openRecent(recentWorkspaces: Array, recentFiles: URI[]): void { + + const toPick = (workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | URI, fileKind: FileKind, labelService: ILabelService, buttons: IQuickInputButton[] | undefined) => { + let resource: URI; + let label: string; + let description: string; + if (isSingleFolderWorkspaceIdentifier(workspace) && fileKind !== FileKind.FILE) { + resource = workspace; + label = labelService.getWorkspaceLabel(workspace); + description = labelService.getUriLabel(dirname(resource)!); + } else if (isWorkspaceIdentifier(workspace)) { + resource = URI.file(workspace.configPath); + label = labelService.getWorkspaceLabel(workspace); + description = labelService.getUriLabel(dirname(resource)!); + } else { + resource = workspace; + label = getBaseLabel(workspace); + description = labelService.getUriLabel(dirname(resource)!); + } + + return { + iconClasses: getIconClasses(this.modelService, this.modeService, resource, fileKind), + label, + description, + buttons, + workspace, + resource, + fileKind, + }; + }; + + const runPick = (resource: URI, isFile: boolean, keyMods: IKeyMods) => { + const forceNewWindow = keyMods.ctrlCmd; + return this.windowService.openWindow([resource], { forceNewWindow, forceOpenWorkspaceAsFile: isFile }); + }; + + const workspacePicks = recentWorkspaces.map(workspace => toPick(workspace, isSingleFolderWorkspaceIdentifier(workspace) ? FileKind.FOLDER : FileKind.ROOT_FOLDER, this.labelService, !this.isQuickNavigate() ? [this.removeFromRecentlyOpened] : undefined)); + const filePicks = recentFiles.map(p => toPick(p, FileKind.FILE, this.labelService, !this.isQuickNavigate() ? [this.removeFromRecentlyOpened] : undefined)); + + // focus second entry if the first recent workspace is the current workspace + let autoFocusSecondEntry: boolean = recentWorkspaces[0] && this.contextService.isCurrentWorkspace(recentWorkspaces[0]); + + let keyMods: IKeyMods; + const workspaceSeparator: IQuickPickSeparator = { type: 'separator', label: nls.localize('workspaces', "workspaces") }; + const fileSeparator: IQuickPickSeparator = { type: 'separator', label: nls.localize('files', "files") }; + const picks = [workspaceSeparator, ...workspacePicks, fileSeparator, ...filePicks]; + this.quickInputService.pick(picks, { + contextKey: inRecentFilesPickerContextKey, + activeItem: [...workspacePicks, ...filePicks][autoFocusSecondEntry ? 1 : 0], + placeHolder: isMacintosh ? nls.localize('openRecentPlaceHolderMac', "Select to open (hold Cmd-key to open in new window)") : nls.localize('openRecentPlaceHolder', "Select to open (hold Ctrl-key to open in new window)"), + matchOnDescription: true, + onKeyMods: mods => keyMods = mods, + quickNavigate: this.isQuickNavigate() ? { keybindings: this.keybindingService.lookupKeybindings(this.id) } : undefined, + onDidTriggerItemButton: context => { + this.windowsService.removeFromRecentlyOpened([context.item.workspace]).then(() => context.removeItem()); + } + }) + .then((pick): Promise | void => { + if (pick) { + return runPick(pick.resource, pick.fileKind === FileKind.FILE, keyMods); + } + }); + } +} + +export class OpenRecentAction extends BaseOpenRecentAction { + + static readonly ID = 'workbench.action.openRecent'; + static readonly LABEL = nls.localize('openRecent', "Open Recent..."); + + constructor( + id: string, + label: string, + @IWindowService windowService: IWindowService, + @IWindowsService windowsService: IWindowsService, + @IQuickInputService quickInputService: IQuickInputService, + @IWorkspaceContextService contextService: IWorkspaceContextService, + @IKeybindingService keybindingService: IKeybindingService, + @IModelService modelService: IModelService, + @IModeService modeService: IModeService, + @ILabelService labelService: ILabelService + ) { + super(id, label, windowService, windowsService, quickInputService, contextService, labelService, keybindingService, modelService, modeService); + } + + protected isQuickNavigate(): boolean { + return false; + } +} + +export class QuickOpenRecentAction extends BaseOpenRecentAction { + + static readonly ID = 'workbench.action.quickOpenRecent'; + static readonly LABEL = nls.localize('quickOpenRecent', "Quick Open Recent..."); + + constructor( + id: string, + label: string, + @IWindowService windowService: IWindowService, + @IWindowsService windowsService: IWindowsService, + @IQuickInputService quickInputService: IQuickInputService, + @IWorkspaceContextService contextService: IWorkspaceContextService, + @IKeybindingService keybindingService: IKeybindingService, + @IModelService modelService: IModelService, + @IModeService modeService: IModeService, + @ILabelService labelService: ILabelService + ) { + super(id, label, windowService, windowsService, quickInputService, contextService, labelService, keybindingService, modelService, modeService); + } + + protected isQuickNavigate(): boolean { + return true; + } +} \ No newline at end of file diff --git a/src/vs/workbench/electron-browser/commands.ts b/src/vs/workbench/electron-browser/commands.ts index 8c61db77d49..84a4e8abda6 100644 --- a/src/vs/workbench/electron-browser/commands.ts +++ b/src/vs/workbench/electron-browser/commands.ts @@ -10,721 +10,18 @@ import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IPartService } from 'vs/workbench/services/part/common/partService'; import { IWindowsService, IWindowService } from 'vs/platform/windows/common/windows'; -import { List } from 'vs/base/browser/ui/list/listWidget'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; -import { WorkbenchListFocusContextKey, IListService, WorkbenchListSupportsMultiSelectContextKey, ListWidget, WorkbenchListHasSelectionOrFocus } from 'vs/platform/list/browser/listService'; -import { PagedList } from 'vs/base/browser/ui/list/listPaging'; -import { range } from 'vs/base/common/arrays'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { ITree } from 'vs/base/parts/tree/browser/tree'; import { InEditorZenModeContext, NoEditorsVisibleContext, SingleEditorGroupsContext } from 'vs/workbench/common/editor'; import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { URI } from 'vs/base/common/uri'; import { IDownloadService } from 'vs/platform/download/common/download'; import { generateUuid } from 'vs/base/common/uuid'; -import { ObjectTree } from 'vs/base/browser/ui/tree/objectTree'; -import { AsyncDataTree } from 'vs/base/browser/ui/tree/asyncDataTree'; -import { DataTree } from 'vs/base/browser/ui/tree/dataTree'; -import { ITreeNode } from 'vs/base/browser/ui/tree/tree'; - -// --- List Commands - -function ensureDOMFocus(widget: ListWidget): void { - // it can happen that one of the commands is executed while - // DOM focus is within another focusable control within the - // list/tree item. therefor we should ensure that the - // list/tree has DOM focus again after the command ran. - if (widget && widget.getHTMLElement() !== document.activeElement) { - widget.domFocus(); - } -} export const QUIT_ID = 'workbench.action.quit'; + export function registerCommands(): void { - function focusDown(accessor: ServicesAccessor, arg2?: number): void { - const focused = accessor.get(IListService).lastFocusedList; - const count = typeof arg2 === 'number' ? arg2 : 1; - - // Ensure DOM Focus - ensureDOMFocus(focused); - - // List - if (focused instanceof List || focused instanceof PagedList) { - const list = focused; - - list.focusNext(count); - const listFocus = list.getFocus(); - if (listFocus.length) { - list.reveal(listFocus[0]); - } - } - - // ObjectTree - else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) { - const tree = focused; - - const fakeKeyboardEvent = new KeyboardEvent('keydown'); - tree.focusNext(count, true, fakeKeyboardEvent); - - const listFocus = tree.getFocus(); - if (listFocus.length) { - tree.reveal(listFocus[0]); - } - } - - // Tree - else if (focused) { - const tree = focused; - - tree.focusNext(count, { origin: 'keyboard' }); - tree.reveal(tree.getFocus()); - } - } - - KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: 'list.focusDown', - weight: KeybindingWeight.WorkbenchContrib, - when: WorkbenchListFocusContextKey, - primary: KeyCode.DownArrow, - mac: { - primary: KeyCode.DownArrow, - secondary: [KeyMod.WinCtrl | KeyCode.KEY_N] - }, - handler: (accessor, arg2) => focusDown(accessor, arg2) - }); - - function expandMultiSelection(focused: List | PagedList | ITree | ObjectTree | DataTree | AsyncDataTree, previousFocus: any): void { - - // List - if (focused instanceof List || focused instanceof PagedList) { - const list = focused; - - const focus = list.getFocus() ? list.getFocus()[0] : undefined; - const selection = list.getSelection(); - if (selection && selection.indexOf(focus) >= 0) { - list.setSelection(selection.filter(s => s !== previousFocus)); - } else { - list.setSelection(selection.concat(focus)); - } - } - - // ObjectTree - else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) { - const list = focused; - - const focus = list.getFocus() ? list.getFocus()[0] : undefined; - const selection = list.getSelection(); - const fakeKeyboardEvent = new KeyboardEvent('keydown', { shiftKey: true }); - - if (selection && selection.indexOf(focus) >= 0) { - list.setSelection(selection.filter(s => s !== previousFocus), fakeKeyboardEvent); - } else { - list.setSelection(selection.concat(focus), fakeKeyboardEvent); - } - } - - // Tree - else if (focused) { - const tree = focused; - - const focus = tree.getFocus(); - const selection = tree.getSelection(); - if (selection && selection.indexOf(focus) >= 0) { - tree.setSelection(selection.filter(s => s !== previousFocus)); - } else { - tree.setSelection(selection.concat(focus)); - } - } - } - - KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: 'list.expandSelectionDown', - weight: KeybindingWeight.WorkbenchContrib, - when: ContextKeyExpr.and(WorkbenchListFocusContextKey, WorkbenchListSupportsMultiSelectContextKey), - primary: KeyMod.Shift | KeyCode.DownArrow, - handler: (accessor, arg2) => { - const focused = accessor.get(IListService).lastFocusedList; - - // List - if (focused instanceof List || focused instanceof PagedList || focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) { - const list = focused; - - // Focus down first - const previousFocus = list.getFocus() ? list.getFocus()[0] : undefined; - focusDown(accessor, arg2); - - // Then adjust selection - expandMultiSelection(focused, previousFocus); - } - - // Tree - else if (focused) { - const tree = focused; - - // Focus down first - const previousFocus = tree.getFocus(); - focusDown(accessor, arg2); - - // Then adjust selection - expandMultiSelection(focused, previousFocus); - } - } - }); - - function focusUp(accessor: ServicesAccessor, arg2?: number): void { - const focused = accessor.get(IListService).lastFocusedList; - const count = typeof arg2 === 'number' ? arg2 : 1; - - // Ensure DOM Focus - ensureDOMFocus(focused); - - // List - if (focused instanceof List || focused instanceof PagedList) { - const list = focused; - - list.focusPrevious(count); - const listFocus = list.getFocus(); - if (listFocus.length) { - list.reveal(listFocus[0]); - } - } - - // ObjectTree - else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) { - const tree = focused; - - const fakeKeyboardEvent = new KeyboardEvent('keydown'); - tree.focusPrevious(count, true, fakeKeyboardEvent); - - const listFocus = tree.getFocus(); - if (listFocus.length) { - tree.reveal(listFocus[0]); - } - } - - // Tree - else if (focused) { - const tree = focused; - - tree.focusPrevious(count, { origin: 'keyboard' }); - tree.reveal(tree.getFocus()); - } - } - - KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: 'list.focusUp', - weight: KeybindingWeight.WorkbenchContrib, - when: WorkbenchListFocusContextKey, - primary: KeyCode.UpArrow, - mac: { - primary: KeyCode.UpArrow, - secondary: [KeyMod.WinCtrl | KeyCode.KEY_P] - }, - handler: (accessor, arg2) => focusUp(accessor, arg2) - }); - - KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: 'list.expandSelectionUp', - weight: KeybindingWeight.WorkbenchContrib, - when: ContextKeyExpr.and(WorkbenchListFocusContextKey, WorkbenchListSupportsMultiSelectContextKey), - primary: KeyMod.Shift | KeyCode.UpArrow, - handler: (accessor, arg2) => { - const focused = accessor.get(IListService).lastFocusedList; - - // List - if (focused instanceof List || focused instanceof PagedList || focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) { - const list = focused; - - // Focus up first - const previousFocus = list.getFocus() ? list.getFocus()[0] : undefined; - focusUp(accessor, arg2); - - // Then adjust selection - expandMultiSelection(focused, previousFocus); - } - - // Tree - else if (focused) { - const tree = focused; - - // Focus up first - const previousFocus = tree.getFocus(); - focusUp(accessor, arg2); - - // Then adjust selection - expandMultiSelection(focused, previousFocus); - } - } - }); - - KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: 'list.collapse', - weight: KeybindingWeight.WorkbenchContrib, - when: WorkbenchListFocusContextKey, - primary: KeyCode.LeftArrow, - mac: { - primary: KeyCode.LeftArrow, - secondary: [KeyMod.CtrlCmd | KeyCode.UpArrow] - }, - handler: (accessor) => { - const focused = accessor.get(IListService).lastFocusedList; - - // Tree only - if (focused && !(focused instanceof List || focused instanceof PagedList)) { - if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) { - const tree = focused; - const focusedElements = tree.getFocus(); - - if (focusedElements.length === 0) { - return; - } - - const focus = focusedElements[0]; - - if (!tree.collapse(focus)) { - const parent = tree.getParentElement(focus); - - if (parent) { - const fakeKeyboardEvent = new KeyboardEvent('keydown'); - tree.setFocus([parent], fakeKeyboardEvent); - tree.reveal(parent); - } - } - } else { - const tree = focused; - const focus = tree.getFocus(); - - tree.collapse(focus).then(didCollapse => { - if (focus && !didCollapse) { - tree.focusParent({ origin: 'keyboard' }); - - return tree.reveal(tree.getFocus()); - } - - return undefined; - }); - } - } - } - }); - - KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: 'list.expand', - weight: KeybindingWeight.WorkbenchContrib, - when: WorkbenchListFocusContextKey, - primary: KeyCode.RightArrow, - handler: (accessor) => { - const focused = accessor.get(IListService).lastFocusedList; - - // Tree only - if (focused && !(focused instanceof List || focused instanceof PagedList)) { - if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) { - const tree = focused; - const focusedElements = tree.getFocus(); - - if (focusedElements.length === 0) { - return; - } - - const focus = focusedElements[0]; - - if (!tree.expand(focus)) { - const child = tree.getFirstElementChild(focus); - - if (child) { - const fakeKeyboardEvent = new KeyboardEvent('keydown'); - tree.setFocus([child], fakeKeyboardEvent); - tree.reveal(child); - } - } - } else { - const tree = focused; - const focus = tree.getFocus(); - - tree.expand(focus).then(didExpand => { - if (focus && !didExpand) { - tree.focusFirstChild({ origin: 'keyboard' }); - - return tree.reveal(tree.getFocus()); - } - - return undefined; - }); - } - } - } - }); - - KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: 'list.focusPageUp', - weight: KeybindingWeight.WorkbenchContrib, - when: WorkbenchListFocusContextKey, - primary: KeyCode.PageUp, - handler: (accessor) => { - const focused = accessor.get(IListService).lastFocusedList; - - // Ensure DOM Focus - ensureDOMFocus(focused); - - // List - if (focused instanceof List || focused instanceof PagedList) { - const list = focused; - - list.focusPreviousPage(); - list.reveal(list.getFocus()[0]); - } - - // ObjectTree - else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) { - const list = focused; - - const fakeKeyboardEvent = new KeyboardEvent('keydown'); - list.focusPreviousPage(fakeKeyboardEvent); - list.reveal(list.getFocus()[0]); - } - - // Tree - else if (focused) { - const tree = focused; - - tree.focusPreviousPage({ origin: 'keyboard' }); - tree.reveal(tree.getFocus()); - } - } - }); - - KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: 'list.focusPageDown', - weight: KeybindingWeight.WorkbenchContrib, - when: WorkbenchListFocusContextKey, - primary: KeyCode.PageDown, - handler: (accessor) => { - const focused = accessor.get(IListService).lastFocusedList; - - // Ensure DOM Focus - ensureDOMFocus(focused); - - // List - if (focused instanceof List || focused instanceof PagedList) { - const list = focused; - - list.focusNextPage(); - list.reveal(list.getFocus()[0]); - } - - // ObjectTree - else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) { - const list = focused; - - const fakeKeyboardEvent = new KeyboardEvent('keydown'); - list.focusNextPage(fakeKeyboardEvent); - list.reveal(list.getFocus()[0]); - } - - // Tree - else if (focused) { - const tree = focused; - - tree.focusNextPage({ origin: 'keyboard' }); - tree.reveal(tree.getFocus()); - } - } - }); - - KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: 'list.focusFirst', - weight: KeybindingWeight.WorkbenchContrib, - when: WorkbenchListFocusContextKey, - primary: KeyCode.Home, - handler: accessor => listFocusFirst(accessor) - }); - - KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: 'list.focusFirstChild', - weight: KeybindingWeight.WorkbenchContrib, - when: WorkbenchListFocusContextKey, - primary: 0, - handler: accessor => listFocusFirst(accessor, { fromFocused: true }) - }); - - function listFocusFirst(accessor: ServicesAccessor, options?: { fromFocused: boolean }): void { - const focused = accessor.get(IListService).lastFocusedList; - - // Ensure DOM Focus - ensureDOMFocus(focused); - - // List - if (focused instanceof List || focused instanceof PagedList) { - const list = focused; - - list.setFocus([0]); - list.reveal(0); - } - - // ObjectTree - else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) { - const tree = focused; - const fakeKeyboardEvent = new KeyboardEvent('keydown'); - tree.focusFirst(fakeKeyboardEvent); - - const focus = tree.getFocus(); - - if (focus.length > 0) { - tree.reveal(focus[0]); - } - } - - // Tree - else if (focused) { - const tree = focused; - - tree.focusFirst({ origin: 'keyboard' }, options && options.fromFocused ? tree.getFocus() : undefined); - tree.reveal(tree.getFocus()); - } - } - - KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: 'list.focusLast', - weight: KeybindingWeight.WorkbenchContrib, - when: WorkbenchListFocusContextKey, - primary: KeyCode.End, - handler: accessor => listFocusLast(accessor) - }); - - KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: 'list.focusLastChild', - weight: KeybindingWeight.WorkbenchContrib, - when: WorkbenchListFocusContextKey, - primary: 0, - handler: accessor => listFocusLast(accessor, { fromFocused: true }) - }); - - function listFocusLast(accessor: ServicesAccessor, options?: { fromFocused: boolean }): void { - const focused = accessor.get(IListService).lastFocusedList; - - // Ensure DOM Focus - ensureDOMFocus(focused); - - // List - if (focused instanceof List || focused instanceof PagedList) { - const list = focused; - - list.setFocus([list.length - 1]); - list.reveal(list.length - 1); - } - - // ObjectTree - else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) { - const tree = focused; - const fakeKeyboardEvent = new KeyboardEvent('keydown'); - tree.focusLast(fakeKeyboardEvent); - - const focus = tree.getFocus(); - - if (focus.length > 0) { - tree.reveal(focus[0]); - } - } - - // Tree - else if (focused) { - const tree = focused; - - tree.focusLast({ origin: 'keyboard' }, options && options.fromFocused ? tree.getFocus() : undefined); - tree.reveal(tree.getFocus()); - } - } - - KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: 'list.select', - weight: KeybindingWeight.WorkbenchContrib, - when: WorkbenchListFocusContextKey, - primary: KeyCode.Enter, - mac: { - primary: KeyCode.Enter, - secondary: [KeyMod.CtrlCmd | KeyCode.DownArrow] - }, - handler: (accessor) => { - const focused = accessor.get(IListService).lastFocusedList; - - // List - if (focused instanceof List || focused instanceof PagedList) { - const list = focused; - list.setSelection(list.getFocus()); - list.open(list.getFocus()); - } - - // ObjectTree - else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) { - const list = focused; - const fakeKeyboardEvent = new KeyboardEvent('keydown'); - list.setSelection(list.getFocus(), fakeKeyboardEvent); - list.open(list.getFocus()); - } - - // Tree - else if (focused) { - const tree = focused; - const focus = tree.getFocus(); - - if (focus) { - tree.setSelection([focus], { origin: 'keyboard' }); - } - } - } - }); - - KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: 'list.selectAll', - weight: KeybindingWeight.WorkbenchContrib, - when: ContextKeyExpr.and(WorkbenchListFocusContextKey, WorkbenchListSupportsMultiSelectContextKey), - primary: KeyMod.CtrlCmd | KeyCode.KEY_A, - handler: (accessor) => { - const focused = accessor.get(IListService).lastFocusedList; - - // List - if (focused instanceof List || focused instanceof PagedList) { - const list = focused; - list.setSelection(range(list.length)); - } - - // Trees - else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) { - const tree = focused; - const focus = tree.getFocus(); - const selection = tree.getSelection(); - - // Which element should be considered to start selecting all? - let start: any | undefined = undefined; - - if (focus.length > 0 && (selection.length === 0 || selection.indexOf(focus[0]) === -1)) { - start = focus[0]; - } - - if (!start && selection.length > 0) { - start = selection[0]; - } - - // What is the scope of select all? - let scope: any | undefined = undefined; - - if (!start) { - scope = undefined; - } else { - const selectedNode = tree.getNode(start); - const parentNode = selectedNode.parent; - - if (!parentNode.parent) { // root - scope = undefined; - } else { - scope = parentNode.element; - } - } - - const newSelection: any[] = []; - - // If the scope isn't the tree root, it should be part of the new selection - if (scope) { - newSelection.push(scope); - } - - const visit = (node: ITreeNode) => { - for (const child of node.children) { - if (child.visible) { - newSelection.push(child.element); - - if (!child.collapsed) { - visit(child); - } - } - } - }; - - // Add the whole scope subtree to the new selection - visit(tree.getNode(scope)); - - const fakeKeyboardEvent = new KeyboardEvent('keydown'); - tree.setSelection(newSelection, fakeKeyboardEvent); - } - } - }); - - KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: 'list.toggleExpand', - weight: KeybindingWeight.WorkbenchContrib, - when: WorkbenchListFocusContextKey, - primary: KeyCode.Space, - handler: (accessor) => { - const focused = accessor.get(IListService).lastFocusedList; - - // Tree only - if (focused && !(focused instanceof List || focused instanceof PagedList)) { - if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) { - const tree = focused; - const focus = tree.getFocus(); - - if (focus.length === 0) { - return; - } - - tree.toggleCollapsed(focus[0]); - } else { - const tree = focused; - const focus = tree.getFocus(); - - if (focus) { - tree.toggleExpansion(focus); - } - } - } - } - }); - - KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: 'list.clear', - weight: KeybindingWeight.WorkbenchContrib, - when: ContextKeyExpr.and(WorkbenchListFocusContextKey, WorkbenchListHasSelectionOrFocus), - primary: KeyCode.Escape, - handler: (accessor) => { - const focused = accessor.get(IListService).lastFocusedList; - - // List - if (focused instanceof List || focused instanceof PagedList) { - const list = focused; - - if (list.getSelection().length > 0) { - list.setSelection([]); - } else if (list.getFocus().length > 0) { - list.setFocus([]); - } - } - - // ObjectTree - else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) { - const list = focused; - const fakeKeyboardEvent = new KeyboardEvent('keydown'); - - if (list.getSelection().length > 0) { - list.setSelection([], fakeKeyboardEvent); - } else if (list.getFocus().length > 0) { - list.setFocus([], fakeKeyboardEvent); - } - } - - // Tree - else if (focused) { - const tree = focused; - - if (tree.getSelection().length) { - tree.clearSelection({ origin: 'keyboard' }); - } else if (tree.getFocus()) { - tree.clearFocus({ origin: 'keyboard' }); - } - } - } - }); - // --- commands KeybindingsRegistry.registerCommandAndKeybindingRule({ @@ -741,7 +38,7 @@ export function registerCommands(): void { KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'workbench.action.exitZenMode', weight: KeybindingWeight.EditorContrib - 1000, - handler(accessor: ServicesAccessor, configurationOrName: any) { + handler(accessor: ServicesAccessor) { const partService = accessor.get(IPartService); partService.toggleZenMode(); }, diff --git a/src/vs/workbench/electron-browser/main.contribution.ts b/src/vs/workbench/electron-browser/main.contribution.ts index b130c7b55ab..0cdd304e3d5 100644 --- a/src/vs/workbench/electron-browser/main.contribution.ts +++ b/src/vs/workbench/electron-browser/main.contribution.ts @@ -12,9 +12,10 @@ import { IConfigurationRegistry, Extensions as ConfigurationExtensions, Configur import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; import { isWindows, isLinux, isMacintosh } from 'vs/base/common/platform'; -import { KeybindingsReferenceAction, OpenDocumentationUrlAction, OpenIntroductoryVideosUrlAction, OpenTipsAndTricksUrlAction, OpenIssueReporterAction, ReportPerformanceIssueUsingReporterAction, ZoomResetAction, ZoomOutAction, ZoomInAction, ToggleFullScreenAction, ToggleMenuBarAction, CloseWorkspaceAction, CloseCurrentWindowAction, SwitchWindow, NewWindowAction, NavigateUpAction, NavigateDownAction, NavigateLeftAction, NavigateRightAction, IncreaseViewSizeAction, DecreaseViewSizeAction, ToggleSharedProcessAction, QuickSwitchWindow, QuickOpenRecentAction, inRecentFilesPickerContextKey, ShowAboutDialogAction, InspectContextKeysAction, OpenProcessExplorer, OpenTwitterUrlAction, OpenRequestFeatureUrlAction, OpenPrivacyStatementUrlAction, OpenLicenseUrlAction, OpenRecentAction, ToggleScreencastModeAction } from 'vs/workbench/electron-browser/actions'; +import { KeybindingsReferenceAction, OpenDocumentationUrlAction, OpenIntroductoryVideosUrlAction, OpenTipsAndTricksUrlAction, OpenIssueReporterAction, ReportPerformanceIssueUsingReporterAction, NavigateUpAction, NavigateDownAction, NavigateLeftAction, NavigateRightAction, IncreaseViewSizeAction, DecreaseViewSizeAction, ToggleSharedProcessAction, ShowAboutDialogAction, InspectContextKeysAction, OpenProcessExplorer, OpenTwitterUrlAction, OpenRequestFeatureUrlAction, OpenPrivacyStatementUrlAction, OpenLicenseUrlAction, ToggleScreencastModeAction } from 'vs/workbench/electron-browser/actions'; +import { ZoomResetAction, ZoomOutAction, ZoomInAction, ToggleFullScreenAction, CloseCurrentWindowAction, SwitchWindow, NewWindowAction, QuickSwitchWindow, QuickOpenRecentAction, inRecentFilesPickerContextKey, OpenRecentAction } from 'vs/workbench/electron-browser/actions/windowActions'; import { registerCommands, QUIT_ID } from 'vs/workbench/electron-browser/commands'; -import { AddRootFolderAction, GlobalRemoveRootFolderAction, OpenWorkspaceAction, SaveWorkspaceAsAction, OpenWorkspaceConfigFileAction, DuplicateWorkspaceInNewWindowAction, OpenFileFolderAction, OpenFileAction, OpenFolderAction } from 'vs/workbench/browser/actions/workspaceActions'; +import { AddRootFolderAction, GlobalRemoveRootFolderAction, OpenWorkspaceAction, SaveWorkspaceAsAction, OpenWorkspaceConfigFileAction, DuplicateWorkspaceInNewWindowAction, OpenFileFolderAction, OpenFileAction, OpenFolderAction, CloseWorkspaceAction } from 'vs/workbench/browser/actions/workspaceActions'; import { ContextKeyExpr, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { inQuickOpenContext, getQuickNavigateHandler } from 'vs/workbench/browser/parts/quickopen/quickopen'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; @@ -93,9 +94,6 @@ workbenchActionsRegistry.registerWorkbenchAction( ); workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleFullScreenAction, ToggleFullScreenAction.ID, ToggleFullScreenAction.LABEL, { primary: KeyCode.F11, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_F } }), 'View: Toggle Full Screen', viewCategory); -if (isWindows || isLinux) { - workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleMenuBarAction, ToggleMenuBarAction.ID, ToggleMenuBarAction.LABEL), 'View: Toggle Menu Bar', viewCategory); -} workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(NavigateUpAction, NavigateUpAction.ID, NavigateUpAction.LABEL, undefined), 'View: Navigate to the View Above', viewCategory); workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(NavigateDownAction, NavigateDownAction.ID, NavigateDownAction.LABEL, undefined), 'View: Navigate to the View Below', viewCategory); workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(NavigateLeftAction, NavigateLeftAction.ID, NavigateLeftAction.LABEL, undefined), 'View: Navigate to the View on the Left', viewCategory); @@ -302,16 +300,6 @@ MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { order: 1 }); -MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { - group: '1_toggle_view', - command: { - id: ToggleMenuBarAction.ID, - title: nls.localize({ key: 'miToggleMenuBar', comment: ['&& denotes a mnemonic'] }, "Toggle Menu &&Bar") - }, - when: IsMacContext.toNegated(), - order: 4 -}); - // Zoom MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { diff --git a/src/vs/workbench/electron-browser/media/clear.svg b/src/vs/workbench/electron-browser/media/clear.svg deleted file mode 100644 index c2f3e027100..00000000000 --- a/src/vs/workbench/electron-browser/media/clear.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts index 72e6255f3f7..51fb662368a 100644 --- a/src/vs/workbench/electron-browser/workbench.ts +++ b/src/vs/workbench/electron-browser/workbench.ts @@ -79,7 +79,8 @@ import { MenuService } from 'vs/platform/actions/common/menuService'; import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; -import { OpenRecentAction, ToggleDevToolsAction, ReloadWindowAction, ShowPreviousWindowTab, MoveWindowTabToNewWindow, MergeAllWindowTabs, ShowNextWindowTab, ToggleWindowTabsBar, ReloadWindowWithExtensionsDisabledAction, NewWindowTab } from 'vs/workbench/electron-browser/actions'; +import { ShowPreviousWindowTab, MoveWindowTabToNewWindow, MergeAllWindowTabs, ShowNextWindowTab, ToggleWindowTabsBar, NewWindowTab } from 'vs/workbench/electron-browser/actions'; +import { OpenRecentAction, ToggleDevToolsAction, ReloadWindowAction, ReloadWindowWithExtensionsDisabledAction } from 'vs/workbench/electron-browser/actions/windowActions'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing'; import { WorkspaceEditingService } from 'vs/workbench/services/workspace/node/workspaceEditingService'; diff --git a/src/vs/workbench/parts/watermark/electron-browser/watermark.ts b/src/vs/workbench/parts/watermark/electron-browser/watermark.ts index 84b5334c1b5..07b562b3156 100644 --- a/src/vs/workbench/parts/watermark/electron-browser/watermark.ts +++ b/src/vs/workbench/parts/watermark/electron-browser/watermark.ts @@ -15,7 +15,7 @@ import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/ import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { OpenRecentAction } from 'vs/workbench/electron-browser/actions'; +import { OpenRecentAction } from 'vs/workbench/electron-browser/actions/windowActions'; import { GlobalNewUntitledFileAction } from 'vs/workbench/parts/files/electron-browser/fileActions'; import { OpenFolderAction, OpenFileFolderAction, OpenFileAction } from 'vs/workbench/browser/actions/workspaceActions'; import { ShowAllCommandsAction } from 'vs/workbench/parts/quickopen/browser/commandsHandler'; diff --git a/src/vs/workbench/workbench.main.ts b/src/vs/workbench/workbench.main.ts index 6b5b6430a84..f537bf6f15a 100644 --- a/src/vs/workbench/workbench.main.ts +++ b/src/vs/workbench/workbench.main.ts @@ -27,6 +27,7 @@ import 'vs/workbench/parts/localizations/electron-browser/localizations.contribu // Workbench import 'vs/workbench/browser/actions/layoutActions'; +import 'vs/workbench/browser/actions/listCommands'; import 'vs/workbench/parts/preferences/electron-browser/preferences.contribution'; import 'vs/workbench/parts/preferences/browser/keybindingsEditorContribution'; import 'vs/workbench/parts/logs/electron-browser/logs.contribution'; From 61b16f215f58342ec82e01cb72ca767d61109a6e Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 24 Jan 2019 15:54:12 +0100 Subject: [PATCH 084/274] fixes #66981 --- src/vs/base/browser/ui/tree/abstractTree.ts | 10 +++++++++- src/vs/base/browser/ui/tree/indexTreeModel.ts | 7 ++++++- src/vs/base/browser/ui/tree/objectTreeModel.ts | 2 ++ src/vs/base/browser/ui/tree/tree.ts | 2 ++ 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 9b23289cb1f..8ed9de83d04 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -359,6 +359,7 @@ class TypeFilterController implements IDisposable { constructor( private tree: AbstractTree, + model: ITreeModel, private view: List>, private filter: TypeFilter, private keyboardNavigationLabelProvider: IKeyboardNavigationLabelProvider @@ -383,6 +384,8 @@ class TypeFilterController implements IDisposable { this.clearDomNode.tabIndex = -1; this.clearDomNode.title = localize('clear', "Clear"); + model.onDidSplice(this.onDidSpliceModel, this, this.disposables); + tree.onDidUpdateOptions(this.onDidUpdateTreeOptions, this, this.disposables); this.onDidUpdateTreeOptions(tree.options); } @@ -524,6 +527,11 @@ class TypeFilterController implements IDisposable { disposables.push(toDisposable(() => StaticDND.CurrentDragAndDropData = undefined)); } + private onDidSpliceModel(): void { + this.tree.refilter(); + this.updateMessage(); + } + private onDidChangeFilterOnType(): void { this.tree.updateOptions({ filterOnType: this.filterOnTypeDomNode.checked }); this.tree.refilter(); @@ -660,7 +668,7 @@ export abstract class AbstractTree implements IDisposable } if (_options.keyboardNavigationLabelProvider) { - const typeFilterController = new TypeFilterController(this, this.view, filter as TypeFilter, _options.keyboardNavigationLabelProvider); + const typeFilterController = new TypeFilterController(this, this.model, this.view, filter as TypeFilter, _options.keyboardNavigationLabelProvider); this.focusNavigationFilter = node => !typeFilterController.pattern || !FuzzyScore.isDefault(node.filterData as any as FuzzyScore); // TODO@joao this.disposables.push(typeFilterController); } diff --git a/src/vs/base/browser/ui/tree/indexTreeModel.ts b/src/vs/base/browser/ui/tree/indexTreeModel.ts index 0b4fad00e98..c00e38b55ee 100644 --- a/src/vs/base/browser/ui/tree/indexTreeModel.ts +++ b/src/vs/base/browser/ui/tree/indexTreeModel.ts @@ -61,6 +61,9 @@ export class IndexTreeModel, TFilterData = voi private filter?: ITreeFilter; private autoExpandSingleChildren: boolean; + private _onDidSplice = new Emitter(); + readonly onDidSplice = this._onDidSplice.event; + constructor(private list: ISpliceable>, rootElement: T, options: IIndexTreeModelOptions = {}) { this.collapseByDefault = typeof options.collapseByDefault === 'undefined' ? false : options.collapseByDefault; this.filter = options.filter; @@ -123,7 +126,9 @@ export class IndexTreeModel, TFilterData = voi deletedNodes.forEach(visit); } - return Iterator.map(Iterator.fromArray(deletedNodes), treeNodeToElement); + const result = Iterator.map(Iterator.fromArray(deletedNodes), treeNodeToElement); + this._onDidSplice.fire(undefined); + return result; } refresh(location: number[]): void { diff --git a/src/vs/base/browser/ui/tree/objectTreeModel.ts b/src/vs/base/browser/ui/tree/objectTreeModel.ts index e024cf1dd92..467a7c76e2f 100644 --- a/src/vs/base/browser/ui/tree/objectTreeModel.ts +++ b/src/vs/base/browser/ui/tree/objectTreeModel.ts @@ -21,6 +21,7 @@ export class ObjectTreeModel, TFilterData extends Non private nodes = new Map>(); private sorter?: ITreeSorter>; + readonly onDidSplice: Event; readonly onDidChangeCollapseState: Event>; readonly onDidChangeRenderNodeCount: Event>; @@ -28,6 +29,7 @@ export class ObjectTreeModel, TFilterData extends Non constructor(list: ISpliceable>, options: IObjectTreeModelOptions = {}) { this.model = new IndexTreeModel(list, null, options); + this.onDidSplice = this.model.onDidSplice; this.onDidChangeCollapseState = this.model.onDidChangeCollapseState as Event>; this.onDidChangeRenderNodeCount = this.model.onDidChangeRenderNodeCount as Event>; diff --git a/src/vs/base/browser/ui/tree/tree.ts b/src/vs/base/browser/ui/tree/tree.ts index 8fc05270f02..7cf94dbb023 100644 --- a/src/vs/base/browser/ui/tree/tree.ts +++ b/src/vs/base/browser/ui/tree/tree.ts @@ -97,6 +97,8 @@ export interface ICollapseStateChangeEvent { export interface ITreeModel { readonly rootRef: TRef; + + readonly onDidSplice: Event; readonly onDidChangeCollapseState: Event>; readonly onDidChangeRenderNodeCount: Event>; From ebf9cb41367c91d57cc35bccc074e910fb8330c9 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 24 Jan 2019 16:11:32 +0100 Subject: [PATCH 085/274] debt - inline commands.ts from electron-browser --- src/vs/workbench/api/node/apiCommands.ts | 24 +++++- .../browser/actions/layoutActions.ts | 14 ++++ src/vs/workbench/electron-browser/actions.ts | 2 - src/vs/workbench/electron-browser/commands.ts | 73 ------------------- .../electron-browser/main.contribution.ts | 29 +++++++- 5 files changed, 62 insertions(+), 80 deletions(-) delete mode 100644 src/vs/workbench/electron-browser/commands.ts diff --git a/src/vs/workbench/api/node/apiCommands.ts b/src/vs/workbench/api/node/apiCommands.ts index 0e228988d60..0e808dbfed4 100644 --- a/src/vs/workbench/api/node/apiCommands.ts +++ b/src/vs/workbench/api/node/apiCommands.ts @@ -3,14 +3,21 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { tmpdir } from 'os'; +import { posix } from 'path'; +import * as vscode from 'vscode'; import { URI } from 'vs/base/common/uri'; import { isMalformedFileUri } from 'vs/base/common/resources'; -import * as vscode from 'vscode'; import * as typeConverters from 'vs/workbench/api/node/extHostTypeConverters'; import { CommandsRegistry, ICommandService, ICommandHandler } from 'vs/platform/commands/common/commands'; import { ITextEditorOptions } from 'vs/platform/editor/common/editor'; import { EditorViewColumn } from 'vs/workbench/api/shared/editor'; import { EditorGroupLayout } from 'vs/workbench/services/group/common/editorGroupsService'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { IWindowsService } from 'vs/platform/windows/common/windows'; +import { IDownloadService } from 'vs/platform/download/common/download'; +import { generateUuid } from 'vs/base/common/uuid'; // ----------------------------------------------------------------- // The following commands are registered on both sides separately. @@ -99,6 +106,12 @@ export class OpenAPICommand { } CommandsRegistry.registerCommand(OpenAPICommand.ID, adjustHandler(OpenAPICommand.execute)); +CommandsRegistry.registerCommand('_workbench.removeFromRecentlyOpened', function (accessor: ServicesAccessor, path: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | URI | string) { + const windowsService = accessor.get(IWindowsService); + + return windowsService.removeFromRecentlyOpened([path]).then(() => undefined); +}); + export class RemoveFromRecentlyOpenedAPICommand { public static ID = 'vscode.removeFromRecentlyOpened'; public static execute(executor: ICommandsExecutor, path: string): Promise { @@ -113,4 +126,11 @@ export class SetEditorLayoutAPICommand { return executor.executeCommand('layoutEditorGroups', layout); } } -CommandsRegistry.registerCommand(SetEditorLayoutAPICommand.ID, adjustHandler(SetEditorLayoutAPICommand.execute)); \ No newline at end of file +CommandsRegistry.registerCommand(SetEditorLayoutAPICommand.ID, adjustHandler(SetEditorLayoutAPICommand.execute)); + +CommandsRegistry.registerCommand('_workbench.downloadResource', function (accessor: ServicesAccessor, resource: URI) { + const downloadService = accessor.get(IDownloadService); + const location = posix.join(tmpdir(), generateUuid()); + + return downloadService.download(resource, location).then(() => URI.file(location)); +}); \ No newline at end of file diff --git a/src/vs/workbench/browser/actions/layoutActions.ts b/src/vs/workbench/browser/actions/layoutActions.ts index 4625ea6e742..10e1db37351 100644 --- a/src/vs/workbench/browser/actions/layoutActions.ts +++ b/src/vs/workbench/browser/actions/layoutActions.ts @@ -18,6 +18,8 @@ import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { MenuBarVisibility } from 'vs/platform/windows/common/windows'; import { isWindows, isLinux } from 'vs/base/common/platform'; import { IsMacContext } from 'vs/platform/workbench/common/contextkeys'; +import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { InEditorZenModeContext } from 'vs/workbench/common/editor'; const registry = Registry.as(Extensions.WorkbenchActions); const viewCategory = nls.localize('view', "View"); @@ -342,6 +344,18 @@ MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { order: 2 }); +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'workbench.action.exitZenMode', + weight: KeybindingWeight.EditorContrib - 1000, + handler(accessor: ServicesAccessor) { + const partService = accessor.get(IPartService); + partService.toggleZenMode(); + }, + when: InEditorZenModeContext, + primary: KeyChord(KeyCode.Escape, KeyCode.Escape) +}); + + // --- Toggle Menu Bar export class ToggleMenuBarAction extends Action { diff --git a/src/vs/workbench/electron-browser/actions.ts b/src/vs/workbench/electron-browser/actions.ts index d13c1a42739..56cfeede886 100644 --- a/src/vs/workbench/electron-browser/actions.ts +++ b/src/vs/workbench/electron-browser/actions.ts @@ -3,8 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!./media/actions'; - import { Action } from 'vs/base/common/actions'; import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows'; import * as nls from 'vs/nls'; diff --git a/src/vs/workbench/electron-browser/commands.ts b/src/vs/workbench/electron-browser/commands.ts deleted file mode 100644 index 84a4e8abda6..00000000000 --- a/src/vs/workbench/electron-browser/commands.ts +++ /dev/null @@ -1,73 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { tmpdir } from 'os'; -import { posix } from 'path'; -import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; -import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { IPartService } from 'vs/workbench/services/part/common/partService'; -import { IWindowsService, IWindowService } from 'vs/platform/windows/common/windows'; -import { CommandsRegistry } from 'vs/platform/commands/common/commands'; -import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { InEditorZenModeContext, NoEditorsVisibleContext, SingleEditorGroupsContext } from 'vs/workbench/common/editor'; -import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; -import { URI } from 'vs/base/common/uri'; -import { IDownloadService } from 'vs/platform/download/common/download'; -import { generateUuid } from 'vs/base/common/uuid'; - -export const QUIT_ID = 'workbench.action.quit'; - -export function registerCommands(): void { - - // --- commands - - KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: 'workbench.action.closeWindow', // close the window when the last editor is closed by reusing the same keybinding - weight: KeybindingWeight.WorkbenchContrib, - when: ContextKeyExpr.and(NoEditorsVisibleContext, SingleEditorGroupsContext), - primary: KeyMod.CtrlCmd | KeyCode.KEY_W, - handler: accessor => { - const windowService = accessor.get(IWindowService); - windowService.closeWindow(); - } - }); - - KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: 'workbench.action.exitZenMode', - weight: KeybindingWeight.EditorContrib - 1000, - handler(accessor: ServicesAccessor) { - const partService = accessor.get(IPartService); - partService.toggleZenMode(); - }, - when: InEditorZenModeContext, - primary: KeyChord(KeyCode.Escape, KeyCode.Escape) - }); - - KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: QUIT_ID, - weight: KeybindingWeight.WorkbenchContrib, - handler(accessor: ServicesAccessor) { - const windowsService = accessor.get(IWindowsService); - windowsService.quit(); - }, - when: undefined, - primary: KeyMod.CtrlCmd | KeyCode.KEY_Q, - win: { primary: undefined } - }); - - CommandsRegistry.registerCommand('_workbench.removeFromRecentlyOpened', function (accessor: ServicesAccessor, path: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | URI | string) { - const windowsService = accessor.get(IWindowsService); - - return windowsService.removeFromRecentlyOpened([path]).then(() => undefined); - }); - - CommandsRegistry.registerCommand('_workbench.downloadResource', function (accessor: ServicesAccessor, resource: URI) { - const downloadService = accessor.get(IDownloadService); - const location = posix.join(tmpdir(), generateUuid()); - - return downloadService.download(resource, location).then(() => URI.file(location)); - }); -} diff --git a/src/vs/workbench/electron-browser/main.contribution.ts b/src/vs/workbench/electron-browser/main.contribution.ts index 0cdd304e3d5..a64786d02df 100644 --- a/src/vs/workbench/electron-browser/main.contribution.ts +++ b/src/vs/workbench/electron-browser/main.contribution.ts @@ -14,18 +14,41 @@ import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; import { isWindows, isLinux, isMacintosh } from 'vs/base/common/platform'; import { KeybindingsReferenceAction, OpenDocumentationUrlAction, OpenIntroductoryVideosUrlAction, OpenTipsAndTricksUrlAction, OpenIssueReporterAction, ReportPerformanceIssueUsingReporterAction, NavigateUpAction, NavigateDownAction, NavigateLeftAction, NavigateRightAction, IncreaseViewSizeAction, DecreaseViewSizeAction, ToggleSharedProcessAction, ShowAboutDialogAction, InspectContextKeysAction, OpenProcessExplorer, OpenTwitterUrlAction, OpenRequestFeatureUrlAction, OpenPrivacyStatementUrlAction, OpenLicenseUrlAction, ToggleScreencastModeAction } from 'vs/workbench/electron-browser/actions'; import { ZoomResetAction, ZoomOutAction, ZoomInAction, ToggleFullScreenAction, CloseCurrentWindowAction, SwitchWindow, NewWindowAction, QuickSwitchWindow, QuickOpenRecentAction, inRecentFilesPickerContextKey, OpenRecentAction } from 'vs/workbench/electron-browser/actions/windowActions'; -import { registerCommands, QUIT_ID } from 'vs/workbench/electron-browser/commands'; import { AddRootFolderAction, GlobalRemoveRootFolderAction, OpenWorkspaceAction, SaveWorkspaceAsAction, OpenWorkspaceConfigFileAction, DuplicateWorkspaceInNewWindowAction, OpenFileFolderAction, OpenFileAction, OpenFolderAction, CloseWorkspaceAction } from 'vs/workbench/browser/actions/workspaceActions'; import { ContextKeyExpr, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { inQuickOpenContext, getQuickNavigateHandler } from 'vs/workbench/browser/parts/quickopen/quickopen'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ADD_ROOT_FOLDER_COMMAND_ID } from 'vs/workbench/browser/actions/workspaceCommands'; import { IsMacContext } from 'vs/platform/workbench/common/contextkeys'; +import { NoEditorsVisibleContext, SingleEditorGroupsContext } from 'vs/workbench/common/editor'; +import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows'; // Contribute Commands -registerCommands(); +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'workbench.action.closeWindow', // close the window when the last editor is closed by reusing the same keybinding + weight: KeybindingWeight.WorkbenchContrib, + when: ContextKeyExpr.and(NoEditorsVisibleContext, SingleEditorGroupsContext), + primary: KeyMod.CtrlCmd | KeyCode.KEY_W, + handler: accessor => { + const windowService = accessor.get(IWindowService); + windowService.closeWindow(); + } +}); + +const QUIT_ID = 'workbench.action.quit'; +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: QUIT_ID, + weight: KeybindingWeight.WorkbenchContrib, + handler(accessor: ServicesAccessor) { + const windowsService = accessor.get(IWindowsService); + windowsService.quit(); + }, + when: undefined, + primary: KeyMod.CtrlCmd | KeyCode.KEY_Q, + win: { primary: undefined } +}); // Contribute Global Actions const viewCategory = nls.localize('view', "View"); From c21a86e6aac86bd8ac6a22dc148c7a89de532f13 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 24 Jan 2019 16:15:47 +0100 Subject: [PATCH 086/274] explorer: if there is no view state for this workspace, expand all roots --- .../parts/files/electron-browser/views/explorerView.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts index cdfa5fa056e..2ace95f1c79 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -443,6 +443,10 @@ export class ExplorerView extends ViewletPanel { } const promise = this.tree.setInput(input, viewState).then(() => { + if (!viewState && Array.isArray(input)) { + // There is no view state for this workspace, expand all roots. + input.forEach(item => this.tree.expand(item).then(undefined, onUnexpectedError)); + } if (initialInputSetup) { perf.mark('didResolveExplorer'); } From ca7e3a015a11b6b9efeed28bfe6abea1fbfac483 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 24 Jan 2019 16:19:41 +0100 Subject: [PATCH 087/274] debt - extract developer actions --- src/vs/workbench/electron-browser/actions.ts | 191 +--------------- .../actions/developerActions.ts | 212 ++++++++++++++++++ .../electron-browser/actions/windowActions.ts | 14 -- .../electron-browser/main.contribution.ts | 3 +- .../workbench/electron-browser/workbench.ts | 3 +- 5 files changed, 217 insertions(+), 206 deletions(-) create mode 100644 src/vs/workbench/electron-browser/actions/developerActions.ts diff --git a/src/vs/workbench/electron-browser/actions.ts b/src/vs/workbench/electron-browser/actions.ts index 56cfeede886..d1495f21666 100644 --- a/src/vs/workbench/electron-browser/actions.ts +++ b/src/vs/workbench/electron-browser/actions.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Action } from 'vs/base/common/actions'; -import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows'; +import { IWindowsService } from 'vs/platform/windows/common/windows'; import * as nls from 'vs/nls'; import product from 'vs/platform/node/product'; import { isMacintosh, isLinux, language } from 'vs/base/common/platform'; @@ -12,20 +12,11 @@ import { IEditorGroupsService, GroupDirection, GroupLocation, IFindGroupScope } import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IPartService, Parts, Position as PartPosition } from 'vs/workbench/services/part/common/partService'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { shell } from 'electron'; import { IViewlet } from 'vs/workbench/common/viewlet'; import { IPanel } from 'vs/workbench/common/panel'; import { IssueType } from 'vs/platform/issue/common/issue'; -import { domEvent } from 'vs/base/browser/event'; -import { Event } from 'vs/base/common/event'; -import { IDisposable, toDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; -import { getDomNodePagePosition, createStyleSheet, createCSSRule, append, $ } from 'vs/base/browser/dom'; -import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { Context } from 'vs/platform/contextkey/browser/contextKeyService'; import { IWorkbenchIssueService } from 'vs/workbench/services/issue/common/issue'; -import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { timeout } from 'vs/base/common/async'; // --- actions @@ -84,7 +75,6 @@ export class ReportPerformanceIssueUsingReporterAction extends Action { } } - export class KeybindingsReferenceAction extends Action { static readonly ID = 'workbench.action.keybindingsReference'; @@ -169,20 +159,6 @@ export class OpenTipsAndTricksUrlAction extends Action { } } -export class ToggleSharedProcessAction extends Action { - - static readonly ID = 'workbench.action.toggleSharedProcess'; - static LABEL = nls.localize('toggleSharedProcess', "Toggle Shared Process"); - - constructor(id: string, label: string, @IWindowsService private readonly windowsService: IWindowsService) { - super(id, label); - } - - run(): Promise { - return this.windowsService.toggleSharedProcess(); - } -} - export const enum Direction { Next, Previous, @@ -729,168 +705,3 @@ export class ShowAboutDialogAction extends Action { return this.windowsService.openAboutDialog(); } } - -export class InspectContextKeysAction extends Action { - - static readonly ID = 'workbench.action.inspectContextKeys'; - static LABEL = nls.localize('inspect context keys', "Inspect Context Keys"); - - constructor( - id: string, - label: string, - @IContextKeyService private readonly contextKeyService: IContextKeyService, - @IWindowService private readonly windowService: IWindowService, - ) { - super(id, label); - } - - run(): Promise { - const disposables: IDisposable[] = []; - - const stylesheet = createStyleSheet(); - disposables.push(toDisposable(() => { - if (stylesheet.parentNode) { - stylesheet.parentNode.removeChild(stylesheet); - } - })); - createCSSRule('*', 'cursor: crosshair !important;', stylesheet); - - const hoverFeedback = document.createElement('div'); - document.body.appendChild(hoverFeedback); - disposables.push(toDisposable(() => document.body.removeChild(hoverFeedback))); - - hoverFeedback.style.position = 'absolute'; - hoverFeedback.style.pointerEvents = 'none'; - hoverFeedback.style.backgroundColor = 'rgba(255, 0, 0, 0.5)'; - hoverFeedback.style.zIndex = '1000'; - - const onMouseMove = domEvent(document.body, 'mousemove', true); - disposables.push(onMouseMove(e => { - const target = e.target as HTMLElement; - const position = getDomNodePagePosition(target); - - hoverFeedback.style.top = `${position.top}px`; - hoverFeedback.style.left = `${position.left}px`; - hoverFeedback.style.width = `${position.width}px`; - hoverFeedback.style.height = `${position.height}px`; - })); - - const onMouseDown = Event.once(domEvent(document.body, 'mousedown', true)); - onMouseDown(e => { e.preventDefault(); e.stopPropagation(); }, null, disposables); - - const onMouseUp = Event.once(domEvent(document.body, 'mouseup', true)); - onMouseUp(e => { - e.preventDefault(); - e.stopPropagation(); - - const context = this.contextKeyService.getContext(e.target as HTMLElement) as Context; - console.log(context.collectAllValues()); - this.windowService.openDevTools(); - - dispose(disposables); - }, null, disposables); - - return Promise.resolve(); - } -} - -export class ToggleScreencastModeAction extends Action { - - static readonly ID = 'workbench.action.toggleScreencastMode'; - static LABEL = nls.localize('toggle mouse clicks', "Toggle Screencast Mode"); - - static disposable: IDisposable | undefined; - - constructor(id: string, label: string, @IKeybindingService private readonly keybindingService: IKeybindingService) { - super(id, label); - } - - async run(): Promise { - if (ToggleScreencastModeAction.disposable) { - ToggleScreencastModeAction.disposable.dispose(); - ToggleScreencastModeAction.disposable = undefined; - return; - } - - const mouseMarker = append(document.body, $('div')); - mouseMarker.style.position = 'absolute'; - mouseMarker.style.border = '2px solid red'; - mouseMarker.style.borderRadius = '20px'; - mouseMarker.style.width = '20px'; - mouseMarker.style.height = '20px'; - mouseMarker.style.top = '0'; - mouseMarker.style.left = '0'; - mouseMarker.style.zIndex = '100000'; - mouseMarker.style.content = ' '; - mouseMarker.style.pointerEvents = 'none'; - mouseMarker.style.display = 'none'; - - const onMouseDown = domEvent(document.body, 'mousedown', true); - const onMouseUp = domEvent(document.body, 'mouseup', true); - const onMouseMove = domEvent(document.body, 'mousemove', true); - - const mouseListener = onMouseDown(e => { - mouseMarker.style.top = `${e.clientY - 10}px`; - mouseMarker.style.left = `${e.clientX - 10}px`; - mouseMarker.style.display = 'block'; - - const mouseMoveListener = onMouseMove(e => { - mouseMarker.style.top = `${e.clientY - 10}px`; - mouseMarker.style.left = `${e.clientX - 10}px`; - }); - - Event.once(onMouseUp)(() => { - mouseMarker.style.display = 'none'; - mouseMoveListener.dispose(); - }); - }); - - const keyboardMarker = append(document.body, $('div')); - keyboardMarker.style.position = 'absolute'; - keyboardMarker.style.backgroundColor = 'rgba(0, 0, 0 ,0.5)'; - keyboardMarker.style.width = '100%'; - keyboardMarker.style.height = '100px'; - keyboardMarker.style.bottom = '20%'; - keyboardMarker.style.left = '0'; - keyboardMarker.style.zIndex = '100000'; - keyboardMarker.style.pointerEvents = 'none'; - keyboardMarker.style.color = 'white'; - keyboardMarker.style.lineHeight = '100px'; - keyboardMarker.style.textAlign = 'center'; - keyboardMarker.style.fontSize = '56px'; - keyboardMarker.style.display = 'none'; - - const onKeyDown = domEvent(document.body, 'keydown', true); - let keyboardTimeout: IDisposable = Disposable.None; - - const keyboardListener = onKeyDown(e => { - keyboardTimeout.dispose(); - - const event = new StandardKeyboardEvent(e); - const keybinding = this.keybindingService.resolveKeyboardEvent(event); - const label = keybinding.getLabel(); - - if (!event.ctrlKey && !event.altKey && !event.metaKey && !event.shiftKey && this.keybindingService.mightProducePrintableCharacter(event) && label) { - keyboardMarker.textContent += ' ' + label; - } else { - keyboardMarker.textContent = label; - } - - keyboardMarker.style.display = 'block'; - - const promise = timeout(800); - keyboardTimeout = toDisposable(() => promise.cancel()); - - promise.then(() => { - keyboardMarker.textContent = ''; - keyboardMarker.style.display = 'none'; - }); - }); - - ToggleScreencastModeAction.disposable = toDisposable(() => { - mouseListener.dispose(); - keyboardListener.dispose(); - document.body.removeChild(mouseMarker); - }); - } -} diff --git a/src/vs/workbench/electron-browser/actions/developerActions.ts b/src/vs/workbench/electron-browser/actions/developerActions.ts new file mode 100644 index 00000000000..6f9e2f1ffb1 --- /dev/null +++ b/src/vs/workbench/electron-browser/actions/developerActions.ts @@ -0,0 +1,212 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Action } from 'vs/base/common/actions'; +import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows'; +import * as nls from 'vs/nls'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { domEvent } from 'vs/base/browser/event'; +import { Event } from 'vs/base/common/event'; +import { IDisposable, toDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; +import { getDomNodePagePosition, createStyleSheet, createCSSRule, append, $ } from 'vs/base/browser/dom'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { Context } from 'vs/platform/contextkey/browser/contextKeyService'; +import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { timeout } from 'vs/base/common/async'; + +// --- actions + +export class ToggleDevToolsAction extends Action { + + static readonly ID = 'workbench.action.toggleDevTools'; + static LABEL = nls.localize('toggleDevTools', "Toggle Developer Tools"); + + constructor(id: string, label: string, @IWindowService private readonly windowsService: IWindowService) { + super(id, label); + } + + run(): Promise { + return this.windowsService.toggleDevTools(); + } +} + +export class ToggleSharedProcessAction extends Action { + + static readonly ID = 'workbench.action.toggleSharedProcess'; + static LABEL = nls.localize('toggleSharedProcess', "Toggle Shared Process"); + + constructor(id: string, label: string, @IWindowsService private readonly windowsService: IWindowsService) { + super(id, label); + } + + run(): Promise { + return this.windowsService.toggleSharedProcess(); + } +} + +export class InspectContextKeysAction extends Action { + + static readonly ID = 'workbench.action.inspectContextKeys'; + static LABEL = nls.localize('inspect context keys', "Inspect Context Keys"); + + constructor( + id: string, + label: string, + @IContextKeyService private readonly contextKeyService: IContextKeyService, + @IWindowService private readonly windowService: IWindowService, + ) { + super(id, label); + } + + run(): Promise { + const disposables: IDisposable[] = []; + + const stylesheet = createStyleSheet(); + disposables.push(toDisposable(() => { + if (stylesheet.parentNode) { + stylesheet.parentNode.removeChild(stylesheet); + } + })); + createCSSRule('*', 'cursor: crosshair !important;', stylesheet); + + const hoverFeedback = document.createElement('div'); + document.body.appendChild(hoverFeedback); + disposables.push(toDisposable(() => document.body.removeChild(hoverFeedback))); + + hoverFeedback.style.position = 'absolute'; + hoverFeedback.style.pointerEvents = 'none'; + hoverFeedback.style.backgroundColor = 'rgba(255, 0, 0, 0.5)'; + hoverFeedback.style.zIndex = '1000'; + + const onMouseMove = domEvent(document.body, 'mousemove', true); + disposables.push(onMouseMove(e => { + const target = e.target as HTMLElement; + const position = getDomNodePagePosition(target); + + hoverFeedback.style.top = `${position.top}px`; + hoverFeedback.style.left = `${position.left}px`; + hoverFeedback.style.width = `${position.width}px`; + hoverFeedback.style.height = `${position.height}px`; + })); + + const onMouseDown = Event.once(domEvent(document.body, 'mousedown', true)); + onMouseDown(e => { e.preventDefault(); e.stopPropagation(); }, null, disposables); + + const onMouseUp = Event.once(domEvent(document.body, 'mouseup', true)); + onMouseUp(e => { + e.preventDefault(); + e.stopPropagation(); + + const context = this.contextKeyService.getContext(e.target as HTMLElement) as Context; + console.log(context.collectAllValues()); + this.windowService.openDevTools(); + + dispose(disposables); + }, null, disposables); + + return Promise.resolve(); + } +} + +export class ToggleScreencastModeAction extends Action { + + static readonly ID = 'workbench.action.toggleScreencastMode'; + static LABEL = nls.localize('toggle mouse clicks', "Toggle Screencast Mode"); + + static disposable: IDisposable | undefined; + + constructor(id: string, label: string, @IKeybindingService private readonly keybindingService: IKeybindingService) { + super(id, label); + } + + async run(): Promise { + if (ToggleScreencastModeAction.disposable) { + ToggleScreencastModeAction.disposable.dispose(); + ToggleScreencastModeAction.disposable = undefined; + return; + } + + const mouseMarker = append(document.body, $('div')); + mouseMarker.style.position = 'absolute'; + mouseMarker.style.border = '2px solid red'; + mouseMarker.style.borderRadius = '20px'; + mouseMarker.style.width = '20px'; + mouseMarker.style.height = '20px'; + mouseMarker.style.top = '0'; + mouseMarker.style.left = '0'; + mouseMarker.style.zIndex = '100000'; + mouseMarker.style.content = ' '; + mouseMarker.style.pointerEvents = 'none'; + mouseMarker.style.display = 'none'; + + const onMouseDown = domEvent(document.body, 'mousedown', true); + const onMouseUp = domEvent(document.body, 'mouseup', true); + const onMouseMove = domEvent(document.body, 'mousemove', true); + + const mouseListener = onMouseDown(e => { + mouseMarker.style.top = `${e.clientY - 10}px`; + mouseMarker.style.left = `${e.clientX - 10}px`; + mouseMarker.style.display = 'block'; + + const mouseMoveListener = onMouseMove(e => { + mouseMarker.style.top = `${e.clientY - 10}px`; + mouseMarker.style.left = `${e.clientX - 10}px`; + }); + + Event.once(onMouseUp)(() => { + mouseMarker.style.display = 'none'; + mouseMoveListener.dispose(); + }); + }); + + const keyboardMarker = append(document.body, $('div')); + keyboardMarker.style.position = 'absolute'; + keyboardMarker.style.backgroundColor = 'rgba(0, 0, 0 ,0.5)'; + keyboardMarker.style.width = '100%'; + keyboardMarker.style.height = '100px'; + keyboardMarker.style.bottom = '20%'; + keyboardMarker.style.left = '0'; + keyboardMarker.style.zIndex = '100000'; + keyboardMarker.style.pointerEvents = 'none'; + keyboardMarker.style.color = 'white'; + keyboardMarker.style.lineHeight = '100px'; + keyboardMarker.style.textAlign = 'center'; + keyboardMarker.style.fontSize = '56px'; + keyboardMarker.style.display = 'none'; + + const onKeyDown = domEvent(document.body, 'keydown', true); + let keyboardTimeout: IDisposable = Disposable.None; + + const keyboardListener = onKeyDown(e => { + keyboardTimeout.dispose(); + + const event = new StandardKeyboardEvent(e); + const keybinding = this.keybindingService.resolveKeyboardEvent(event); + const label = keybinding.getLabel(); + + if (!event.ctrlKey && !event.altKey && !event.metaKey && !event.shiftKey && this.keybindingService.mightProducePrintableCharacter(event) && label) { + keyboardMarker.textContent += ' ' + label; + } else { + keyboardMarker.textContent = label; + } + + keyboardMarker.style.display = 'block'; + + const promise = timeout(800); + keyboardTimeout = toDisposable(() => promise.cancel()); + + promise.then(() => { + keyboardMarker.textContent = ''; + keyboardMarker.style.display = 'none'; + }); + }); + + ToggleScreencastModeAction.disposable = toDisposable(() => { + mouseListener.dispose(); + keyboardListener.dispose(); + document.body.removeChild(mouseMarker); + }); + } +} diff --git a/src/vs/workbench/electron-browser/actions/windowActions.ts b/src/vs/workbench/electron-browser/actions/windowActions.ts index 2bea402ef54..e93e6eb5918 100644 --- a/src/vs/workbench/electron-browser/actions/windowActions.ts +++ b/src/vs/workbench/electron-browser/actions/windowActions.ts @@ -75,20 +75,6 @@ export class ToggleFullScreenAction extends Action { } } -export class ToggleDevToolsAction extends Action { - - static readonly ID = 'workbench.action.toggleDevTools'; - static LABEL = nls.localize('toggleDevTools', "Toggle Developer Tools"); - - constructor(id: string, label: string, @IWindowService private readonly windowsService: IWindowService) { - super(id, label); - } - - run(): Promise { - return this.windowsService.toggleDevTools(); - } -} - export abstract class BaseZoomAction extends Action { private static readonly SETTING_KEY = 'window.zoomLevel'; diff --git a/src/vs/workbench/electron-browser/main.contribution.ts b/src/vs/workbench/electron-browser/main.contribution.ts index a64786d02df..a7178399567 100644 --- a/src/vs/workbench/electron-browser/main.contribution.ts +++ b/src/vs/workbench/electron-browser/main.contribution.ts @@ -12,7 +12,8 @@ import { IConfigurationRegistry, Extensions as ConfigurationExtensions, Configur import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; import { isWindows, isLinux, isMacintosh } from 'vs/base/common/platform'; -import { KeybindingsReferenceAction, OpenDocumentationUrlAction, OpenIntroductoryVideosUrlAction, OpenTipsAndTricksUrlAction, OpenIssueReporterAction, ReportPerformanceIssueUsingReporterAction, NavigateUpAction, NavigateDownAction, NavigateLeftAction, NavigateRightAction, IncreaseViewSizeAction, DecreaseViewSizeAction, ToggleSharedProcessAction, ShowAboutDialogAction, InspectContextKeysAction, OpenProcessExplorer, OpenTwitterUrlAction, OpenRequestFeatureUrlAction, OpenPrivacyStatementUrlAction, OpenLicenseUrlAction, ToggleScreencastModeAction } from 'vs/workbench/electron-browser/actions'; +import { KeybindingsReferenceAction, OpenDocumentationUrlAction, OpenIntroductoryVideosUrlAction, OpenTipsAndTricksUrlAction, OpenIssueReporterAction, ReportPerformanceIssueUsingReporterAction, NavigateUpAction, NavigateDownAction, NavigateLeftAction, NavigateRightAction, IncreaseViewSizeAction, DecreaseViewSizeAction, ShowAboutDialogAction, OpenProcessExplorer, OpenTwitterUrlAction, OpenRequestFeatureUrlAction, OpenPrivacyStatementUrlAction, OpenLicenseUrlAction } from 'vs/workbench/electron-browser/actions'; +import { ToggleSharedProcessAction, InspectContextKeysAction, ToggleScreencastModeAction } from 'vs/workbench/electron-browser/actions/developerActions'; import { ZoomResetAction, ZoomOutAction, ZoomInAction, ToggleFullScreenAction, CloseCurrentWindowAction, SwitchWindow, NewWindowAction, QuickSwitchWindow, QuickOpenRecentAction, inRecentFilesPickerContextKey, OpenRecentAction } from 'vs/workbench/electron-browser/actions/windowActions'; import { AddRootFolderAction, GlobalRemoveRootFolderAction, OpenWorkspaceAction, SaveWorkspaceAsAction, OpenWorkspaceConfigFileAction, DuplicateWorkspaceInNewWindowAction, OpenFileFolderAction, OpenFileAction, OpenFolderAction, CloseWorkspaceAction } from 'vs/workbench/browser/actions/workspaceActions'; import { ContextKeyExpr, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts index 51fb662368a..1ed2d19ec12 100644 --- a/src/vs/workbench/electron-browser/workbench.ts +++ b/src/vs/workbench/electron-browser/workbench.ts @@ -80,7 +80,8 @@ import { IContextMenuService, IContextViewService } from 'vs/platform/contextvie import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; import { ShowPreviousWindowTab, MoveWindowTabToNewWindow, MergeAllWindowTabs, ShowNextWindowTab, ToggleWindowTabsBar, NewWindowTab } from 'vs/workbench/electron-browser/actions'; -import { OpenRecentAction, ToggleDevToolsAction, ReloadWindowAction, ReloadWindowWithExtensionsDisabledAction } from 'vs/workbench/electron-browser/actions/windowActions'; +import { OpenRecentAction, ReloadWindowAction, ReloadWindowWithExtensionsDisabledAction } from 'vs/workbench/electron-browser/actions/windowActions'; +import { ToggleDevToolsAction } from 'vs/workbench/electron-browser/actions/developerActions'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing'; import { WorkspaceEditingService } from 'vs/workbench/services/workspace/node/workspaceEditingService'; From bda7cadc5f3614d328636dff1696eb9ff5e68735 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 24 Jan 2019 16:22:16 +0100 Subject: [PATCH 088/274] debt - extract more window actions --- src/vs/workbench/electron-browser/actions.ts | 126 ----------------- .../electron-browser/actions/windowActions.ts | 127 ++++++++++++++++++ .../electron-browser/main.contribution.ts | 4 +- .../workbench/electron-browser/workbench.ts | 3 +- 4 files changed, 130 insertions(+), 130 deletions(-) diff --git a/src/vs/workbench/electron-browser/actions.ts b/src/vs/workbench/electron-browser/actions.ts index d1495f21666..094c5d599a2 100644 --- a/src/vs/workbench/electron-browser/actions.ts +++ b/src/vs/workbench/electron-browser/actions.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { Action } from 'vs/base/common/actions'; -import { IWindowsService } from 'vs/platform/windows/common/windows'; import * as nls from 'vs/nls'; import product from 'vs/platform/node/product'; import { isMacintosh, isLinux, language } from 'vs/base/common/platform'; @@ -485,114 +484,6 @@ export class DecreaseViewSizeAction extends BaseResizeViewAction { } } -export class NewWindowTab extends Action { - - static readonly ID = 'workbench.action.newWindowTab'; - static readonly LABEL = nls.localize('newTab', "New Window Tab"); - - constructor( - _id: string, - _label: string, - @IWindowsService private readonly windowsService: IWindowsService - ) { - super(NewWindowTab.ID, NewWindowTab.LABEL); - } - - run(): Promise { - return this.windowsService.newWindowTab().then(() => true); - } -} - -export class ShowPreviousWindowTab extends Action { - - static readonly ID = 'workbench.action.showPreviousWindowTab'; - static readonly LABEL = nls.localize('showPreviousTab', "Show Previous Window Tab"); - - constructor( - _id: string, - _label: string, - @IWindowsService private readonly windowsService: IWindowsService - ) { - super(ShowPreviousWindowTab.ID, ShowPreviousWindowTab.LABEL); - } - - run(): Promise { - return this.windowsService.showPreviousWindowTab().then(() => true); - } -} - -export class ShowNextWindowTab extends Action { - - static readonly ID = 'workbench.action.showNextWindowTab'; - static readonly LABEL = nls.localize('showNextWindowTab', "Show Next Window Tab"); - - constructor( - _id: string, - _label: string, - @IWindowsService private readonly windowsService: IWindowsService - ) { - super(ShowNextWindowTab.ID, ShowNextWindowTab.LABEL); - } - - run(): Promise { - return this.windowsService.showNextWindowTab().then(() => true); - } -} - -export class MoveWindowTabToNewWindow extends Action { - - static readonly ID = 'workbench.action.moveWindowTabToNewWindow'; - static readonly LABEL = nls.localize('moveWindowTabToNewWindow', "Move Window Tab to New Window"); - - constructor( - _id: string, - _label: string, - @IWindowsService private readonly windowsService: IWindowsService - ) { - super(MoveWindowTabToNewWindow.ID, MoveWindowTabToNewWindow.LABEL); - } - - run(): Promise { - return this.windowsService.moveWindowTabToNewWindow().then(() => true); - } -} - -export class MergeAllWindowTabs extends Action { - - static readonly ID = 'workbench.action.mergeAllWindowTabs'; - static readonly LABEL = nls.localize('mergeAllWindowTabs', "Merge All Windows"); - - constructor( - _id: string, - _label: string, - @IWindowsService private readonly windowsService: IWindowsService - ) { - super(MergeAllWindowTabs.ID, MergeAllWindowTabs.LABEL); - } - - run(): Promise { - return this.windowsService.mergeAllWindowTabs().then(() => true); - } -} - -export class ToggleWindowTabsBar extends Action { - - static readonly ID = 'workbench.action.toggleWindowTabsBar'; - static readonly LABEL = nls.localize('toggleWindowTabsBar', "Toggle Window Tabs Bar"); - - constructor( - _id: string, - _label: string, - @IWindowsService private readonly windowsService: IWindowsService - ) { - super(ToggleWindowTabsBar.ID, ToggleWindowTabsBar.LABEL); - } - - run(): Promise { - return this.windowsService.toggleWindowTabsBar().then(() => true); - } -} - export class OpenTwitterUrlAction extends Action { static readonly ID = 'workbench.action.openTwitterUrl'; @@ -688,20 +579,3 @@ export class OpenPrivacyStatementUrlAction extends Action { } } -export class ShowAboutDialogAction extends Action { - - static readonly ID = 'workbench.action.showAboutDialog'; - static LABEL = nls.localize('about', "About {0}", product.applicationName); - - constructor( - id: string, - label: string, - @IWindowsService private readonly windowsService: IWindowsService - ) { - super(id, label); - } - - run(): Promise { - return this.windowsService.openAboutDialog(); - } -} diff --git a/src/vs/workbench/electron-browser/actions/windowActions.ts b/src/vs/workbench/electron-browser/actions/windowActions.ts index e93e6eb5918..50e78b9bfec 100644 --- a/src/vs/workbench/electron-browser/actions/windowActions.ts +++ b/src/vs/workbench/electron-browser/actions/windowActions.ts @@ -24,6 +24,7 @@ import { IModelService } from 'vs/editor/common/services/modelService'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IQuickInputService, IQuickPickItem, IQuickInputButton, IQuickPickSeparator, IKeyMods } from 'vs/platform/quickinput/common/quickInput'; import { getIconClasses } from 'vs/editor/common/services/getIconClasses'; +import product from 'vs/platform/node/product'; // --- actions @@ -449,4 +450,130 @@ export class QuickOpenRecentAction extends BaseOpenRecentAction { protected isQuickNavigate(): boolean { return true; } +} + +export class ShowAboutDialogAction extends Action { + + static readonly ID = 'workbench.action.showAboutDialog'; + static LABEL = nls.localize('about', "About {0}", product.applicationName); + + constructor( + id: string, + label: string, + @IWindowsService private readonly windowsService: IWindowsService + ) { + super(id, label); + } + + run(): Promise { + return this.windowsService.openAboutDialog(); + } +} + +export class NewWindowTab extends Action { + + static readonly ID = 'workbench.action.newWindowTab'; + static readonly LABEL = nls.localize('newTab', "New Window Tab"); + + constructor( + _id: string, + _label: string, + @IWindowsService private readonly windowsService: IWindowsService + ) { + super(NewWindowTab.ID, NewWindowTab.LABEL); + } + + run(): Promise { + return this.windowsService.newWindowTab().then(() => true); + } +} + +export class ShowPreviousWindowTab extends Action { + + static readonly ID = 'workbench.action.showPreviousWindowTab'; + static readonly LABEL = nls.localize('showPreviousTab', "Show Previous Window Tab"); + + constructor( + _id: string, + _label: string, + @IWindowsService private readonly windowsService: IWindowsService + ) { + super(ShowPreviousWindowTab.ID, ShowPreviousWindowTab.LABEL); + } + + run(): Promise { + return this.windowsService.showPreviousWindowTab().then(() => true); + } +} + +export class ShowNextWindowTab extends Action { + + static readonly ID = 'workbench.action.showNextWindowTab'; + static readonly LABEL = nls.localize('showNextWindowTab', "Show Next Window Tab"); + + constructor( + _id: string, + _label: string, + @IWindowsService private readonly windowsService: IWindowsService + ) { + super(ShowNextWindowTab.ID, ShowNextWindowTab.LABEL); + } + + run(): Promise { + return this.windowsService.showNextWindowTab().then(() => true); + } +} + +export class MoveWindowTabToNewWindow extends Action { + + static readonly ID = 'workbench.action.moveWindowTabToNewWindow'; + static readonly LABEL = nls.localize('moveWindowTabToNewWindow', "Move Window Tab to New Window"); + + constructor( + _id: string, + _label: string, + @IWindowsService private readonly windowsService: IWindowsService + ) { + super(MoveWindowTabToNewWindow.ID, MoveWindowTabToNewWindow.LABEL); + } + + run(): Promise { + return this.windowsService.moveWindowTabToNewWindow().then(() => true); + } +} + +export class MergeAllWindowTabs extends Action { + + static readonly ID = 'workbench.action.mergeAllWindowTabs'; + static readonly LABEL = nls.localize('mergeAllWindowTabs', "Merge All Windows"); + + constructor( + _id: string, + _label: string, + @IWindowsService private readonly windowsService: IWindowsService + ) { + super(MergeAllWindowTabs.ID, MergeAllWindowTabs.LABEL); + } + + run(): Promise { + return this.windowsService.mergeAllWindowTabs().then(() => true); + } +} + +export class ToggleWindowTabsBar extends Action { + + static readonly ID = 'workbench.action.toggleWindowTabsBar'; + static readonly LABEL = nls.localize('toggleWindowTabsBar', "Toggle Window Tabs Bar"); + + constructor( + _id: string, + _label: string, + @IWindowsService private readonly windowsService: IWindowsService + ) { + super(ToggleWindowTabsBar.ID, ToggleWindowTabsBar.LABEL); + } + + run(): Promise { + return this.windowsService.toggleWindowTabsBar().then(() => true); + } } \ No newline at end of file diff --git a/src/vs/workbench/electron-browser/main.contribution.ts b/src/vs/workbench/electron-browser/main.contribution.ts index a7178399567..9be1e74dc29 100644 --- a/src/vs/workbench/electron-browser/main.contribution.ts +++ b/src/vs/workbench/electron-browser/main.contribution.ts @@ -12,9 +12,9 @@ import { IConfigurationRegistry, Extensions as ConfigurationExtensions, Configur import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; import { isWindows, isLinux, isMacintosh } from 'vs/base/common/platform'; -import { KeybindingsReferenceAction, OpenDocumentationUrlAction, OpenIntroductoryVideosUrlAction, OpenTipsAndTricksUrlAction, OpenIssueReporterAction, ReportPerformanceIssueUsingReporterAction, NavigateUpAction, NavigateDownAction, NavigateLeftAction, NavigateRightAction, IncreaseViewSizeAction, DecreaseViewSizeAction, ShowAboutDialogAction, OpenProcessExplorer, OpenTwitterUrlAction, OpenRequestFeatureUrlAction, OpenPrivacyStatementUrlAction, OpenLicenseUrlAction } from 'vs/workbench/electron-browser/actions'; +import { KeybindingsReferenceAction, OpenDocumentationUrlAction, OpenIntroductoryVideosUrlAction, OpenTipsAndTricksUrlAction, OpenIssueReporterAction, ReportPerformanceIssueUsingReporterAction, NavigateUpAction, NavigateDownAction, NavigateLeftAction, NavigateRightAction, IncreaseViewSizeAction, DecreaseViewSizeAction, OpenProcessExplorer, OpenTwitterUrlAction, OpenRequestFeatureUrlAction, OpenPrivacyStatementUrlAction, OpenLicenseUrlAction } from 'vs/workbench/electron-browser/actions'; import { ToggleSharedProcessAction, InspectContextKeysAction, ToggleScreencastModeAction } from 'vs/workbench/electron-browser/actions/developerActions'; -import { ZoomResetAction, ZoomOutAction, ZoomInAction, ToggleFullScreenAction, CloseCurrentWindowAction, SwitchWindow, NewWindowAction, QuickSwitchWindow, QuickOpenRecentAction, inRecentFilesPickerContextKey, OpenRecentAction } from 'vs/workbench/electron-browser/actions/windowActions'; +import { ShowAboutDialogAction, ZoomResetAction, ZoomOutAction, ZoomInAction, ToggleFullScreenAction, CloseCurrentWindowAction, SwitchWindow, NewWindowAction, QuickSwitchWindow, QuickOpenRecentAction, inRecentFilesPickerContextKey, OpenRecentAction } from 'vs/workbench/electron-browser/actions/windowActions'; import { AddRootFolderAction, GlobalRemoveRootFolderAction, OpenWorkspaceAction, SaveWorkspaceAsAction, OpenWorkspaceConfigFileAction, DuplicateWorkspaceInNewWindowAction, OpenFileFolderAction, OpenFileAction, OpenFolderAction, CloseWorkspaceAction } from 'vs/workbench/browser/actions/workspaceActions'; import { ContextKeyExpr, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { inQuickOpenContext, getQuickNavigateHandler } from 'vs/workbench/browser/parts/quickopen/quickopen'; diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts index 1ed2d19ec12..aee037e7b7c 100644 --- a/src/vs/workbench/electron-browser/workbench.ts +++ b/src/vs/workbench/electron-browser/workbench.ts @@ -79,8 +79,7 @@ import { MenuService } from 'vs/platform/actions/common/menuService'; import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; -import { ShowPreviousWindowTab, MoveWindowTabToNewWindow, MergeAllWindowTabs, ShowNextWindowTab, ToggleWindowTabsBar, NewWindowTab } from 'vs/workbench/electron-browser/actions'; -import { OpenRecentAction, ReloadWindowAction, ReloadWindowWithExtensionsDisabledAction } from 'vs/workbench/electron-browser/actions/windowActions'; +import { ShowPreviousWindowTab, MoveWindowTabToNewWindow, MergeAllWindowTabs, ShowNextWindowTab, ToggleWindowTabsBar, NewWindowTab, OpenRecentAction, ReloadWindowAction, ReloadWindowWithExtensionsDisabledAction } from 'vs/workbench/electron-browser/actions/windowActions'; import { ToggleDevToolsAction } from 'vs/workbench/electron-browser/actions/developerActions'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing'; From 2ac36dca50e35fd96031a8d360c19a7710d306aa Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 24 Jan 2019 16:31:58 +0100 Subject: [PATCH 089/274] debt - extract navigational actions --- .../browser/actions/navigationActions.ts | 272 ++++++++++++++++++ src/vs/workbench/electron-browser/actions.ts | 260 +---------------- .../electron-browser/main.contribution.ts | 6 +- src/vs/workbench/workbench.main.ts | 1 + 4 files changed, 275 insertions(+), 264 deletions(-) create mode 100644 src/vs/workbench/browser/actions/navigationActions.ts diff --git a/src/vs/workbench/browser/actions/navigationActions.ts b/src/vs/workbench/browser/actions/navigationActions.ts new file mode 100644 index 00000000000..6a83374bacb --- /dev/null +++ b/src/vs/workbench/browser/actions/navigationActions.ts @@ -0,0 +1,272 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { Action } from 'vs/base/common/actions'; +import { IEditorGroupsService, GroupDirection, GroupLocation, IFindGroupScope } from 'vs/workbench/services/group/common/editorGroupsService'; +import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; +import { IPartService, Parts, Position as PartPosition } from 'vs/workbench/services/part/common/partService'; +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { IViewlet } from 'vs/workbench/common/viewlet'; +import { IPanel } from 'vs/workbench/common/panel'; +import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; + +abstract class BaseNavigationAction extends Action { + + constructor( + id: string, + label: string, + @IEditorGroupsService protected editorGroupService: IEditorGroupsService, + @IPanelService protected panelService: IPanelService, + @IPartService protected partService: IPartService, + @IViewletService protected viewletService: IViewletService + ) { + super(id, label); + } + + run(): Promise { + const isEditorFocus = this.partService.hasFocus(Parts.EDITOR_PART); + const isPanelFocus = this.partService.hasFocus(Parts.PANEL_PART); + const isSidebarFocus = this.partService.hasFocus(Parts.SIDEBAR_PART); + + const isSidebarPositionLeft = this.partService.getSideBarPosition() === PartPosition.LEFT; + const isPanelPositionDown = this.partService.getPanelPosition() === PartPosition.BOTTOM; + + if (isEditorFocus) { + return this.navigateOnEditorFocus(isSidebarPositionLeft, isPanelPositionDown); + } + + if (isPanelFocus) { + return this.navigateOnPanelFocus(isSidebarPositionLeft, isPanelPositionDown); + } + + if (isSidebarFocus) { + return Promise.resolve(this.navigateOnSidebarFocus(isSidebarPositionLeft, isPanelPositionDown)); + } + + return Promise.resolve(false); + } + + protected navigateOnEditorFocus(_isSidebarPositionLeft: boolean, _isPanelPositionDown: boolean): Promise { + return Promise.resolve(true); + } + + protected navigateOnPanelFocus(_isSidebarPositionLeft: boolean, _isPanelPositionDown: boolean): Promise { + return Promise.resolve(true); + } + + protected navigateOnSidebarFocus(_isSidebarPositionLeft: boolean, _isPanelPositionDown: boolean): boolean | IViewlet { + return true; + } + + protected navigateToPanel(): IPanel | boolean { + if (!this.partService.isVisible(Parts.PANEL_PART)) { + return false; + } + + const activePanelId = this.panelService.getActivePanel().getId(); + + return this.panelService.openPanel(activePanelId, true); + } + + protected navigateToSidebar(): Promise { + if (!this.partService.isVisible(Parts.SIDEBAR_PART)) { + return Promise.resolve(false); + } + + const activeViewletId = this.viewletService.getActiveViewlet().getId(); + + return this.viewletService.openViewlet(activeViewletId, true) + .then(value => value === null ? false : value); + } + + protected navigateAcrossEditorGroup(direction: GroupDirection): boolean { + return this.doNavigateToEditorGroup({ direction }); + } + + protected navigateToEditorGroup(location: GroupLocation): boolean { + return this.doNavigateToEditorGroup({ location }); + } + + private doNavigateToEditorGroup(scope: IFindGroupScope): boolean { + const targetGroup = this.editorGroupService.findGroup(scope, this.editorGroupService.activeGroup); + if (targetGroup) { + targetGroup.focus(); + + return true; + } + + return false; + } +} + +class NavigateLeftAction extends BaseNavigationAction { + + static readonly ID = 'workbench.action.navigateLeft'; + static readonly LABEL = nls.localize('navigateLeft', "Navigate to the View on the Left"); + + constructor( + id: string, + label: string, + @IEditorGroupsService editorGroupService: IEditorGroupsService, + @IPanelService panelService: IPanelService, + @IPartService partService: IPartService, + @IViewletService viewletService: IViewletService + ) { + super(id, label, editorGroupService, panelService, partService, viewletService); + } + + protected navigateOnEditorFocus(isSidebarPositionLeft: boolean, _isPanelPositionDown: boolean): Promise { + const didNavigate = this.navigateAcrossEditorGroup(GroupDirection.LEFT); + if (didNavigate) { + return Promise.resolve(true); + } + + if (isSidebarPositionLeft) { + return this.navigateToSidebar(); + } + + return Promise.resolve(false); + } + + protected navigateOnPanelFocus(isSidebarPositionLeft: boolean, isPanelPositionDown: boolean): Promise { + if (isPanelPositionDown && isSidebarPositionLeft) { + return this.navigateToSidebar(); + } + + if (!isPanelPositionDown) { + return Promise.resolve(this.navigateToEditorGroup(GroupLocation.LAST)); + } + + return Promise.resolve(false); + } + + protected navigateOnSidebarFocus(isSidebarPositionLeft: boolean, _isPanelPositionDown: boolean): boolean { + if (!isSidebarPositionLeft) { + return this.navigateToEditorGroup(GroupLocation.LAST); + } + + return false; + } +} + +class NavigateRightAction extends BaseNavigationAction { + + static readonly ID = 'workbench.action.navigateRight'; + static readonly LABEL = nls.localize('navigateRight', "Navigate to the View on the Right"); + + constructor( + id: string, + label: string, + @IEditorGroupsService editorGroupService: IEditorGroupsService, + @IPanelService panelService: IPanelService, + @IPartService partService: IPartService, + @IViewletService viewletService: IViewletService + ) { + super(id, label, editorGroupService, panelService, partService, viewletService); + } + + protected navigateOnEditorFocus(isSidebarPositionLeft: boolean, isPanelPositionDown: boolean): Promise { + const didNavigate = this.navigateAcrossEditorGroup(GroupDirection.RIGHT); + if (didNavigate) { + return Promise.resolve(true); + } + + if (!isPanelPositionDown) { + return Promise.resolve(this.navigateToPanel()); + } + + if (!isSidebarPositionLeft) { + return this.navigateToSidebar(); + } + + return Promise.resolve(false); + } + + protected navigateOnPanelFocus(isSidebarPositionLeft: boolean, _isPanelPositionDown: boolean): Promise { + if (!isSidebarPositionLeft) { + return this.navigateToSidebar(); + } + + return Promise.resolve(false); + } + + protected navigateOnSidebarFocus(isSidebarPositionLeft: boolean, _isPanelPositionDown: boolean): boolean { + if (isSidebarPositionLeft) { + return this.navigateToEditorGroup(GroupLocation.FIRST); + } + + return false; + } +} + +class NavigateUpAction extends BaseNavigationAction { + + static readonly ID = 'workbench.action.navigateUp'; + static readonly LABEL = nls.localize('navigateUp', "Navigate to the View Above"); + + constructor( + id: string, + label: string, + @IEditorGroupsService editorGroupService: IEditorGroupsService, + @IPanelService panelService: IPanelService, + @IPartService partService: IPartService, + @IViewletService viewletService: IViewletService + ) { + super(id, label, editorGroupService, panelService, partService, viewletService); + } + + protected navigateOnEditorFocus(_isSidebarPositionLeft: boolean, _isPanelPositionDown: boolean): Promise { + return Promise.resolve(this.navigateAcrossEditorGroup(GroupDirection.UP)); + } + + protected navigateOnPanelFocus(_isSidebarPositionLeft: boolean, isPanelPositionDown: boolean): Promise { + if (isPanelPositionDown) { + return Promise.resolve(this.navigateToEditorGroup(GroupLocation.LAST)); + } + + return Promise.resolve(false); + } +} + +class NavigateDownAction extends BaseNavigationAction { + + static readonly ID = 'workbench.action.navigateDown'; + static readonly LABEL = nls.localize('navigateDown', "Navigate to the View Below"); + + constructor( + id: string, + label: string, + @IEditorGroupsService editorGroupService: IEditorGroupsService, + @IPanelService panelService: IPanelService, + @IPartService partService: IPartService, + @IViewletService viewletService: IViewletService + ) { + super(id, label, editorGroupService, panelService, partService, viewletService); + } + + protected navigateOnEditorFocus(_isSidebarPositionLeft: boolean, isPanelPositionDown: boolean): Promise { + const didNavigate = this.navigateAcrossEditorGroup(GroupDirection.DOWN); + if (didNavigate) { + return Promise.resolve(true); + } + + if (isPanelPositionDown) { + return Promise.resolve(this.navigateToPanel()); + } + + return Promise.resolve(false); + } +} + +const registry = Registry.as(Extensions.WorkbenchActions); +const viewCategory = nls.localize('view', "View"); + +registry.registerWorkbenchAction(new SyncActionDescriptor(NavigateUpAction, NavigateUpAction.ID, NavigateUpAction.LABEL, undefined), 'View: Navigate to the View Above', viewCategory); +registry.registerWorkbenchAction(new SyncActionDescriptor(NavigateDownAction, NavigateDownAction.ID, NavigateDownAction.LABEL, undefined), 'View: Navigate to the View Below', viewCategory); +registry.registerWorkbenchAction(new SyncActionDescriptor(NavigateLeftAction, NavigateLeftAction.ID, NavigateLeftAction.LABEL, undefined), 'View: Navigate to the View on the Left', viewCategory); +registry.registerWorkbenchAction(new SyncActionDescriptor(NavigateRightAction, NavigateRightAction.ID, NavigateRightAction.LABEL, undefined), 'View: Navigate to the View on the Right', viewCategory); diff --git a/src/vs/workbench/electron-browser/actions.ts b/src/vs/workbench/electron-browser/actions.ts index 094c5d599a2..389a6747173 100644 --- a/src/vs/workbench/electron-browser/actions.ts +++ b/src/vs/workbench/electron-browser/actions.ts @@ -7,13 +7,8 @@ import { Action } from 'vs/base/common/actions'; import * as nls from 'vs/nls'; import product from 'vs/platform/node/product'; import { isMacintosh, isLinux, language } from 'vs/base/common/platform'; -import { IEditorGroupsService, GroupDirection, GroupLocation, IFindGroupScope } from 'vs/workbench/services/group/common/editorGroupsService'; -import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; -import { IPartService, Parts, Position as PartPosition } from 'vs/workbench/services/part/common/partService'; -import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { IPartService, Parts } from 'vs/workbench/services/part/common/partService'; import { shell } from 'electron'; -import { IViewlet } from 'vs/workbench/common/viewlet'; -import { IPanel } from 'vs/workbench/common/panel'; import { IssueType } from 'vs/platform/issue/common/issue'; import { IWorkbenchIssueService } from 'vs/workbench/services/issue/common/issue'; @@ -158,259 +153,6 @@ export class OpenTipsAndTricksUrlAction extends Action { } } -export const enum Direction { - Next, - Previous, -} - -export abstract class BaseNavigationAction extends Action { - - constructor( - id: string, - label: string, - @IEditorGroupsService protected editorGroupService: IEditorGroupsService, - @IPanelService protected panelService: IPanelService, - @IPartService protected partService: IPartService, - @IViewletService protected viewletService: IViewletService - ) { - super(id, label); - } - - run(): Promise { - const isEditorFocus = this.partService.hasFocus(Parts.EDITOR_PART); - const isPanelFocus = this.partService.hasFocus(Parts.PANEL_PART); - const isSidebarFocus = this.partService.hasFocus(Parts.SIDEBAR_PART); - - const isSidebarPositionLeft = this.partService.getSideBarPosition() === PartPosition.LEFT; - const isPanelPositionDown = this.partService.getPanelPosition() === PartPosition.BOTTOM; - - if (isEditorFocus) { - return this.navigateOnEditorFocus(isSidebarPositionLeft, isPanelPositionDown); - } - - if (isPanelFocus) { - return this.navigateOnPanelFocus(isSidebarPositionLeft, isPanelPositionDown); - } - - if (isSidebarFocus) { - return Promise.resolve(this.navigateOnSidebarFocus(isSidebarPositionLeft, isPanelPositionDown)); - } - - return Promise.resolve(false); - } - - protected navigateOnEditorFocus(_isSidebarPositionLeft: boolean, _isPanelPositionDown: boolean): Promise { - return Promise.resolve(true); - } - - protected navigateOnPanelFocus(_isSidebarPositionLeft: boolean, _isPanelPositionDown: boolean): Promise { - return Promise.resolve(true); - } - - protected navigateOnSidebarFocus(_isSidebarPositionLeft: boolean, _isPanelPositionDown: boolean): boolean | IViewlet { - return true; - } - - protected navigateToPanel(): IPanel | boolean { - if (!this.partService.isVisible(Parts.PANEL_PART)) { - return false; - } - - const activePanelId = this.panelService.getActivePanel().getId(); - - return this.panelService.openPanel(activePanelId, true); - } - - protected navigateToSidebar(): Promise { - if (!this.partService.isVisible(Parts.SIDEBAR_PART)) { - return Promise.resolve(false); - } - - const activeViewletId = this.viewletService.getActiveViewlet().getId(); - - return this.viewletService.openViewlet(activeViewletId, true) - .then(value => value === null ? false : value); - } - - protected navigateAcrossEditorGroup(direction: GroupDirection): boolean { - return this.doNavigateToEditorGroup({ direction }); - } - - protected navigateToEditorGroup(location: GroupLocation): boolean { - return this.doNavigateToEditorGroup({ location }); - } - - private doNavigateToEditorGroup(scope: IFindGroupScope): boolean { - const targetGroup = this.editorGroupService.findGroup(scope, this.editorGroupService.activeGroup); - if (targetGroup) { - targetGroup.focus(); - - return true; - } - - return false; - } -} - -export class NavigateLeftAction extends BaseNavigationAction { - - static readonly ID = 'workbench.action.navigateLeft'; - static readonly LABEL = nls.localize('navigateLeft', "Navigate to the View on the Left"); - - constructor( - id: string, - label: string, - @IEditorGroupsService editorGroupService: IEditorGroupsService, - @IPanelService panelService: IPanelService, - @IPartService partService: IPartService, - @IViewletService viewletService: IViewletService - ) { - super(id, label, editorGroupService, panelService, partService, viewletService); - } - - protected navigateOnEditorFocus(isSidebarPositionLeft: boolean, _isPanelPositionDown: boolean): Promise { - const didNavigate = this.navigateAcrossEditorGroup(GroupDirection.LEFT); - if (didNavigate) { - return Promise.resolve(true); - } - - if (isSidebarPositionLeft) { - return this.navigateToSidebar(); - } - - return Promise.resolve(false); - } - - protected navigateOnPanelFocus(isSidebarPositionLeft: boolean, isPanelPositionDown: boolean): Promise { - if (isPanelPositionDown && isSidebarPositionLeft) { - return this.navigateToSidebar(); - } - - if (!isPanelPositionDown) { - return Promise.resolve(this.navigateToEditorGroup(GroupLocation.LAST)); - } - - return Promise.resolve(false); - } - - protected navigateOnSidebarFocus(isSidebarPositionLeft: boolean, _isPanelPositionDown: boolean): boolean { - if (!isSidebarPositionLeft) { - return this.navigateToEditorGroup(GroupLocation.LAST); - } - - return false; - } -} - -export class NavigateRightAction extends BaseNavigationAction { - - static readonly ID = 'workbench.action.navigateRight'; - static readonly LABEL = nls.localize('navigateRight', "Navigate to the View on the Right"); - - constructor( - id: string, - label: string, - @IEditorGroupsService editorGroupService: IEditorGroupsService, - @IPanelService panelService: IPanelService, - @IPartService partService: IPartService, - @IViewletService viewletService: IViewletService - ) { - super(id, label, editorGroupService, panelService, partService, viewletService); - } - - protected navigateOnEditorFocus(isSidebarPositionLeft: boolean, isPanelPositionDown: boolean): Promise { - const didNavigate = this.navigateAcrossEditorGroup(GroupDirection.RIGHT); - if (didNavigate) { - return Promise.resolve(true); - } - - if (!isPanelPositionDown) { - return Promise.resolve(this.navigateToPanel()); - } - - if (!isSidebarPositionLeft) { - return this.navigateToSidebar(); - } - - return Promise.resolve(false); - } - - protected navigateOnPanelFocus(isSidebarPositionLeft: boolean, _isPanelPositionDown: boolean): Promise { - if (!isSidebarPositionLeft) { - return this.navigateToSidebar(); - } - - return Promise.resolve(false); - } - - protected navigateOnSidebarFocus(isSidebarPositionLeft: boolean, _isPanelPositionDown: boolean): boolean { - if (isSidebarPositionLeft) { - return this.navigateToEditorGroup(GroupLocation.FIRST); - } - - return false; - } -} - -export class NavigateUpAction extends BaseNavigationAction { - - static readonly ID = 'workbench.action.navigateUp'; - static readonly LABEL = nls.localize('navigateUp', "Navigate to the View Above"); - - constructor( - id: string, - label: string, - @IEditorGroupsService editorGroupService: IEditorGroupsService, - @IPanelService panelService: IPanelService, - @IPartService partService: IPartService, - @IViewletService viewletService: IViewletService - ) { - super(id, label, editorGroupService, panelService, partService, viewletService); - } - - protected navigateOnEditorFocus(_isSidebarPositionLeft: boolean, _isPanelPositionDown: boolean): Promise { - return Promise.resolve(this.navigateAcrossEditorGroup(GroupDirection.UP)); - } - - protected navigateOnPanelFocus(_isSidebarPositionLeft: boolean, isPanelPositionDown: boolean): Promise { - if (isPanelPositionDown) { - return Promise.resolve(this.navigateToEditorGroup(GroupLocation.LAST)); - } - - return Promise.resolve(false); - } -} - -export class NavigateDownAction extends BaseNavigationAction { - - static readonly ID = 'workbench.action.navigateDown'; - static readonly LABEL = nls.localize('navigateDown', "Navigate to the View Below"); - - constructor( - id: string, - label: string, - @IEditorGroupsService editorGroupService: IEditorGroupsService, - @IPanelService panelService: IPanelService, - @IPartService partService: IPartService, - @IViewletService viewletService: IViewletService - ) { - super(id, label, editorGroupService, panelService, partService, viewletService); - } - - protected navigateOnEditorFocus(_isSidebarPositionLeft: boolean, isPanelPositionDown: boolean): Promise { - const didNavigate = this.navigateAcrossEditorGroup(GroupDirection.DOWN); - if (didNavigate) { - return Promise.resolve(true); - } - - if (isPanelPositionDown) { - return Promise.resolve(this.navigateToPanel()); - } - - return Promise.resolve(false); - } -} - // Resize focused view actions export abstract class BaseResizeViewAction extends Action { diff --git a/src/vs/workbench/electron-browser/main.contribution.ts b/src/vs/workbench/electron-browser/main.contribution.ts index 9be1e74dc29..932dab0e30c 100644 --- a/src/vs/workbench/electron-browser/main.contribution.ts +++ b/src/vs/workbench/electron-browser/main.contribution.ts @@ -12,7 +12,7 @@ import { IConfigurationRegistry, Extensions as ConfigurationExtensions, Configur import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; import { isWindows, isLinux, isMacintosh } from 'vs/base/common/platform'; -import { KeybindingsReferenceAction, OpenDocumentationUrlAction, OpenIntroductoryVideosUrlAction, OpenTipsAndTricksUrlAction, OpenIssueReporterAction, ReportPerformanceIssueUsingReporterAction, NavigateUpAction, NavigateDownAction, NavigateLeftAction, NavigateRightAction, IncreaseViewSizeAction, DecreaseViewSizeAction, OpenProcessExplorer, OpenTwitterUrlAction, OpenRequestFeatureUrlAction, OpenPrivacyStatementUrlAction, OpenLicenseUrlAction } from 'vs/workbench/electron-browser/actions'; +import { KeybindingsReferenceAction, OpenDocumentationUrlAction, OpenIntroductoryVideosUrlAction, OpenTipsAndTricksUrlAction, OpenIssueReporterAction, ReportPerformanceIssueUsingReporterAction, IncreaseViewSizeAction, DecreaseViewSizeAction, OpenProcessExplorer, OpenTwitterUrlAction, OpenRequestFeatureUrlAction, OpenPrivacyStatementUrlAction, OpenLicenseUrlAction } from 'vs/workbench/electron-browser/actions'; import { ToggleSharedProcessAction, InspectContextKeysAction, ToggleScreencastModeAction } from 'vs/workbench/electron-browser/actions/developerActions'; import { ShowAboutDialogAction, ZoomResetAction, ZoomOutAction, ZoomInAction, ToggleFullScreenAction, CloseCurrentWindowAction, SwitchWindow, NewWindowAction, QuickSwitchWindow, QuickOpenRecentAction, inRecentFilesPickerContextKey, OpenRecentAction } from 'vs/workbench/electron-browser/actions/windowActions'; import { AddRootFolderAction, GlobalRemoveRootFolderAction, OpenWorkspaceAction, SaveWorkspaceAsAction, OpenWorkspaceConfigFileAction, DuplicateWorkspaceInNewWindowAction, OpenFileFolderAction, OpenFileAction, OpenFolderAction, CloseWorkspaceAction } from 'vs/workbench/browser/actions/workspaceActions'; @@ -118,10 +118,6 @@ workbenchActionsRegistry.registerWorkbenchAction( ); workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleFullScreenAction, ToggleFullScreenAction.ID, ToggleFullScreenAction.LABEL, { primary: KeyCode.F11, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_F } }), 'View: Toggle Full Screen', viewCategory); -workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(NavigateUpAction, NavigateUpAction.ID, NavigateUpAction.LABEL, undefined), 'View: Navigate to the View Above', viewCategory); -workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(NavigateDownAction, NavigateDownAction.ID, NavigateDownAction.LABEL, undefined), 'View: Navigate to the View Below', viewCategory); -workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(NavigateLeftAction, NavigateLeftAction.ID, NavigateLeftAction.LABEL, undefined), 'View: Navigate to the View on the Left', viewCategory); -workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(NavigateRightAction, NavigateRightAction.ID, NavigateRightAction.LABEL, undefined), 'View: Navigate to the View on the Right', viewCategory); workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(IncreaseViewSizeAction, IncreaseViewSizeAction.ID, IncreaseViewSizeAction.LABEL, undefined), 'View: Increase Current View Size', viewCategory); workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(DecreaseViewSizeAction, DecreaseViewSizeAction.ID, DecreaseViewSizeAction.LABEL, undefined), 'View: Decrease Current View Size', viewCategory); diff --git a/src/vs/workbench/workbench.main.ts b/src/vs/workbench/workbench.main.ts index f537bf6f15a..448a34dabbb 100644 --- a/src/vs/workbench/workbench.main.ts +++ b/src/vs/workbench/workbench.main.ts @@ -28,6 +28,7 @@ import 'vs/workbench/parts/localizations/electron-browser/localizations.contribu // Workbench import 'vs/workbench/browser/actions/layoutActions'; import 'vs/workbench/browser/actions/listCommands'; +import 'vs/workbench/browser/actions/navigationActions'; import 'vs/workbench/parts/preferences/electron-browser/preferences.contribution'; import 'vs/workbench/parts/preferences/browser/keybindingsEditorContribution'; import 'vs/workbench/parts/logs/electron-browser/logs.contribution'; From 41da9cc7c865d2eac2f767ad1ef75231d5e21d06 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 24 Jan 2019 16:17:27 +0100 Subject: [PATCH 090/274] fixes #66904 --- src/vs/base/browser/ui/tree/abstractTree.ts | 50 ++++++++++++++++++--- 1 file changed, 43 insertions(+), 7 deletions(-) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 8ed9de83d04..5ba1cb06b4f 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -278,10 +278,16 @@ class TreeRenderer implements IListRenderer implements ITreeFilter { +class TypeFilter implements ITreeFilter, IDisposable { + + private _totalCount = 0; + get totalCount(): number { return this._totalCount; } + private _matchCount = 0; + get matchCount(): number { return this._matchCount; } private _pattern: string; private _lowercasePattern: string; + private disposables: IDisposable[] = []; set pattern(pattern: string) { this._pattern = pattern; @@ -292,7 +298,9 @@ class TypeFilter implements ITreeFilter { private tree: AbstractTree, private keyboardNavigationLabelProvider: IKeyboardNavigationLabelProvider, private _filter?: ITreeFilter - ) { } + ) { + tree.onWillRefilter(this.reset, this, this.disposables); + } filter(element: T, parentVisibility: TreeVisibility): TreeFilterResult { if (this._filter) { @@ -317,7 +325,10 @@ class TypeFilter implements ITreeFilter { } } + this._totalCount++; + if (this.tree.options.simpleKeyboardNavigation || !this._pattern) { + this._matchCount++; return { data: FuzzyScore.Default, visibility: true }; } @@ -335,8 +346,18 @@ class TypeFilter implements ITreeFilter { // return parentVisibility === TreeVisibility.Visible ? true : TreeVisibility.Recurse; } + this._matchCount++; return { data: score, visibility: true }; } + + private reset(): void { + this._totalCount = 0; + this._matchCount = 0; + } + + dispose(): void { + this.disposables = dispose(this.disposables); + } } class TypeFilterController implements IDisposable { @@ -629,6 +650,9 @@ export abstract class AbstractTree implements IDisposable get onDidChangeCollapseState(): Event> { return this.model.onDidChangeCollapseState; } get onDidChangeRenderNodeCount(): Event> { return this.model.onDidChangeRenderNodeCount; } + private _onWillRefilter = new Emitter(); + readonly onWillRefilter: Event = this._onWillRefilter.event; + get onDidDispose(): Event { return this.view.onDidDispose; } constructor( @@ -643,11 +667,12 @@ export abstract class AbstractTree implements IDisposable this.renderers = renderers.map(r => new TreeRenderer(r, onDidChangeCollapseStateRelay.event, _options)); this.disposables.push(...this.renderers); - let filter: ITreeFilter | undefined; + let filter: TypeFilter | undefined; if (_options.keyboardNavigationLabelProvider) { - filter = new TypeFilter(this, _options.keyboardNavigationLabelProvider, _options.filter as ITreeFilter) as ITreeFilter; // TODO need typescript help here - _options = { ..._options, filter }; + filter = new TypeFilter(this, _options.keyboardNavigationLabelProvider, _options.filter as ITreeFilter); + _options = { ..._options, filter: filter as ITreeFilter }; // TODO need typescript help here + this.disposables.push(filter); } this.view = new List(container, treeDelegate, this.renderers, asListOptions(() => this.model, _options)); @@ -668,8 +693,18 @@ export abstract class AbstractTree implements IDisposable } if (_options.keyboardNavigationLabelProvider) { - const typeFilterController = new TypeFilterController(this, this.model, this.view, filter as TypeFilter, _options.keyboardNavigationLabelProvider); - this.focusNavigationFilter = node => !typeFilterController.pattern || !FuzzyScore.isDefault(node.filterData as any as FuzzyScore); // TODO@joao + const typeFilterController = new TypeFilterController(this, this.model, this.view, filter, _options.keyboardNavigationLabelProvider); + this.focusNavigationFilter = node => { + if (!typeFilterController.pattern) { + return true; + } + + if (filter.totalCount > 0 && filter.matchCount === 0) { + return true; + } + + return !FuzzyScore.isDefault(node.filterData as any as FuzzyScore); + }; this.disposables.push(typeFilterController); } } @@ -784,6 +819,7 @@ export abstract class AbstractTree implements IDisposable } refilter(): void { + this._onWillRefilter.fire(undefined); this.model.refilter(); } From 02f861ad1efcb5f8c626717bb5339a36409c5088 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 24 Jan 2019 16:34:47 +0100 Subject: [PATCH 091/274] show warning border when type filter doesnt match anything related to #66904 --- src/vs/base/browser/ui/list/list.css | 1 + src/vs/base/browser/ui/list/listWidget.ts | 5 ++++ src/vs/base/browser/ui/tree/abstractTree.ts | 31 +++++++++++---------- src/vs/platform/theme/common/styler.ts | 2 ++ 4 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/vs/base/browser/ui/list/list.css b/src/vs/base/browser/ui/list/list.css index 5c0cfcf3a85..88e9c43e78b 100644 --- a/src/vs/base/browser/ui/list/list.css +++ b/src/vs/base/browser/ui/list/list.css @@ -67,6 +67,7 @@ .monaco-list-type-filter { display: flex; + align-items: center; position: absolute; border-radius: 2px; padding: 0px 3px; diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 32b806ca6b1..13daf5d8072 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -764,6 +764,10 @@ export class DefaultStyleController implements IStyleController { content.push(`.monaco-list-type-filter { border: 1px solid ${styles.listMatchesOutline}; }`); } + if (styles.listNoMatchesOutline) { + content.push(`.monaco-list-type-filter.no-matches { border: 1px solid ${styles.listNoMatchesOutline}; }`); + } + if (styles.listMatchesShadow) { content.push(`.monaco-list-type-filter { box-shadow: 1px 1px 1px ${styles.listMatchesShadow}; }`); } @@ -815,6 +819,7 @@ export interface IListStyles { listHoverOutline?: Color; listMatchesBackground?: Color; listMatchesOutline?: Color; + listNoMatchesOutline?: Color; listMatchesShadow?: Color; } diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 5ba1cb06b4f..cf6122a3b6b 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -420,7 +420,7 @@ class TypeFilterController implements IDisposable { this.filterOnTypeDomNode.checked = !!options.filterOnType; this.tree.refilter(); - this.updateMessage(); + this.render(); } private enable(): void { @@ -454,7 +454,7 @@ class TypeFilterController implements IDisposable { onInput(this.onInput, this, this.enabledDisposables); this.filter.pattern = ''; this.tree.refilter(); - this.updateMessage(); + this.render(); this.enabled = true; } @@ -466,7 +466,7 @@ class TypeFilterController implements IDisposable { this.domNode.remove(); this.enabledDisposables = dispose(this.enabledDisposables); this.tree.refilter(); - this.updateMessage(); + this.render(); this.enabled = false; } @@ -480,16 +480,11 @@ class TypeFilterController implements IDisposable { this.tree.domFocus(); } - this.labelDomNode.textContent = pattern.length > 16 - ? '…' + pattern.substr(pattern.length - 16) - : pattern; - this._pattern = pattern; this.filter.pattern = pattern; this.tree.refilter(); this.tree.focusNext(0, true); - - this.updateMessage(); + this.render(); } private onDragStart(): void { @@ -550,14 +545,14 @@ class TypeFilterController implements IDisposable { private onDidSpliceModel(): void { this.tree.refilter(); - this.updateMessage(); + this.render(); } private onDidChangeFilterOnType(): void { this.tree.updateOptions({ filterOnType: this.filterOnTypeDomNode.checked }); this.tree.refilter(); this.tree.domFocus(); - this.updateMessage(); + this.render(); this.updateFilterOnTypeTitle(); } @@ -569,12 +564,18 @@ class TypeFilterController implements IDisposable { } } - private updateMessage(): void { - if (this.pattern && this.view.length === 0) { + private render(): void { + const noMatches = this.filter.totalCount > 0 && this.filter.matchCount === 0; + + if (this.pattern && this.tree.options.filterOnType && noMatches) { this.messageDomNode.textContent = localize('empty', "No elements found"); } else { this.messageDomNode.innerHTML = ''; } + + toggleClass(this.domNode, 'no-matches', noMatches); + this.domNode.title = localize('found', "Matched {0} out of {1} elements", this.filter.matchCount, this.filter.totalCount); + this.labelDomNode.textContent = this.pattern.length > 16 ? '…' + this.pattern.substr(this.pattern.length - 16) : this.pattern; } dispose() { @@ -693,13 +694,13 @@ export abstract class AbstractTree implements IDisposable } if (_options.keyboardNavigationLabelProvider) { - const typeFilterController = new TypeFilterController(this, this.model, this.view, filter, _options.keyboardNavigationLabelProvider); + const typeFilterController = new TypeFilterController(this, this.model, this.view, filter!, _options.keyboardNavigationLabelProvider); this.focusNavigationFilter = node => { if (!typeFilterController.pattern) { return true; } - if (filter.totalCount > 0 && filter.matchCount === 0) { + if (filter!.totalCount > 0 && filter!.matchCount === 0) { return true; } diff --git a/src/vs/platform/theme/common/styler.ts b/src/vs/platform/theme/common/styler.ts index f14f28c3f5c..a1f3ee581d6 100644 --- a/src/vs/platform/theme/common/styler.ts +++ b/src/vs/platform/theme/common/styler.ts @@ -225,6 +225,7 @@ export interface IListStyleOverrides extends IStyleOverrides { listHoverOutline?: ColorIdentifier; listMatchesBackground?: ColorIdentifier; listMatchesOutline?: ColorIdentifier; + listNoMatchesOutline?: ColorIdentifier; listMatchesShadow?: ColorIdentifier; } @@ -249,6 +250,7 @@ export const defaultListStyles: IColorMapping = { listSelectionOutline: activeContrastBorder, listHoverOutline: activeContrastBorder, listMatchesBackground: blend2(editorFindMatchHighlight, editorWidgetBackground), + listNoMatchesOutline: inputValidationWarningBorder, listMatchesShadow: widgetShadow }; From 8a086d4d30d1a7e92401a90635badb5d50ff203e Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 24 Jan 2019 16:39:04 +0100 Subject: [PATCH 092/274] remove todo --- src/vs/base/browser/ui/tree/asyncDataTree.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index 4f308bf86f5..b982e323d45 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -255,7 +255,6 @@ export class AsyncDataTree implements IDisposable private readonly nodes = new Map>(); private readonly currentRefreshCalls = new Map, Promise>(); - // TODO@joao remove this private readonly refreshPromises = new Map, CancelablePromise>(); private readonly identityProvider?: IIdentityProvider; private readonly autoExpandSingleChildren: boolean; From 6b7808f337b115084e188852304dd578bd106143 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 24 Jan 2019 16:51:29 +0100 Subject: [PATCH 093/274] explorer: expand all roots when transitioning from one folder to MR workspace --- .../parts/files/electron-browser/views/explorerView.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts index 2ace95f1c79..fbaa7c3367a 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -442,9 +442,10 @@ export class ExplorerView extends ViewletPanel { viewState = JSON.parse(rawViewState) as IAsyncDataTreeViewState; } + const previousInput = this.tree.getInput(); const promise = this.tree.setInput(input, viewState).then(() => { - if (!viewState && Array.isArray(input)) { - // There is no view state for this workspace, expand all roots. + if (Array.isArray(input) && (!viewState || previousInput instanceof ExplorerItem)) { + // There is no view state for this workspace, expand all roots. Or we transitioned from a folder workspace. input.forEach(item => this.tree.expand(item).then(undefined, onUnexpectedError)); } if (initialInputSetup) { From d9fb14589188e5706221f02f2b6dc5e321285424 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 24 Jan 2019 08:02:30 -0800 Subject: [PATCH 094/274] expose Promise#finally to standalone editor, #67027 --- src/tsconfig.monaco.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tsconfig.monaco.json b/src/tsconfig.monaco.json index 1bf4732a4a0..1303af547f4 100644 --- a/src/tsconfig.monaco.json +++ b/src/tsconfig.monaco.json @@ -19,6 +19,7 @@ "./typings/require-monaco.d.ts", "typings/thenable.d.ts", "typings/es6-promise.d.ts", + "typings/lib.es2018.promise.d.ts", "typings/lib.array-ext.d.ts", "typings/lib.ie11_safe_es6.d.ts", "vs/css.d.ts", @@ -35,4 +36,4 @@ "exclude": [ "node_modules/*" ] -} \ No newline at end of file +} From 403646f883b56fbaa8d6538fe067ed905918faf5 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 24 Jan 2019 08:07:31 -0800 Subject: [PATCH 095/274] a little more finally, #67027 --- src/vs/editor/browser/editorExtensions.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/browser/editorExtensions.ts b/src/vs/editor/browser/editorExtensions.ts index 071893e858b..15365b311a9 100644 --- a/src/vs/editor/browser/editorExtensions.ts +++ b/src/vs/editor/browser/editorExtensions.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { IPosition } from 'vs/base/browser/ui/contextview/contextview'; -import { always } from 'vs/base/common/async'; import { illegalArgument } from 'vs/base/common/errors'; import { URI } from 'vs/base/common/uri'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; @@ -266,14 +265,14 @@ export function registerDefaultLanguageCommand(id: string, handler: (model: ITex } return accessor.get(ITextModelService).createModelReference(resource).then(reference => { - return always(new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { try { let result = handler(reference.object.textEditorModel, Position.lift(position), args); resolve(result); } catch (err) { reject(err); } - }), () => { + }).finally(() => { reference.dispose(); }); }); From 28f6902b8961f993e81596e00719e21a4c15fd4e Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 24 Jan 2019 17:04:22 +0100 Subject: [PATCH 096/274] debt - extract view size actions --- .../browser/actions/layoutActions.ts | 79 ++++++++++++++++++- src/vs/workbench/electron-browser/actions.ts | 74 ----------------- .../electron-browser/main.contribution.ts | 5 +- 3 files changed, 78 insertions(+), 80 deletions(-) diff --git a/src/vs/workbench/browser/actions/layoutActions.ts b/src/vs/workbench/browser/actions/layoutActions.ts index 10e1db37351..fa168b918d7 100644 --- a/src/vs/workbench/browser/actions/layoutActions.ts +++ b/src/vs/workbench/browser/actions/layoutActions.ts @@ -355,7 +355,6 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ primary: KeyChord(KeyCode.Escape, KeyCode.Escape) }); - // --- Toggle Menu Bar export class ToggleMenuBarAction extends Action { @@ -404,4 +403,80 @@ MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { }, when: IsMacContext.toNegated(), order: 4 -}); \ No newline at end of file +}); + +// --- Resize View + +export abstract class BaseResizeViewAction extends Action { + + protected static RESIZE_INCREMENT = 6.5; // This is a media-size percentage + + constructor( + id: string, + label: string, + @IPartService protected partService: IPartService + ) { + super(id, label); + } + + protected resizePart(sizeChange: number): void { + const isEditorFocus = this.partService.hasFocus(Parts.EDITOR_PART); + const isSidebarFocus = this.partService.hasFocus(Parts.SIDEBAR_PART); + const isPanelFocus = this.partService.hasFocus(Parts.PANEL_PART); + + let part: Parts | undefined; + if (isSidebarFocus) { + part = Parts.SIDEBAR_PART; + } else if (isPanelFocus) { + part = Parts.PANEL_PART; + } else if (isEditorFocus) { + part = Parts.EDITOR_PART; + } + + if (part) { + this.partService.resizePart(part, sizeChange); + } + } +} + +export class IncreaseViewSizeAction extends BaseResizeViewAction { + + static readonly ID = 'workbench.action.increaseViewSize'; + static readonly LABEL = nls.localize('increaseViewSize', "Increase Current View Size"); + + constructor( + id: string, + label: string, + @IPartService partService: IPartService + ) { + super(id, label, partService); + } + + run(): Promise { + this.resizePart(BaseResizeViewAction.RESIZE_INCREMENT); + return Promise.resolve(true); + } +} + +export class DecreaseViewSizeAction extends BaseResizeViewAction { + + static readonly ID = 'workbench.action.decreaseViewSize'; + static readonly LABEL = nls.localize('decreaseViewSize', "Decrease Current View Size"); + + constructor( + id: string, + label: string, + @IPartService partService: IPartService + + ) { + super(id, label, partService); + } + + run(): Promise { + this.resizePart(-BaseResizeViewAction.RESIZE_INCREMENT); + return Promise.resolve(true); + } +} + +registry.registerWorkbenchAction(new SyncActionDescriptor(IncreaseViewSizeAction, IncreaseViewSizeAction.ID, IncreaseViewSizeAction.LABEL, undefined), 'View: Increase Current View Size', viewCategory); +registry.registerWorkbenchAction(new SyncActionDescriptor(DecreaseViewSizeAction, DecreaseViewSizeAction.ID, DecreaseViewSizeAction.LABEL, undefined), 'View: Decrease Current View Size', viewCategory); diff --git a/src/vs/workbench/electron-browser/actions.ts b/src/vs/workbench/electron-browser/actions.ts index 389a6747173..25c0b2388ef 100644 --- a/src/vs/workbench/electron-browser/actions.ts +++ b/src/vs/workbench/electron-browser/actions.ts @@ -7,7 +7,6 @@ import { Action } from 'vs/base/common/actions'; import * as nls from 'vs/nls'; import product from 'vs/platform/node/product'; import { isMacintosh, isLinux, language } from 'vs/base/common/platform'; -import { IPartService, Parts } from 'vs/workbench/services/part/common/partService'; import { shell } from 'electron'; import { IssueType } from 'vs/platform/issue/common/issue'; import { IWorkbenchIssueService } from 'vs/workbench/services/issue/common/issue'; @@ -153,79 +152,6 @@ export class OpenTipsAndTricksUrlAction extends Action { } } -// Resize focused view actions -export abstract class BaseResizeViewAction extends Action { - - // This is a media-size percentage - protected static RESIZE_INCREMENT = 6.5; - - constructor( - id: string, - label: string, - @IPartService protected partService: IPartService - ) { - super(id, label); - } - - protected resizePart(sizeChange: number): void { - const isEditorFocus = this.partService.hasFocus(Parts.EDITOR_PART); - const isSidebarFocus = this.partService.hasFocus(Parts.SIDEBAR_PART); - const isPanelFocus = this.partService.hasFocus(Parts.PANEL_PART); - - let part: Parts | undefined; - if (isSidebarFocus) { - part = Parts.SIDEBAR_PART; - } else if (isPanelFocus) { - part = Parts.PANEL_PART; - } else if (isEditorFocus) { - part = Parts.EDITOR_PART; - } - - if (part) { - this.partService.resizePart(part, sizeChange); - } - } -} - -export class IncreaseViewSizeAction extends BaseResizeViewAction { - - static readonly ID = 'workbench.action.increaseViewSize'; - static readonly LABEL = nls.localize('increaseViewSize', "Increase Current View Size"); - - constructor( - id: string, - label: string, - @IPartService partService: IPartService - ) { - super(id, label, partService); - } - - run(): Promise { - this.resizePart(BaseResizeViewAction.RESIZE_INCREMENT); - return Promise.resolve(true); - } -} - -export class DecreaseViewSizeAction extends BaseResizeViewAction { - - static readonly ID = 'workbench.action.decreaseViewSize'; - static readonly LABEL = nls.localize('decreaseViewSize', "Decrease Current View Size"); - - constructor( - id: string, - label: string, - @IPartService partService: IPartService - - ) { - super(id, label, partService); - } - - run(): Promise { - this.resizePart(-BaseResizeViewAction.RESIZE_INCREMENT); - return Promise.resolve(true); - } -} - export class OpenTwitterUrlAction extends Action { static readonly ID = 'workbench.action.openTwitterUrl'; diff --git a/src/vs/workbench/electron-browser/main.contribution.ts b/src/vs/workbench/electron-browser/main.contribution.ts index 932dab0e30c..623c7375eaf 100644 --- a/src/vs/workbench/electron-browser/main.contribution.ts +++ b/src/vs/workbench/electron-browser/main.contribution.ts @@ -12,7 +12,7 @@ import { IConfigurationRegistry, Extensions as ConfigurationExtensions, Configur import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; import { isWindows, isLinux, isMacintosh } from 'vs/base/common/platform'; -import { KeybindingsReferenceAction, OpenDocumentationUrlAction, OpenIntroductoryVideosUrlAction, OpenTipsAndTricksUrlAction, OpenIssueReporterAction, ReportPerformanceIssueUsingReporterAction, IncreaseViewSizeAction, DecreaseViewSizeAction, OpenProcessExplorer, OpenTwitterUrlAction, OpenRequestFeatureUrlAction, OpenPrivacyStatementUrlAction, OpenLicenseUrlAction } from 'vs/workbench/electron-browser/actions'; +import { KeybindingsReferenceAction, OpenDocumentationUrlAction, OpenIntroductoryVideosUrlAction, OpenTipsAndTricksUrlAction, OpenIssueReporterAction, ReportPerformanceIssueUsingReporterAction, OpenProcessExplorer, OpenTwitterUrlAction, OpenRequestFeatureUrlAction, OpenPrivacyStatementUrlAction, OpenLicenseUrlAction } from 'vs/workbench/electron-browser/actions'; import { ToggleSharedProcessAction, InspectContextKeysAction, ToggleScreencastModeAction } from 'vs/workbench/electron-browser/actions/developerActions'; import { ShowAboutDialogAction, ZoomResetAction, ZoomOutAction, ZoomInAction, ToggleFullScreenAction, CloseCurrentWindowAction, SwitchWindow, NewWindowAction, QuickSwitchWindow, QuickOpenRecentAction, inRecentFilesPickerContextKey, OpenRecentAction } from 'vs/workbench/electron-browser/actions/windowActions'; import { AddRootFolderAction, GlobalRemoveRootFolderAction, OpenWorkspaceAction, SaveWorkspaceAsAction, OpenWorkspaceConfigFileAction, DuplicateWorkspaceInNewWindowAction, OpenFileFolderAction, OpenFileAction, OpenFolderAction, CloseWorkspaceAction } from 'vs/workbench/browser/actions/workspaceActions'; @@ -119,9 +119,6 @@ workbenchActionsRegistry.registerWorkbenchAction( workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleFullScreenAction, ToggleFullScreenAction.ID, ToggleFullScreenAction.LABEL, { primary: KeyCode.F11, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_F } }), 'View: Toggle Full Screen', viewCategory); -workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(IncreaseViewSizeAction, IncreaseViewSizeAction.ID, IncreaseViewSizeAction.LABEL, undefined), 'View: Increase Current View Size', viewCategory); -workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(DecreaseViewSizeAction, DecreaseViewSizeAction.ID, DecreaseViewSizeAction.LABEL, undefined), 'View: Decrease Current View Size', viewCategory); - const workspacesCategory = nls.localize('workspaces', "Workspaces"); workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(AddRootFolderAction, AddRootFolderAction.ID, AddRootFolderAction.LABEL), 'Workspaces: Add Folder to Workspace...', workspacesCategory); workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(GlobalRemoveRootFolderAction, GlobalRemoveRootFolderAction.ID, GlobalRemoveRootFolderAction.LABEL), 'Workspaces: Remove Folder from Workspace...', workspacesCategory); From d49b0a5a64ad32db7df57693687f56213cb18d6b Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 24 Jan 2019 17:13:34 +0100 Subject: [PATCH 097/274] debt - extract help actions --- src/tsconfig.strictNullChecks.json | 5 ++- .../{actions.ts => actions/helpActions.ts} | 38 +++++++++---------- .../electron-browser/main.contribution.ts | 2 +- 3 files changed, 22 insertions(+), 23 deletions(-) rename src/vs/workbench/electron-browser/{actions.ts => actions/helpActions.ts} (86%) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index 196efef48d9..838b9d7033e 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -515,6 +515,7 @@ "./vs/workbench/api/shared/tasks.ts", "./vs/workbench/browser/actions.ts", "./vs/workbench/browser/actions/layoutActions.ts", + "./vs/workbench/browser/actions/navigationActions.ts", "./vs/workbench/browser/actions/workspaceActions.ts", "./vs/workbench/browser/actions/workspaceCommands.ts", "./vs/workbench/browser/composite.ts", @@ -571,7 +572,9 @@ "./vs/workbench/common/theme.ts", "./vs/workbench/common/viewlet.ts", "./vs/workbench/common/views.ts", - "./vs/workbench/electron-browser/actions.ts", + "./vs/workbench/electron-browser/actions/helpActions", + "./vs/workbench/electron-browser/actions/developerActions", + "./vs/workbench/electron-browser/actions/windowActions", "./vs/workbench/electron-browser/resources.ts", "./vs/workbench/electron-browser/window.ts", "./vs/workbench/parts/backup/common/backupRestorer.ts", diff --git a/src/vs/workbench/electron-browser/actions.ts b/src/vs/workbench/electron-browser/actions/helpActions.ts similarity index 86% rename from src/vs/workbench/electron-browser/actions.ts rename to src/vs/workbench/electron-browser/actions/helpActions.ts index 25c0b2388ef..f6c1190b82d 100644 --- a/src/vs/workbench/electron-browser/actions.ts +++ b/src/vs/workbench/electron-browser/actions/helpActions.ts @@ -7,12 +7,9 @@ import { Action } from 'vs/base/common/actions'; import * as nls from 'vs/nls'; import product from 'vs/platform/node/product'; import { isMacintosh, isLinux, language } from 'vs/base/common/platform'; -import { shell } from 'electron'; import { IssueType } from 'vs/platform/issue/common/issue'; import { IWorkbenchIssueService } from 'vs/workbench/services/issue/common/issue'; -// --- actions - export class OpenIssueReporterAction extends Action { static readonly ID = 'workbench.action.openIssueReporter'; static readonly LABEL = nls.localize({ key: 'reportIssueInEnglish', comment: ['Translate this to "Report Issue in English" in all languages please!'] }, "Report Issue"); @@ -26,8 +23,7 @@ export class OpenIssueReporterAction extends Action { } run(): Promise { - return this.issueService.openReporter() - .then(() => true); + return this.issueService.openReporter().then(() => true); } } @@ -44,8 +40,7 @@ export class OpenProcessExplorer extends Action { } run(): Promise { - return this.issueService.openProcessExplorer() - .then(() => true); + return this.issueService.openProcessExplorer().then(() => true); } } @@ -62,9 +57,7 @@ export class ReportPerformanceIssueUsingReporterAction extends Action { } run(): Promise { - // TODO: Reporter should send timings table as well - return this.issueService.openReporter({ issueType: IssueType.PerformanceIssue }) - .then(() => true); + return this.issueService.openReporter({ issueType: IssueType.PerformanceIssue }).then(() => true); } } @@ -85,6 +78,7 @@ export class KeybindingsReferenceAction extends Action { run(): Promise { window.open(KeybindingsReferenceAction.URL); + return Promise.resolve(); } } @@ -106,6 +100,7 @@ export class OpenDocumentationUrlAction extends Action { run(): Promise { window.open(OpenDocumentationUrlAction.URL); + return Promise.resolve(); } } @@ -127,6 +122,7 @@ export class OpenIntroductoryVideosUrlAction extends Action { run(): Promise { window.open(OpenIntroductoryVideosUrlAction.URL); + return Promise.resolve(); } } @@ -148,6 +144,7 @@ export class OpenTipsAndTricksUrlAction extends Action { run(): Promise { window.open(OpenTipsAndTricksUrlAction.URL); + return Promise.resolve(); } } @@ -166,10 +163,10 @@ export class OpenTwitterUrlAction extends Action { run(): Promise { if (product.twitterUrl) { - return Promise.resolve(shell.openExternal(product.twitterUrl)); + window.open(product.twitterUrl); } - return Promise.resolve(false); + return Promise.resolve(); } } @@ -187,10 +184,10 @@ export class OpenRequestFeatureUrlAction extends Action { run(): Promise { if (product.requestFeatureUrl) { - return Promise.resolve(shell.openExternal(product.requestFeatureUrl)); + window.open(product.requestFeatureUrl); } - return Promise.resolve(false); + return Promise.resolve(); } } @@ -210,13 +207,13 @@ export class OpenLicenseUrlAction extends Action { if (product.licenseUrl) { if (language) { const queryArgChar = product.licenseUrl.indexOf('?') > 0 ? '&' : '?'; - return Promise.resolve(shell.openExternal(`${product.licenseUrl}${queryArgChar}lang=${language}`)); + window.open(`${product.licenseUrl}${queryArgChar}lang=${language}`); } else { - return Promise.resolve(shell.openExternal(product.licenseUrl)); + window.open(product.licenseUrl); } } - return Promise.resolve(false); + return Promise.resolve(); } } @@ -236,14 +233,13 @@ export class OpenPrivacyStatementUrlAction extends Action { if (product.privacyStatementUrl) { if (language) { const queryArgChar = product.privacyStatementUrl.indexOf('?') > 0 ? '&' : '?'; - return Promise.resolve(shell.openExternal(`${product.privacyStatementUrl}${queryArgChar}lang=${language}`)); + window.open(`${product.privacyStatementUrl}${queryArgChar}lang=${language}`); } else { - return Promise.resolve(shell.openExternal(product.privacyStatementUrl)); + window.open(product.privacyStatementUrl); } } - - return Promise.resolve(false); + return Promise.resolve(); } } diff --git a/src/vs/workbench/electron-browser/main.contribution.ts b/src/vs/workbench/electron-browser/main.contribution.ts index 623c7375eaf..8ce0542d0fa 100644 --- a/src/vs/workbench/electron-browser/main.contribution.ts +++ b/src/vs/workbench/electron-browser/main.contribution.ts @@ -12,7 +12,7 @@ import { IConfigurationRegistry, Extensions as ConfigurationExtensions, Configur import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; import { isWindows, isLinux, isMacintosh } from 'vs/base/common/platform'; -import { KeybindingsReferenceAction, OpenDocumentationUrlAction, OpenIntroductoryVideosUrlAction, OpenTipsAndTricksUrlAction, OpenIssueReporterAction, ReportPerformanceIssueUsingReporterAction, OpenProcessExplorer, OpenTwitterUrlAction, OpenRequestFeatureUrlAction, OpenPrivacyStatementUrlAction, OpenLicenseUrlAction } from 'vs/workbench/electron-browser/actions'; +import { KeybindingsReferenceAction, OpenDocumentationUrlAction, OpenIntroductoryVideosUrlAction, OpenTipsAndTricksUrlAction, OpenIssueReporterAction, ReportPerformanceIssueUsingReporterAction, OpenProcessExplorer, OpenTwitterUrlAction, OpenRequestFeatureUrlAction, OpenPrivacyStatementUrlAction, OpenLicenseUrlAction } from 'vs/workbench/electron-browser/actions/helpActions'; import { ToggleSharedProcessAction, InspectContextKeysAction, ToggleScreencastModeAction } from 'vs/workbench/electron-browser/actions/developerActions'; import { ShowAboutDialogAction, ZoomResetAction, ZoomOutAction, ZoomInAction, ToggleFullScreenAction, CloseCurrentWindowAction, SwitchWindow, NewWindowAction, QuickSwitchWindow, QuickOpenRecentAction, inRecentFilesPickerContextKey, OpenRecentAction } from 'vs/workbench/electron-browser/actions/windowActions'; import { AddRootFolderAction, GlobalRemoveRootFolderAction, OpenWorkspaceAction, SaveWorkspaceAsAction, OpenWorkspaceConfigFileAction, DuplicateWorkspaceInNewWindowAction, OpenFileFolderAction, OpenFileAction, OpenFolderAction, CloseWorkspaceAction } from 'vs/workbench/browser/actions/workspaceActions'; From fad75f17dfecbbc9c6a078566ee70dd965a8bafc Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 24 Jan 2019 17:31:50 +0100 Subject: [PATCH 098/274] explorer: do not listen on onDidChangeWorkbenchState --- .../workbench/parts/files/electron-browser/views/explorerView.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts index fbaa7c3367a..5c472592909 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -159,7 +159,6 @@ export class ExplorerView extends ViewletPanel { this.toolbar.setActions(this.getActions(), this.getSecondaryActions())(); } - this.disposables.push(this.contextService.onDidChangeWorkbenchState(() => this.setTreeInput())); this.disposables.push(this.labelService.onDidChangeFormatters(() => { this._onDidChangeTitleArea.fire(); this.refresh(); From fd9fbbfd507d7db9eafd86fc746b574e1b86d790 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 24 Jan 2019 17:36:45 +0100 Subject: [PATCH 099/274] explorer: expand newly added roots --- .../files/electron-browser/views/explorerView.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts index 5c472592909..46360bd1759 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -443,9 +443,15 @@ export class ExplorerView extends ViewletPanel { const previousInput = this.tree.getInput(); const promise = this.tree.setInput(input, viewState).then(() => { - if (Array.isArray(input) && (!viewState || previousInput instanceof ExplorerItem)) { - // There is no view state for this workspace, expand all roots. Or we transitioned from a folder workspace. - input.forEach(item => this.tree.expand(item).then(undefined, onUnexpectedError)); + if (Array.isArray(input)) { + if (!viewState || previousInput instanceof ExplorerItem) { + // There is no view state for this workspace, expand all roots. Or we transitioned from a folder workspace. + input.forEach(item => this.tree.expand(item).then(undefined, onUnexpectedError)); + } + if (Array.isArray(previousInput) && previousInput.length < input.length) { + // Roots added to the explorer -> expand them. + input.slice(previousInput.length).forEach(item => this.tree.expand(item).then(undefined, onUnexpectedError)); + } } if (initialInputSetup) { perf.mark('didResolveExplorer'); From b4c5a1a1ea894171cb0a75b23f67bf60c8ccd0aa Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 24 Jan 2019 08:34:04 -0800 Subject: [PATCH 100/274] fix #67029 --- src/vs/editor/contrib/suggest/suggestWidget.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/suggest/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts index 347de4b6499..b1cb1d7f32b 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -157,7 +157,7 @@ class Renderer implements IListRenderer }; let color: string | null = null; - if (suggestion.kind === CompletionItemKind.Color && ((color = matchesColor(suggestion.label)) || typeof suggestion.documentation === 'string' && matchesColor(suggestion.documentation))) { + if (suggestion.kind === CompletionItemKind.Color && ((color = matchesColor(suggestion.label) || typeof suggestion.documentation === 'string' && matchesColor(suggestion.documentation)))) { // special logic for 'color' completion items data.icon.className = 'icon customcolor'; data.colorspan.style.backgroundColor = color; From dc9777458bb7b0a28a9e13658937c6abdbb5ce43 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 24 Jan 2019 08:51:41 -0800 Subject: [PATCH 101/274] add (failing) test for #66923 --- src/vs/base/test/common/filters.test.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/base/test/common/filters.test.ts b/src/vs/base/test/common/filters.test.ts index d8f84b0aced..4aeedc57f01 100644 --- a/src/vs/base/test/common/filters.test.ts +++ b/src/vs/base/test/common/filters.test.ts @@ -449,4 +449,8 @@ suite('Filters', () => { assertMatches('cno', 'co_new', '^c^o_^new', fuzzyScoreGraceful); assertMatches('cno', 'co_new', '^c^o_^new', fuzzyScoreGracefulAggressive); }); + + // test('List highlight filter: Not all characters from match are highlighterd #66923', () => { + // assertMatches('foo', 'barbarbarbarbarbarbarbarbarbarbarbarbarbarbarbar_foo', 'barbarbarbarbarbarbarbarbarbarbarbarbarbarbarbar_^f^o^o', fuzzyScore); + // }); }); From aca24779fbcce7c1b523df39d2592b249ca6121c Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 24 Jan 2019 08:52:38 -0800 Subject: [PATCH 102/274] strict null pain --- src/vs/editor/contrib/suggest/suggestWidget.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/suggest/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts index b1cb1d7f32b..fc3f3d8f62a 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -157,7 +157,7 @@ class Renderer implements IListRenderer }; let color: string | null = null; - if (suggestion.kind === CompletionItemKind.Color && ((color = matchesColor(suggestion.label) || typeof suggestion.documentation === 'string' && matchesColor(suggestion.documentation)))) { + if (suggestion.kind === CompletionItemKind.Color && ((color = matchesColor(suggestion.label) || typeof suggestion.documentation === 'string' ? matchesColor(suggestion.documentation as any) : null))) { // special logic for 'color' completion items data.icon.className = 'icon customcolor'; data.colorspan.style.backgroundColor = color; From c4d6e79d4d56949d8eba3b51fe76c60f529a9445 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 24 Jan 2019 09:01:27 -0800 Subject: [PATCH 103/274] move open-function into stable, #66741 --- src/vs/vscode.d.ts | 12 ++++++++++++ src/vs/vscode.proposed.d.ts | 19 ------------------- src/vs/workbench/api/node/extHost.api.impl.ts | 4 ++-- 3 files changed, 14 insertions(+), 21 deletions(-) diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index fd07204c108..6bfa75c148c 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -5921,6 +5921,18 @@ declare module 'vscode' { * Changes each time the editor is started. */ export const sessionId: string; + + /** + * Opens an *external* item, e.g. a http(s) or mailto-link, using the + * default application. + * + * *Note* that [`showTextDocument`](#window.showTextDocument) is the right + * way to open a text document inside the editor, not this function. + * + * @param target The uri that should be opened. + * @returns A promise indicating if open was successful. + */ + export function open(target: Uri): Thenable; } /** diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 9b492047f9f..e2c6fb84fdc 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -16,25 +16,6 @@ declare module 'vscode' { - //#region Joh - vscode.open - - export namespace env { - - /** - * Opens an *external* item, e.g. a http(s) or mailto-link, using the - * default application. - * - * *Note* that [`showTextDocument`](#window.showTextDocument) is the right - * way to open a text document inside the editor, not this function. - * - * @param target The uri that should be opened. - * @returns A promise indicating if open was successful. - */ - export function open(target: Uri): Thenable; - } - - //#endregion - //#region Joh - selection range provider export class SelectionRangeKind { diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index 6f9e14ee77f..249c88fa891 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -256,9 +256,9 @@ export function createApiFactory( get clipboard(): vscode.Clipboard { return extHostClipboard; }, - open: proposedApiFunction(extension, (uri: URI) => { + open(uri: URI) { return extHostWindow.openUri(uri); - }) + } }); // namespace: extensions From dfeda31f76a23d5ebf2be6387da1b5a79db8efcc Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 24 Jan 2019 18:11:36 +0100 Subject: [PATCH 104/274] :lipstick: actions --- src/vs/workbench/browser/actions/listCommands.ts | 2 -- .../browser/parts/editor/editor.contribution.ts | 4 ++++ .../workbench/browser/parts/editor/resourceViewer.ts | 10 ++-------- .../workbench/browser/parts/editor/sideBySideEditor.ts | 7 ++++--- src/vs/workbench/browser/parts/views/panelViewlet.ts | 2 +- src/vs/workbench/common/contributions.ts | 2 -- .../electron-browser/actions/developerActions.ts | 2 -- .../workbench/electron-browser/actions/helpActions.ts | 1 - .../electron-browser/actions/windowActions.ts | 4 +--- .../electron-browser/extensionHostProcessManager.ts | 4 ++-- 10 files changed, 14 insertions(+), 24 deletions(-) diff --git a/src/vs/workbench/browser/actions/listCommands.ts b/src/vs/workbench/browser/actions/listCommands.ts index 6e22af2d2d9..196930c81b7 100644 --- a/src/vs/workbench/browser/actions/listCommands.ts +++ b/src/vs/workbench/browser/actions/listCommands.ts @@ -17,8 +17,6 @@ import { AsyncDataTree } from 'vs/base/browser/ui/tree/asyncDataTree'; import { DataTree } from 'vs/base/browser/ui/tree/dataTree'; import { ITreeNode } from 'vs/base/browser/ui/tree/tree'; -// --- List Commands - function ensureDOMFocus(widget: ListWidget): void { // it can happen that one of the commands is executed while // DOM focus is within another focusable control within the diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index 6d34336e1ab..b262c8aeef8 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -50,6 +50,7 @@ import { AllEditorsPicker, ActiveEditorGroupPicker } from 'vs/workbench/browser/ import { Schemas } from 'vs/base/common/network'; import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { OpenWorkspaceButtonContribution } from 'vs/workbench/browser/parts/editor/editorWidgets'; +import { ZoomStatusbarItem } from 'vs/workbench/browser/parts/editor/resourceViewer'; // Register String Editor Registry.as(EditorExtensions.Editors).registerEditor( @@ -221,6 +222,9 @@ registerEditorContribution(OpenWorkspaceButtonContribution); const statusBar = Registry.as(StatusExtensions.Statusbar); statusBar.registerStatusbarItem(new StatusbarItemDescriptor(EditorStatus, StatusbarAlignment.RIGHT, 100 /* towards the left of the right hand side */)); +// Register Zoom Status +statusBar.registerStatusbarItem(new StatusbarItemDescriptor(ZoomStatusbarItem, StatusbarAlignment.RIGHT, 101 /* to the left of editor status (100) */)); + // Register Status Actions const registry = Registry.as(ActionExtensions.WorkbenchActions); registry.registerWorkbenchAction(new SyncActionDescriptor(ChangeModeAction, ChangeModeAction.ID, ChangeModeAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_M) }), 'Change Language Mode'); diff --git a/src/vs/workbench/browser/parts/editor/resourceViewer.ts b/src/vs/workbench/browser/parts/editor/resourceViewer.ts index d819d0f555c..a0cc90b5735 100644 --- a/src/vs/workbench/browser/parts/editor/resourceViewer.ts +++ b/src/vs/workbench/browser/parts/editor/resourceViewer.ts @@ -13,12 +13,10 @@ import { LRUCache } from 'vs/base/common/map'; import { Schemas } from 'vs/base/common/network'; import { clamp } from 'vs/base/common/numbers'; import { Themable } from 'vs/workbench/common/theme'; -import { IStatusbarItem, StatusbarItemDescriptor, IStatusbarRegistry, Extensions } from 'vs/workbench/browser/parts/statusbar/statusbar'; -import { StatusbarAlignment } from 'vs/platform/statusbar/common/statusbar'; +import { IStatusbarItem } from 'vs/workbench/browser/parts/statusbar/statusbar'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IDisposable, Disposable, combinedDisposable } from 'vs/base/common/lifecycle'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { Registry } from 'vs/platform/registry/common/platform'; import { Action } from 'vs/base/common/actions'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { memoize } from 'vs/base/common/decorators'; @@ -234,7 +232,7 @@ class FileSeemsBinaryFileView { type Scale = number | 'fit'; -class ZoomStatusbarItem extends Themable implements IStatusbarItem { +export class ZoomStatusbarItem extends Themable implements IStatusbarItem { static instance: ZoomStatusbarItem; @@ -314,10 +312,6 @@ class ZoomStatusbarItem extends Themable implements IStatusbarItem { } } -Registry.as(Extensions.Statusbar).registerStatusbarItem( - new StatusbarItemDescriptor(ZoomStatusbarItem, StatusbarAlignment.RIGHT, 101 /* to the left of editor status (100) */) -); - interface ImageState { scale: Scale; offsetX: number; diff --git a/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts b/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts index 6a7316293d8..9db3583b18d 100644 --- a/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts +++ b/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts @@ -176,17 +176,18 @@ export class SideBySideEditor extends BaseEditor { } private setNewInput(newInput: SideBySideEditorInput, options: EditorOptions, token: CancellationToken): Promise { - const detailsEditor = this._createEditor(newInput.details, this.detailsEditorContainer); - const masterEditor = this._createEditor(newInput.master, this.masterEditorContainer); + const detailsEditor = this.doCreateEditor(newInput.details, this.detailsEditorContainer); + const masterEditor = this.doCreateEditor(newInput.master, this.masterEditorContainer); return this.onEditorsCreated(detailsEditor, masterEditor, newInput.details, newInput.master, options, token); } - private _createEditor(editorInput: EditorInput, container: HTMLElement): BaseEditor { + private doCreateEditor(editorInput: EditorInput, container: HTMLElement): BaseEditor { const descriptor = Registry.as(EditorExtensions.Editors).getEditor(editorInput); if (!descriptor) { throw new Error('No descriptor for editor found'); } + const editor = descriptor.instantiate(this.instantiationService); editor.create(container); editor.setVisible(this.isVisible(), this.group); diff --git a/src/vs/workbench/browser/parts/views/panelViewlet.ts b/src/vs/workbench/browser/parts/views/panelViewlet.ts index 5979e6d8b44..f69ee245d69 100644 --- a/src/vs/workbench/browser/parts/views/panelViewlet.ts +++ b/src/vs/workbench/browser/parts/views/panelViewlet.ts @@ -259,7 +259,7 @@ export class PanelViewlet extends Viewlet { let title = Registry.as(Extensions.Viewlets).getViewlet(this.getId()).name; if (this.isSingleView()) { - title += ': ' + this.panelItems[0].panel.title; + title = `${title}: ${this.panelItems[0].panel.title}`; } return title; diff --git a/src/vs/workbench/common/contributions.ts b/src/vs/workbench/common/contributions.ts index f881e90555e..01a8631bf8f 100644 --- a/src/vs/workbench/common/contributions.ts +++ b/src/vs/workbench/common/contributions.ts @@ -8,8 +8,6 @@ import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/ import { Registry } from 'vs/platform/registry/common/platform'; import { runWhenIdle, IdleDeadline } from 'vs/base/common/async'; -// --- Workbench Contribution Registry - /** * A workbench contribution that will be loaded when the workbench starts and disposed when the workbench shuts down. */ diff --git a/src/vs/workbench/electron-browser/actions/developerActions.ts b/src/vs/workbench/electron-browser/actions/developerActions.ts index 6f9e2f1ffb1..8b9c5485b63 100644 --- a/src/vs/workbench/electron-browser/actions/developerActions.ts +++ b/src/vs/workbench/electron-browser/actions/developerActions.ts @@ -16,8 +16,6 @@ import { Context } from 'vs/platform/contextkey/browser/contextKeyService'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { timeout } from 'vs/base/common/async'; -// --- actions - export class ToggleDevToolsAction extends Action { static readonly ID = 'workbench.action.toggleDevTools'; diff --git a/src/vs/workbench/electron-browser/actions/helpActions.ts b/src/vs/workbench/electron-browser/actions/helpActions.ts index f6c1190b82d..85d39d64d7b 100644 --- a/src/vs/workbench/electron-browser/actions/helpActions.ts +++ b/src/vs/workbench/electron-browser/actions/helpActions.ts @@ -242,4 +242,3 @@ export class OpenPrivacyStatementUrlAction extends Action { return Promise.resolve(); } } - diff --git a/src/vs/workbench/electron-browser/actions/windowActions.ts b/src/vs/workbench/electron-browser/actions/windowActions.ts index 50e78b9bfec..0cf3ec66110 100644 --- a/src/vs/workbench/electron-browser/actions/windowActions.ts +++ b/src/vs/workbench/electron-browser/actions/windowActions.ts @@ -26,8 +26,6 @@ import { IQuickInputService, IQuickPickItem, IQuickInputButton, IQuickPickSepara import { getIconClasses } from 'vs/editor/common/services/getIconClasses'; import product from 'vs/platform/node/product'; -// --- actions - export class CloseCurrentWindowAction extends Action { static readonly ID = 'workbench.action.closeWindow'; @@ -576,4 +574,4 @@ export class ToggleWindowTabsBar extends Action { run(): Promise { return this.windowsService.toggleWindowTabsBar().then(() => true); } -} \ No newline at end of file +} diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts index fe57ae9ca52..8329209bdd5 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts @@ -353,7 +353,7 @@ function getLatencyTestProviders(): ExtHostLatencyProvider[] { export class MeasureExtHostLatencyAction extends Action { public static readonly ID = 'editor.action.measureExtHostLatency'; - public static readonly LABEL = nls.localize('measureExtHostLatency', "Developer: Measure Extension Host Latency"); + public static readonly LABEL = nls.localize('measureExtHostLatency', "Measure Extension Host Latency"); constructor( id: string, @@ -384,4 +384,4 @@ export class MeasureExtHostLatencyAction extends Action { } const registry = Registry.as(ActionExtensions.WorkbenchActions); -registry.registerWorkbenchAction(new SyncActionDescriptor(MeasureExtHostLatencyAction, MeasureExtHostLatencyAction.ID, MeasureExtHostLatencyAction.LABEL), 'Developer: Measure Extension Host Latency'); +registry.registerWorkbenchAction(new SyncActionDescriptor(MeasureExtHostLatencyAction, MeasureExtHostLatencyAction.ID, MeasureExtHostLatencyAction.LABEL), 'Developer: Measure Extension Host Latency', nls.localize('developer', "Developer")); From b80da8bb5ad139f8c06022ceeca654ffc6f1d0ca Mon Sep 17 00:00:00 2001 From: Dipen Ved Date: Thu, 24 Jan 2019 23:07:57 +0530 Subject: [PATCH 105/274] Show the folder path a file is in, in title of the window (#66746) * Add active folder feature * use urilabel instead * use resource instead of path * show empty instead for activeFolderShort --- .../src/settingsDocumentHelper.ts | 7 +++++-- src/vs/base/common/paths.ts | 2 +- .../browser/parts/titlebar/titlebarPart.ts | 20 +++++++++++++++---- .../electron-browser/main.contribution.ts | 2 +- 4 files changed, 23 insertions(+), 8 deletions(-) diff --git a/extensions/configuration-editing/src/settingsDocumentHelper.ts b/extensions/configuration-editing/src/settingsDocumentHelper.ts index e6b137809ff..d4ac414897d 100644 --- a/extensions/configuration-editing/src/settingsDocumentHelper.ts +++ b/extensions/configuration-editing/src/settingsDocumentHelper.ts @@ -44,8 +44,11 @@ export class SettingsDocument { const completions: vscode.CompletionItem[] = []; completions.push(this.newSimpleCompletionItem('${activeEditorShort}', range, localize('activeEditorShort', "the file name (e.g. myFile.txt)"))); - completions.push(this.newSimpleCompletionItem('${activeEditorMedium}', range, localize('activeEditorMedium', "the path of the file relative to the workspace folder (e.g. myFolder/myFile.txt)"))); - completions.push(this.newSimpleCompletionItem('${activeEditorLong}', range, localize('activeEditorLong', "the full path of the file (e.g. /Users/Development/myProject/myFolder/myFile.txt)"))); + completions.push(this.newSimpleCompletionItem('${activeEditorMedium}', range, localize('activeEditorMedium', "the path of the file relative to the workspace folder (e.g. myFolder/myFileFolder/myFile.txt)"))); + completions.push(this.newSimpleCompletionItem('${activeEditorLong}', range, localize('activeEditorLong', "the full path of the file (e.g. /Users/Development/myFolder/myFileFolder/myFile.txt)"))); + completions.push(this.newSimpleCompletionItem('${activeFolderShort}', range, localize('activeFolderShort', "the name of the folder the file is contained in (e.g. myFileFolder)"))); + completions.push(this.newSimpleCompletionItem('${activeFolderMedium}', range, localize('activeFolderMedium', "the path of the folder the file is contained in, relative to the workspace folder (e.g. myFolder/myFileFolder)"))); + completions.push(this.newSimpleCompletionItem('${activeFolderLong}', range, localize('activeFolderLong', "the full path of the folder the file is contained in (e.g. /Users/Development/myFolder/myFileFolder)"))); completions.push(this.newSimpleCompletionItem('${rootName}', range, localize('rootName', "name of the workspace (e.g. myFolder or myWorkspace)"))); completions.push(this.newSimpleCompletionItem('${rootPath}', range, localize('rootPath', "file path of the workspace (e.g. /Users/Development/myWorkspace)"))); completions.push(this.newSimpleCompletionItem('${folderName}', range, localize('folderName', "name of the workspace folder the file is contained in (e.g. myFolder)"))); diff --git a/src/vs/base/common/paths.ts b/src/vs/base/common/paths.ts index 26f885e6b7b..33496d1d7d9 100644 --- a/src/vs/base/common/paths.ts +++ b/src/vs/base/common/paths.ts @@ -27,7 +27,7 @@ function isPathSeparator(code: number) { * @param separator the separator to use * @returns the directory name of a path. * '.' is returned for empty paths or single segment relative paths (as done by NodeJS) - * For paths consisting only of a root, the inout path is returned + * For paths consisting only of a root, the input path is returned */ export function dirname(path: string, separator = nativeSep): string { const len = path.length; diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index 8f7d118547c..93263deebea 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -5,6 +5,7 @@ import 'vs/css!./media/titlebarpart'; import * as paths from 'vs/base/common/paths'; +import * as resources from 'vs/base/common/resources'; import { Part } from 'vs/workbench/browser/part'; import { ITitleService, ITitleProperties } from 'vs/workbench/services/title/common/titleService'; import { getZoomFactor } from 'vs/base/browser/browser'; @@ -238,21 +239,26 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi /** * Possible template values: * - * {activeEditorLong}: e.g. /Users/Development/myProject/myFolder/myFile.txt - * {activeEditorMedium}: e.g. myFolder/myFile.txt + * {activeEditorLong}: e.g. /Users/Development/myFolder/myFileFolder/myFile.txt + * {activeEditorMedium}: e.g. myFolder/myFileFolder/myFile.txt * {activeEditorShort}: e.g. myFile.txt + * {activeFolderLong}: e.g. /Users/Development/myFolder/myFileFolder + * {activeFolderMedium}: e.g. myFolder/myFileFolder + * {activeFolderShort}: e.g. myFileFolder * {rootName}: e.g. myFolder1, myFolder2, myFolder3 - * {rootPath}: e.g. /Users/Development/myProject + * {rootPath}: e.g. /Users/Development * {folderName}: e.g. myFolder * {folderPath}: e.g. /Users/Development/myFolder * {appName}: e.g. VS Code - * {dirty}: indiactor + * {dirty}: indicator * {separator}: conditional separator */ private doGetWindowTitle(): string { const editor = this.editorService.activeEditor; const workspace = this.contextService.getWorkspace(); + let editorResource = editor ? toResource(editor, { supportSideBySide: true }) : undefined; + let root: URI; if (workspace.configuration) { root = workspace.configuration; @@ -269,6 +275,9 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi const activeEditorShort = editor ? editor.getTitle(Verbosity.SHORT) : ''; const activeEditorMedium = editor ? editor.getTitle(Verbosity.MEDIUM) : activeEditorShort; const activeEditorLong = editor ? editor.getTitle(Verbosity.LONG) : activeEditorMedium; + const activeFolderShort = editorResource ? resources.dirname(editorResource).path !== '.' ? resources.basename(resources.dirname(editorResource)) : '' : ''; + const activeFolderMedium = editorResource ? resources.dirname(editorResource).path !== '.' ? this.labelService.getUriLabel(resources.dirname(editorResource), { relative: true }) : '' : ''; + const activeFolderLong = editorResource ? resources.dirname(editorResource).path !== '.' ? this.labelService.getUriLabel(resources.dirname(editorResource)) : '' : ''; const rootName = this.labelService.getWorkspaceLabel(workspace); const rootPath = root ? this.labelService.getUriLabel(root) : ''; const folderName = folder ? folder.name : ''; @@ -282,6 +291,9 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi activeEditorShort, activeEditorLong, activeEditorMedium, + activeFolderShort, + activeFolderMedium, + activeFolderLong, rootName, rootPath, folderName, diff --git a/src/vs/workbench/electron-browser/main.contribution.ts b/src/vs/workbench/electron-browser/main.contribution.ts index 8ce0542d0fa..d88705f06e4 100644 --- a/src/vs/workbench/electron-browser/main.contribution.ts +++ b/src/vs/workbench/electron-browser/main.contribution.ts @@ -780,7 +780,7 @@ configurationRegistry.registerConfiguration({ 'type': 'string', 'default': isMacintosh ? '${activeEditorShort}${separator}${rootName}' : '${dirty}${activeEditorShort}${separator}${rootName}${separator}${appName}', 'markdownDescription': nls.localize({ comment: ['This is the description for a setting. Values surrounded by parenthesis are not to be translated.'], key: 'title' }, - "Controls the window title based on the active editor. Variables are substituted based on the context:\n- `\${activeEditorShort}`: the file name (e.g. myFile.txt).\n- `\${activeEditorMedium}`: the path of the file relative to the workspace folder (e.g. myFolder/myFile.txt).\n- `\${activeEditorLong}`: the full path of the file (e.g. /Users/Development/myProject/myFolder/myFile.txt).\n- `\${folderName}`: name of the workspace folder the file is contained in (e.g. myFolder).\n- `\${folderPath}`: file path of the workspace folder the file is contained in (e.g. /Users/Development/myFolder).\n- `\${rootName}`: name of the workspace (e.g. myFolder or myWorkspace).\n- `\${rootPath}`: file path of the workspace (e.g. /Users/Development/myWorkspace).\n- `\${appName}`: e.g. VS Code.\n- `\${dirty}`: a dirty indicator if the active editor is dirty.\n- `\${separator}`: a conditional separator (\" - \") that only shows when surrounded by variables with values or static text.") + "Controls the window title based on the active editor. Variables are substituted based on the context:\n- `\${activeEditorShort}`: the file name (e.g. myFile.txt).\n- `\${activeEditorMedium}`: the path of the file relative to the workspace folder (e.g. myFolder/myFileFolder/myFile.txt).\n- `\${activeEditorLong}`: the full path of the file (e.g. /Users/Development/myFolder/myFileFolder/myFile.txt).\n- `\${activeFolderShort}`: the name of the folder the file is contained in (e.g. myFileFolder).\n- `\${activeFolderMedium}`: the path of the folder the file is contained in, relative to the workspace folder (e.g. myFolder/myFileFolder).\n- `\${activeFolderLong}`: the full path of the folder the file is contained in (e.g. /Users/Development/myFolder/myFileFolder).\n- `\${folderName}`: name of the workspace folder the file is contained in (e.g. myFolder).\n- `\${folderPath}`: file path of the workspace folder the file is contained in (e.g. /Users/Development/myFolder).\n- `\${rootName}`: name of the workspace (e.g. myFolder or myWorkspace).\n- `\${rootPath}`: file path of the workspace (e.g. /Users/Development/myWorkspace).\n- `\${appName}`: e.g. VS Code.\n- `\${dirty}`: a dirty indicator if the active editor is dirty.\n- `\${separator}`: a conditional separator (\" - \") that only shows when surrounded by variables with values or static text.") }, 'window.newWindowDimensions': { 'type': 'string', From 4fb0c268bea197274576ffcae45d19ed0f53b0dd Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 24 Jan 2019 18:44:40 +0100 Subject: [PATCH 106/274] :lipstick: --- .../browser/parts/titlebar/titlebarPart.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index 93263deebea..29714f58a92 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -257,8 +257,7 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi const editor = this.editorService.activeEditor; const workspace = this.contextService.getWorkspace(); - let editorResource = editor ? toResource(editor, { supportSideBySide: true }) : undefined; - + // Compute root let root: URI; if (workspace.configuration) { root = workspace.configuration; @@ -266,18 +265,25 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi root = workspace.folders[0].uri; } + // Compute active editor folder + const editorResource = editor ? toResource(editor) : undefined; + let editorFolderResource = editorResource ? resources.dirname(editorResource) : undefined; + if (editorFolderResource && editorFolderResource.path === '.') { + editorFolderResource = undefined; + } + // Compute folder resource // Single Root Workspace: always the root single workspace in this case // Otherwise: root folder of the currently active file if any - let folder = this.contextService.getWorkbenchState() === WorkbenchState.FOLDER ? workspace.folders[0] : this.contextService.getWorkspaceFolder(toResource(editor, { supportSideBySide: true })); + const folder = this.contextService.getWorkbenchState() === WorkbenchState.FOLDER ? workspace.folders[0] : this.contextService.getWorkspaceFolder(toResource(editor, { supportSideBySide: true })); // Variables const activeEditorShort = editor ? editor.getTitle(Verbosity.SHORT) : ''; const activeEditorMedium = editor ? editor.getTitle(Verbosity.MEDIUM) : activeEditorShort; const activeEditorLong = editor ? editor.getTitle(Verbosity.LONG) : activeEditorMedium; - const activeFolderShort = editorResource ? resources.dirname(editorResource).path !== '.' ? resources.basename(resources.dirname(editorResource)) : '' : ''; - const activeFolderMedium = editorResource ? resources.dirname(editorResource).path !== '.' ? this.labelService.getUriLabel(resources.dirname(editorResource), { relative: true }) : '' : ''; - const activeFolderLong = editorResource ? resources.dirname(editorResource).path !== '.' ? this.labelService.getUriLabel(resources.dirname(editorResource)) : '' : ''; + const activeFolderShort = editorFolderResource ? resources.basename(editorFolderResource) : ''; + const activeFolderMedium = editorFolderResource ? this.labelService.getUriLabel(editorFolderResource, { relative: true }) : ''; + const activeFolderLong = editorFolderResource ? this.labelService.getUriLabel(editorFolderResource) : ''; const rootName = this.labelService.getWorkspaceLabel(workspace); const rootPath = root ? this.labelService.getUriLabel(root) : ''; const folderName = folder ? folder.name : ''; From a090f78356509d297b2b9586fef48ba766e88f44 Mon Sep 17 00:00:00 2001 From: kieferrm Date: Thu, 24 Jan 2019 18:01:09 +0000 Subject: [PATCH 107/274] fixing GDPR annotations --- src/vs/editor/contrib/format/formatActions.ts | 8 ++++---- src/vs/workbench/parts/stats/node/workspaceStats.ts | 12 ++++++------ .../workbench/services/search/node/searchService.ts | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/vs/editor/contrib/format/formatActions.ts b/src/vs/editor/contrib/format/formatActions.ts index 6cbb2a2aeb9..429ea13fa61 100644 --- a/src/vs/editor/contrib/format/formatActions.ts +++ b/src/vs/editor/contrib/format/formatActions.ts @@ -74,8 +74,8 @@ export function formatDocumentRange(telemetryService: ITelemetryService, workerS if (provider.length !== 1) { /* __GDPR__ "manyformatters" : { - "type" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - "language" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + "type" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "language" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, "count" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } } */ @@ -143,8 +143,8 @@ export function formatDocument(telemetryService: ITelemetryService, workerServic if (provider.length !== 1) { /* __GDPR__ "manyformatters" : { - "type" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - "language" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + "type" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "language" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, "count" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } } */ diff --git a/src/vs/workbench/parts/stats/node/workspaceStats.ts b/src/vs/workbench/parts/stats/node/workspaceStats.ts index d42cc242bf4..bed2040b456 100644 --- a/src/vs/workbench/parts/stats/node/workspaceStats.ts +++ b/src/vs/workbench/parts/stats/node/workspaceStats.ts @@ -311,12 +311,12 @@ export class WorkspaceStats implements IWorkbenchContribution { "workspace.py.azure-storage" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.py.azure-translator" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.py.azure-iothub-device-client" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workspace.py.azure-ml" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } - "workspace.py.azure-cognitiveservices" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } - "workspace.py.adal" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } - "workspace.py.pydocumentdb" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } - "workspace.py.botbuilder-core" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } - "workspace.py.botbuilder-schema" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } + "workspace.py.azure-ml" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.py.azure-cognitiveservices" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.py.adal" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.py.pydocumentdb" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.py.botbuilder-core" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.py.botbuilder-schema" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.py.botframework-connector" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } } diff --git a/src/vs/workbench/services/search/node/searchService.ts b/src/vs/workbench/services/search/node/searchService.ts index 8d8fc5f2ef2..8d45afec201 100644 --- a/src/vs/workbench/services/search/node/searchService.ts +++ b/src/vs/workbench/services/search/node/searchService.ts @@ -320,7 +320,7 @@ export class SearchService extends Disposable implements ISearchService { "filesWalked" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "cmdTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "cmdResultCount" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "scheme" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "scheme" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } } */ this.telemetryService.publicLog('searchComplete', { From ac1812d748463ca1cb1e6777e7029134f4b56f2f Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Thu, 24 Jan 2019 10:23:27 -0800 Subject: [PATCH 108/274] Fix #67071 - don't leak badge styler from search results --- .../parts/search/browser/searchResultsView.ts | 37 ++++++++++++++----- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/parts/search/browser/searchResultsView.ts b/src/vs/workbench/parts/search/browser/searchResultsView.ts index 20bec999903..18a8d159b78 100644 --- a/src/vs/workbench/parts/search/browser/searchResultsView.ts +++ b/src/vs/workbench/parts/search/browser/searchResultsView.ts @@ -10,7 +10,7 @@ import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; import { ITreeNode, ITreeRenderer } from 'vs/base/browser/ui/tree/tree'; import { IAction } from 'vs/base/common/actions'; -import { Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; import * as paths from 'vs/base/common/paths'; import * as resources from 'vs/base/common/resources'; import * as nls from 'vs/nls'; @@ -31,6 +31,7 @@ interface IFolderMatchTemplate { label: IResourceLabel; badge: CountBadge; actions: ActionBar; + disposables: IDisposable[]; } interface IFileMatchTemplate { @@ -38,6 +39,7 @@ interface IFileMatchTemplate { label: IResourceLabel; badge: CountBadge; actions: ActionBar; + disposables: IDisposable[]; } interface IMatchTemplate { @@ -87,14 +89,23 @@ export class FolderMatchRenderer extends Disposable implements ITreeRenderer, index: number, templateData: IFolderMatchTemplate): void { @@ -128,8 +139,7 @@ export class FolderMatchRenderer extends Disposable implements ITreeRenderer, index: number, templateData: IFileMatchTemplate): void { @@ -181,8 +201,7 @@ export class FileMatchRenderer extends Disposable implements ITreeRenderer Date: Thu, 24 Jan 2019 09:21:41 -0800 Subject: [PATCH 109/274] perf - add marks for loading the main-process bundle --- src/main.js | 5 ++++- .../parts/performance/electron-browser/perfviewEditor.ts | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main.js b/src/main.js index 0b6c8e18249..b110c753ce5 100644 --- a/src/main.js +++ b/src/main.js @@ -106,7 +106,10 @@ function onReady() { process.env['VSCODE_NODE_CACHED_DATA_DIR'] = cachedDataDir || ''; // Load main in AMD - require('./bootstrap-amd').load('vs/code/electron-main/main'); + perf.mark('willLoadMainBundle'); + require('./bootstrap-amd').load('vs/code/electron-main/main', () => { + perf.mark('didLoadMainBundle'); + }); }; // We recevied a valid nlsConfig from a user defined locale diff --git a/src/vs/workbench/parts/performance/electron-browser/perfviewEditor.ts b/src/vs/workbench/parts/performance/electron-browser/perfviewEditor.ts index bfef17ef8ce..052ad299f86 100644 --- a/src/vs/workbench/parts/performance/electron-browser/perfviewEditor.ts +++ b/src/vs/workbench/parts/performance/electron-browser/perfviewEditor.ts @@ -141,6 +141,7 @@ class PerfModelContentProvider implements ITextModelContentProvider { const table: (string | number)[][] = []; table.push(['start => app.isReady', metrics.timers.ellapsedAppReady, '[main]', `initial startup: ${metrics.initialStartup}`]); table.push(['nls:start => nls:end', metrics.timers.ellapsedNlsGeneration, '[main]', `initial startup: ${metrics.initialStartup}`]); + table.push(['require(main.bundle.js)', metrics.initialStartup ? perf.getDuration('willLoadMainBundle', 'didLoadMainBundle') : undefined, '[main]', `initial startup: ${metrics.initialStartup}`]); table.push(['app.isReady => window.loadUrl()', metrics.timers.ellapsedWindowLoad, '[main]', `initial startup: ${metrics.initialStartup}`]); table.push(['require & init global storage', metrics.timers.ellapsedGlobalStorageInitMain, '[main]', `initial startup: ${metrics.initialStartup}`]); table.push(['window.loadUrl() => begin to require(workbench.main.js)', metrics.timers.ellapsedWindowLoadToRequire, '[main->renderer]', StartupKindToString(metrics.windowKind)]); From 6c7d1cc2bd8425085d602cd0071824abeb20a445 Mon Sep 17 00:00:00 2001 From: Dipen Ved Date: Fri, 25 Jan 2019 01:01:19 +0530 Subject: [PATCH 110/274] Making longDescription as absolute --- src/vs/workbench/parts/files/common/editors/fileEditorInput.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/files/common/editors/fileEditorInput.ts b/src/vs/workbench/parts/files/common/editors/fileEditorInput.ts index 660e206385d..78c08b20065 100644 --- a/src/vs/workbench/parts/files/common/editors/fileEditorInput.ts +++ b/src/vs/workbench/parts/files/common/editors/fileEditorInput.ts @@ -140,7 +140,7 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput { @memoize private get longDescription(): string { - return this.labelService.getUriLabel(resources.dirname(this.resource), { relative: true }); + return this.labelService.getUriLabel(resources.dirname(this.resource)); } getDescription(verbosity: Verbosity = Verbosity.MEDIUM): string { From ba0f76955182717087f63f44bea2c37101d451c9 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 23 Jan 2019 15:13:41 -0800 Subject: [PATCH 111/274] always -> finally #67027 --- src/vs/editor/contrib/codeAction/codeActionWidget.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/vs/editor/contrib/codeAction/codeActionWidget.ts b/src/vs/editor/contrib/codeAction/codeActionWidget.ts index 7b4b5fb5acb..34bf98be6b7 100644 --- a/src/vs/editor/contrib/codeAction/codeActionWidget.ts +++ b/src/vs/editor/contrib/codeAction/codeActionWidget.ts @@ -5,7 +5,6 @@ import { getDomNodePagePosition } from 'vs/base/browser/dom'; import { Action } from 'vs/base/common/actions'; -import { always } from 'vs/base/common/async'; import { canceled } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; @@ -53,11 +52,9 @@ export class CodeActionContextMenu { private codeActionToAction(action: CodeAction): Action { const id = action.command ? action.command.id : action.title; const title = action.isPreferred ? `${action.title} ★` : action.title; - return new Action(id, title, undefined, true, () => { - return always( - this._onApplyCodeAction(action), - () => this._onDidExecuteCodeAction.fire(undefined)); - }); + return new Action(id, title, undefined, true, () => + this._onApplyCodeAction(action) + .finally(() => this._onDidExecuteCodeAction.fire(undefined))); } get isVisible(): boolean { From e6f93f30747c081dbf8640a62ef92d52545e52f8 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 24 Jan 2019 10:56:34 -0800 Subject: [PATCH 112/274] Extract isPreferredFix --- .../src/features/quickFix.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/extensions/typescript-language-features/src/features/quickFix.ts b/extensions/typescript-language-features/src/features/quickFix.ts index ec98820c7f5..960e076e590 100644 --- a/extensions/typescript-language-features/src/features/quickFix.ts +++ b/extensions/typescript-language-features/src/features/quickFix.ts @@ -269,9 +269,7 @@ class TypeScriptQuickFixProvider implements vscode.CodeActionProvider { arguments: [tsAction], title: '' }; - if (tsAction.fixName === 'spelling') { - codeAction.isPreferred = true; - } + codeAction.isPreferred = isPreferredFix(tsAction); return codeAction; } @@ -305,6 +303,11 @@ class TypeScriptQuickFixProvider implements vscode.CodeActionProvider { } } +const preferredFixes = new Set(['spelling']); +function isPreferredFix(tsAction: Proto.CodeFixAction): boolean { + return preferredFixes.has(tsAction.fixName); +} + export function register( selector: vscode.DocumentSelector, client: ITypeScriptServiceClient, From 4a39da971e0810682f8e75ecea21b1cf3c08a623 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 24 Jan 2019 11:56:09 -0800 Subject: [PATCH 113/274] Extend disposable in more places --- .../src/features/fileConfigurationManager.ts | 16 +++++----------- .../src/features/task.ts | 10 ++++++---- .../src/utils/managedFileContext.ts | 12 ++++-------- .../src/utils/typingsStatus.ts | 6 ++++-- .../src/utils/versionStatus.ts | 14 +++++--------- 5 files changed, 24 insertions(+), 34 deletions(-) diff --git a/extensions/typescript-language-features/src/features/fileConfigurationManager.ts b/extensions/typescript-language-features/src/features/fileConfigurationManager.ts index 794396595b2..0091aa1dac5 100644 --- a/extensions/typescript-language-features/src/features/fileConfigurationManager.ts +++ b/extensions/typescript-language-features/src/features/fileConfigurationManager.ts @@ -9,6 +9,7 @@ import { ITypeScriptServiceClient } from '../typescriptService'; import API from '../utils/api'; import { isTypeScriptDocument } from '../utils/languageModeIds'; import { ResourceMap } from '../utils/resourceMap'; +import { Disposable } from '../utils/dispose'; function objsAreEqual(a: T, b: T): boolean { @@ -33,27 +34,20 @@ function areFileConfigurationsEqual(a: FileConfiguration, b: FileConfiguration): ); } -export default class FileConfigurationManager { - private onDidCloseTextDocumentSub: vscode.Disposable | undefined; +export default class FileConfigurationManager extends Disposable { private readonly formatOptions = new ResourceMap>(); public constructor( private readonly client: ITypeScriptServiceClient ) { - this.onDidCloseTextDocumentSub = vscode.workspace.onDidCloseTextDocument(textDocument => { + super(); + vscode.workspace.onDidCloseTextDocument(textDocument => { // When a document gets closed delete the cached formatting options. // This is necessary since the tsserver now closed a project when its // last file in it closes which drops the stored formatting options // as well. this.formatOptions.delete(textDocument.uri); - }); - } - - public dispose() { - if (this.onDidCloseTextDocumentSub) { - this.onDidCloseTextDocumentSub.dispose(); - this.onDidCloseTextDocumentSub = undefined; - } + }, undefined, this._disposables); } public async ensureConfigurationForDocument( diff --git a/extensions/typescript-language-features/src/features/task.ts b/extensions/typescript-language-features/src/features/task.ts index c71f3c4435d..4d58c36a70a 100644 --- a/extensions/typescript-language-features/src/features/task.ts +++ b/extensions/typescript-language-features/src/features/task.ts @@ -11,6 +11,7 @@ import { ITypeScriptServiceClient } from '../typescriptService'; import { Lazy } from '../utils/lazy'; import { isImplicitProjectConfigFile } from '../utils/tsconfig'; import TsConfigProvider, { TSConfig } from '../utils/tsconfigProvider'; +import { Disposable } from '../utils/dispose'; const localize = nls.loadMessageBundle(); @@ -244,23 +245,24 @@ class TscTaskProvider implements vscode.TaskProvider { /** * Manages registrations of TypeScript task providers with VS Code. */ -export default class TypeScriptTaskProviderManager { +export default class TypeScriptTaskProviderManager extends Disposable { private taskProviderSub: vscode.Disposable | undefined = undefined; - private readonly disposables: vscode.Disposable[] = []; constructor( private readonly client: Lazy ) { - vscode.workspace.onDidChangeConfiguration(this.onConfigurationChanged, this, this.disposables); + super(); + vscode.workspace.onDidChangeConfiguration(this.onConfigurationChanged, this, this._disposables); this.onConfigurationChanged(); } dispose() { + super.dispose(); + if (this.taskProviderSub) { this.taskProviderSub.dispose(); this.taskProviderSub = undefined; } - this.disposables.forEach(x => x.dispose()); } private onConfigurationChanged() { diff --git a/extensions/typescript-language-features/src/utils/managedFileContext.ts b/extensions/typescript-language-features/src/utils/managedFileContext.ts index 36cca539ec3..81acf9ad505 100644 --- a/extensions/typescript-language-features/src/utils/managedFileContext.ts +++ b/extensions/typescript-language-features/src/utils/managedFileContext.ts @@ -5,29 +5,25 @@ import * as vscode from 'vscode'; import { isSupportedLanguageMode } from './languageModeIds'; +import { Disposable } from './dispose'; /** * When clause context set when the current file is managed by vscode's built-in typescript extension. */ -export default class ManagedFileContextManager { +export default class ManagedFileContextManager extends Disposable { private static readonly contextName = 'typescript.isManagedFile'; private isInManagedFileContext: boolean = false; - private readonly onDidChangeActiveTextEditorSub: vscode.Disposable; - public constructor( private readonly normalizePath: (resource: vscode.Uri) => string | undefined ) { - this.onDidChangeActiveTextEditorSub = vscode.window.onDidChangeActiveTextEditor(this.onDidChangeActiveTextEditor, this); + super(); + vscode.window.onDidChangeActiveTextEditor(this.onDidChangeActiveTextEditor, this, this._disposables); this.onDidChangeActiveTextEditor(vscode.window.activeTextEditor); } - public dispose() { - this.onDidChangeActiveTextEditorSub.dispose(); - } - private onDidChangeActiveTextEditor(editor?: vscode.TextEditor): any { if (editor) { const isManagedFile = isSupportedLanguageMode(editor.document) && this.normalizePath(editor.document.uri) !== null; diff --git a/extensions/typescript-language-features/src/utils/typingsStatus.ts b/extensions/typescript-language-features/src/utils/typingsStatus.ts index 78f303f0f39..066fda960df 100644 --- a/extensions/typescript-language-features/src/utils/typingsStatus.ts +++ b/extensions/typescript-language-features/src/utils/typingsStatus.ts @@ -6,18 +6,19 @@ import * as vscode from 'vscode'; import { loadMessageBundle } from 'vscode-nls'; import { ITypeScriptServiceClient } from '../typescriptService'; +import { Disposable } from './dispose'; const localize = loadMessageBundle(); const typingsInstallTimeout = 30 * 1000; -export default class TypingsStatus extends vscode.Disposable { +export default class TypingsStatus extends Disposable { private _acquiringTypings: { [eventId: string]: NodeJS.Timer } = Object.create({}); private _client: ITypeScriptServiceClient; private _subscriptions: vscode.Disposable[] = []; constructor(client: ITypeScriptServiceClient) { - super(() => this.dispose()); + super(); this._client = client; this._subscriptions.push( @@ -28,6 +29,7 @@ export default class TypingsStatus extends vscode.Disposable { } public dispose(): void { + super.dispose(); this._subscriptions.forEach(x => x.dispose()); for (const eventId of Object.keys(this._acquiringTypings)) { diff --git a/extensions/typescript-language-features/src/utils/versionStatus.ts b/extensions/typescript-language-features/src/utils/versionStatus.ts index 6d7940c9a8a..0b3e3412617 100644 --- a/extensions/typescript-language-features/src/utils/versionStatus.ts +++ b/extensions/typescript-language-features/src/utils/versionStatus.ts @@ -6,21 +6,17 @@ import * as vscode from 'vscode'; import * as languageModeIds from './languageModeIds'; import { TypeScriptVersion } from './versionProvider'; +import { Disposable } from './dispose'; -export default class VersionStatus { - private readonly _onChangeEditorSub: vscode.Disposable; +export default class VersionStatus extends Disposable { private readonly _versionBarEntry: vscode.StatusBarItem; constructor( private readonly _normalizePath: (resource: vscode.Uri) => string | undefined ) { - this._versionBarEntry = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 99 /* to the right of editor status (100) */); - this._onChangeEditorSub = vscode.window.onDidChangeActiveTextEditor(this.showHideStatus, this); - } - - public dispose() { - this._versionBarEntry.dispose(); - this._onChangeEditorSub.dispose(); + super(); + this._versionBarEntry = this._register(vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 99 /* to the right of editor status (100) */)); + vscode.window.onDidChangeActiveTextEditor(this.showHideStatus, this, this._disposables); } public onDidChangeTypeScriptVersion(version: TypeScriptVersion) { From 09279755650dbc0463757e250451ba898a732487 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 24 Jan 2019 11:59:38 -0800 Subject: [PATCH 114/274] Mark more quick fixes as preferred #62110 https://github.com/Microsoft/TypeScript/issues/29450 --- .../typescript-language-features/src/features/quickFix.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/extensions/typescript-language-features/src/features/quickFix.ts b/extensions/typescript-language-features/src/features/quickFix.ts index 960e076e590..dbce53a14cc 100644 --- a/extensions/typescript-language-features/src/features/quickFix.ts +++ b/extensions/typescript-language-features/src/features/quickFix.ts @@ -303,7 +303,13 @@ class TypeScriptQuickFixProvider implements vscode.CodeActionProvider { } } -const preferredFixes = new Set(['spelling']); +const preferredFixes = new Set([ + 'constructorForDerivedNeedSuperCall', + 'fixClassIncorrectlyImplementsInterface', + 'fixUnreachableCode', + 'spelling', + 'unusedIdentifier', +]); function isPreferredFix(tsAction: Proto.CodeFixAction): boolean { return preferredFixes.has(tsAction.fixName); } From 4cce01ac334c9d6a5f8a8447e23b3af9e0460cdc Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 24 Jan 2019 12:44:01 -0800 Subject: [PATCH 115/274] Delay terminal service creation Fixes #65415 --- .../parts/terminal/electron-browser/terminal.contribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts b/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts index e016e1e90f5..47070e0350d 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts @@ -290,7 +290,7 @@ configurationRegistry.registerConfiguration({ } }); -registerSingleton(ITerminalService, TerminalService); +registerSingleton(ITerminalService, TerminalService, true); (Registry.as(panel.Extensions.Panels)).registerPanel(new panel.PanelDescriptor( TerminalPanel, From 8aa929a867af6dc338954932038b153c45a921aa Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 24 Jan 2019 12:59:43 -0800 Subject: [PATCH 116/274] Add comment indicating this can be removed later --- .../parts/terminal/electron-browser/terminalInstance.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts index b59ae63ed23..2a9886f4ff7 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts @@ -575,6 +575,7 @@ export class TerminalInstance implements ITerminalInstance { if (this._processManager) { this._widgetManager = new TerminalWidgetManager(this._wrapperElement); + // HACK: This can be removed once this is fixed upstream xtermjs/xterm.js#1908 this._disposables.push(dom.addDisposableListener(this._xterm.element, 'mouseleave', () => { this._widgetManager.closeMessage(); })); From 566910c68fa4005a3b5320e0f81838c2b32f7843 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 24 Jan 2019 23:30:48 +0100 Subject: [PATCH 117/274] Install UI dependencies on local server --- .../node/multiExtensionManagement.ts | 129 ++++++++++++++---- 1 file changed, 104 insertions(+), 25 deletions(-) diff --git a/src/vs/platform/extensionManagement/node/multiExtensionManagement.ts b/src/vs/platform/extensionManagement/node/multiExtensionManagement.ts index 3cd5f0acc66..99b99ad7684 100644 --- a/src/vs/platform/extensionManagement/node/multiExtensionManagement.ts +++ b/src/vs/platform/extensionManagement/node/multiExtensionManagement.ts @@ -9,7 +9,7 @@ import { IExtensionManagementServerService, IExtensionManagementServer, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { flatten } from 'vs/base/common/arrays'; -import { isUIExtension, ExtensionType } from 'vs/platform/extensions/common/extensions'; +import { isUIExtension, ExtensionType, IExtensionManifest } from 'vs/platform/extensions/common/extensions'; import { URI } from 'vs/base/common/uri'; import { Disposable } from 'vs/base/common/lifecycle'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -17,6 +17,8 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { IRemoteAuthorityResolverService, ResolvedAuthority } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { getManifest } from 'vs/platform/extensionManagement/node/extensionManagementUtil'; import { ILogService } from 'vs/platform/log/common/log'; +import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { localize } from 'vs/nls'; export class MultiExtensionManagementService extends Disposable implements IExtensionManagementService { @@ -50,12 +52,57 @@ export class MultiExtensionManagementService extends Disposable implements IExte .then(result => flatten(result)); } - uninstall(extension: ILocalExtension, force?: boolean): Promise { - const server = this.getServer(extension); - if (server) { - return server.extensionManagementService.uninstall(extension, force); + async uninstall(extension: ILocalExtension, force?: boolean): Promise { + if (this.extensionManagementServerService.remoteExtensionManagementServer) { + const server = this.getServer(extension); + if (!server) { + return Promise.reject(`Invalid location ${extension.location.toString()}`); + } + const syncExtensions = await this.hasToSyncExtensions(); + return syncExtensions ? this.uninstallEverywhere(extension, force) : this.uninstallInServer(extension, server, force); } - return Promise.reject(`Invalid location ${extension.location.toString()}`); + return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.uninstall(extension, force); + } + + private async uninstallEverywhere(extension: ILocalExtension, force?: boolean): Promise { + const server = this.getServer(extension); + if (!server) { + return Promise.reject(`Invalid location ${extension.location.toString()}`); + } + const promise = server.extensionManagementService.uninstall(extension); + const anotherServer = server === this.extensionManagementServerService.localExtensionManagementServer ? this.extensionManagementServerService.remoteExtensionManagementServer : this.extensionManagementServerService.localExtensionManagementServer; + const installed = await anotherServer.extensionManagementService.getInstalled(ExtensionType.User); + extension = installed.filter(i => areSameExtensions(i.identifier, extension.identifier))[0]; + if (extension) { + await anotherServer.extensionManagementService.uninstall(extension); + } + return promise; + } + + private async uninstallInServer(extension: ILocalExtension, server: IExtensionManagementServer, force?: boolean): Promise { + if (server === this.extensionManagementServerService.localExtensionManagementServer) { + const installedExtensions = await this.extensionManagementServerService.remoteExtensionManagementServer.extensionManagementService.getInstalled(ExtensionType.User); + const dependentNonUIExtensions = installedExtensions.filter(i => !isUIExtension(i.manifest, this.configurationService) + && i.manifest.extensionDependencies && i.manifest.extensionDependencies.some(id => areSameExtensions({ id }, extension.identifier))); + if (dependentNonUIExtensions.length) { + return Promise.reject(new Error(this.getDependentsErrorMessage(extension, dependentNonUIExtensions))); + } + } + return server.extensionManagementService.uninstall(extension, force); + } + + private getDependentsErrorMessage(extension: ILocalExtension, dependents: ILocalExtension[]): string { + if (dependents.length === 1) { + return localize('singleDependentError', "Cannot uninstall extension '{0}'. Extension '{1}' depends on this.", + extension.manifest.displayName || extension.manifest.name, dependents[0].manifest.displayName || dependents[0].manifest.name); + } + if (dependents.length === 2) { + return localize('twoDependentsError', "Cannot uninstall extension '{0}'. Extensions '{1}' and '{2}' depend on this.", + extension.manifest.displayName || extension.manifest.name, dependents[0].manifest.displayName || dependents[0].manifest.name, dependents[1].manifest.displayName || dependents[1].manifest.name); + } + return localize('multipleDependentsError', "Cannot uninstall extension '{0}'. Extensions '{1}', '{2}' and others depend on this.", + extension.manifest.displayName || extension.manifest.name, dependents[0].manifest.displayName || dependents[0].manifest.name, dependents[1].manifest.displayName || dependents[1].manifest.name); + } reinstallFromGallery(extension: ILocalExtension): Promise { @@ -82,35 +129,67 @@ export class MultiExtensionManagementService extends Disposable implements IExte return Promise.all(this.servers.map(({ extensionManagementService }) => extensionManagementService.unzip(zipLocation, type))).then(([extensionIdentifier]) => extensionIdentifier); } - install(vsix: URI): Promise { + async install(vsix: URI): Promise { if (this.extensionManagementServerService.remoteExtensionManagementServer) { - return Promise.all([getManifest(vsix.fsPath), this.hasToSyncExtensions()]) - .then(([manifest, syncExtensions]) => { - const servers = isUIExtension(manifest, this.configurationService) ? [this.extensionManagementServerService.localExtensionManagementServer] : syncExtensions ? this.servers : [this.extensionManagementServerService.remoteExtensionManagementServer!]; - return Promise.all(servers.map(server => server.extensionManagementService.install(vsix))) - .then(([extensionIdentifier]) => extensionIdentifier); - }); + const syncExtensions = await this.hasToSyncExtensions(); + if (syncExtensions) { + // Install on both servers + const [extensionIdentifier] = await Promise.all(this.servers.map(server => server.extensionManagementService.install(vsix))); + return extensionIdentifier; + } + const manifest = await getManifest(vsix.fsPath); + if (isUIExtension(manifest, this.configurationService)) { + // Install only on local server + return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.install(vsix); + } + // Install only on remote server + const promise = this.extensionManagementServerService.remoteExtensionManagementServer.extensionManagementService.install(vsix); + // Install UI Dependencies on local server + await this.installUIDependencies(manifest); + return promise; } return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.install(vsix); } - installFromGallery(gallery: IGalleryExtension): Promise { + async installFromGallery(gallery: IGalleryExtension): Promise { if (this.extensionManagementServerService.remoteExtensionManagementServer) { - return Promise.all([this.extensionGalleryService.getManifest(gallery, CancellationToken.None), this.hasToSyncExtensions()]) - .then(([manifest, syncExtensions]) => { - if (manifest) { - const servers = manifest && isUIExtension(manifest, this.configurationService) ? [this.extensionManagementServerService.localExtensionManagementServer] : syncExtensions ? this.servers : [this.extensionManagementServerService.remoteExtensionManagementServer!]; - return Promise.all(servers.map(server => server.extensionManagementService.installFromGallery(gallery))) - .then(() => undefined); - } else { - this.logService.info('Manifest was not found. Hence installing only in local server'); - return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.installFromGallery(gallery); - } - }); + const [manifest, syncExtensions] = await Promise.all([this.extensionGalleryService.getManifest(gallery, CancellationToken.None), this.hasToSyncExtensions()]); + if (manifest) { + if (syncExtensions) { + // Install on both servers + return Promise.all(this.servers.map(server => server.extensionManagementService.installFromGallery(gallery))).then(() => null); + } + if (isUIExtension(manifest, this.configurationService)) { + // Install only on local server + return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.installFromGallery(gallery); + } + // Install only on remote server + const promise = this.extensionManagementServerService.remoteExtensionManagementServer.extensionManagementService.installFromGallery(gallery); + // Install UI Dependencies on local server + await this.installUIDependencies(manifest); + return promise; + } else { + this.logService.info('Manifest was not found. Hence installing only in local server'); + return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.installFromGallery(gallery); + } } return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.installFromGallery(gallery); } + private async installUIDependencies(manifest: IExtensionManifest): Promise { + if (manifest.extensionDependencies && manifest.extensionDependencies.length) { + const dependencies = await this.extensionGalleryService.loadAllDependencies(manifest.extensionDependencies.map(id => ({ id })), CancellationToken.None); + if (dependencies.length) { + await Promise.all(dependencies.map(async d => { + const manifest = await this.extensionGalleryService.getManifest(d, CancellationToken.None); + if (manifest && isUIExtension(manifest, this.configurationService)) { + await this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.installFromGallery(d); + } + })); + } + } + } + getExtensionsReport(): Promise { return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.getExtensionsReport(); } From 5fdc5c2675c11f31cb9bef972ea3de4b5bfaad86 Mon Sep 17 00:00:00 2001 From: Phil Marshall Date: Thu, 24 Jan 2019 17:25:09 -0600 Subject: [PATCH 118/274] factor documentlink extraction into separate function --- .../src/features/documentLinkProvider.ts | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/extensions/markdown-language-features/src/features/documentLinkProvider.ts b/extensions/markdown-language-features/src/features/documentLinkProvider.ts index ded01382275..ec07f8339b5 100644 --- a/extensions/markdown-language-features/src/features/documentLinkProvider.ts +++ b/extensions/markdown-language-features/src/features/documentLinkProvider.ts @@ -50,6 +50,25 @@ function matchAll( return out; } +function extractDocumentLink( + document: vscode.TextDocument, + base: string, + pre: number, + link: string, + matchIndex: number | undefined +): vscode.DocumentLink | undefined { + const offset = (matchIndex || 0) + pre; + const linkStart = document.positionAt(offset); + const linkEnd = document.positionAt(offset + link.length); + try { + return new vscode.DocumentLink( + new vscode.Range(linkStart, linkEnd), + normalizeLink(document, link, base)); + } catch (e) { + return undefined; + } +} + export default class LinkProvider implements vscode.DocumentLinkProvider { private readonly linkPattern = /(\[((!\[(.+)\]\()(.+)\)\]|[^\]]*\])\(\s*)((([^\s\(\)]|\(\S*?\))+))\s*(".*?")?\)/g; private readonly referenceLinkPattern = /(\[([^\]]+)\]\[\s*?)([^\s\]]*?)\]/g; @@ -73,34 +92,15 @@ export default class LinkProvider implements vscode.DocumentLinkProvider { ): vscode.DocumentLink[] { const results: vscode.DocumentLink[] = []; for (const match of matchAll(this.linkPattern, text)) { - const pre = match[1]; - const link = match[6]; - const offset = (match.index || 0) + pre.length; - const linkStart = document.positionAt(offset); - const linkEnd = document.positionAt(offset + link.length); - try { - results.push(new vscode.DocumentLink( - new vscode.Range(linkStart, linkEnd), - normalizeLink(document, link, base))); - } catch (e) { - // noop + const urlLink = extractDocumentLink(document, base, match[1].length, match[6], match.index); + if (urlLink) { + results.push(urlLink); } - if (match[5]) { - const imagePre = match[3]; - const imageLink = match[5]; - const imageOffset = (match.index || 0) + imagePre.length + 1; - const imageLinkStart = document.positionAt(imageOffset); - const imageLinkEnd = document.positionAt(imageOffset + imageLink.length); - try { - results.push(new vscode.DocumentLink( - new vscode.Range(imageLinkStart, imageLinkEnd), - normalizeLink(document, imageLink, base))); - } catch (e) { - // noop - } + const imgLink = match[5] && extractDocumentLink(document, base, match[3].length + 1, match[5], match.index); + if (imgLink) { + results.push(imgLink); } } - return results; } @@ -173,4 +173,4 @@ export default class LinkProvider implements vscode.DocumentLinkProvider { } return out; } -} +} \ No newline at end of file From 9fe48f15b1a252a0e9e1ef5975301203baf10b8a Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 25 Jan 2019 08:56:20 +0100 Subject: [PATCH 119/274] fix null checks --- .../extensionManagement/node/multiExtensionManagement.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/platform/extensionManagement/node/multiExtensionManagement.ts b/src/vs/platform/extensionManagement/node/multiExtensionManagement.ts index 99b99ad7684..2e1ea397eb5 100644 --- a/src/vs/platform/extensionManagement/node/multiExtensionManagement.ts +++ b/src/vs/platform/extensionManagement/node/multiExtensionManagement.ts @@ -70,7 +70,7 @@ export class MultiExtensionManagementService extends Disposable implements IExte return Promise.reject(`Invalid location ${extension.location.toString()}`); } const promise = server.extensionManagementService.uninstall(extension); - const anotherServer = server === this.extensionManagementServerService.localExtensionManagementServer ? this.extensionManagementServerService.remoteExtensionManagementServer : this.extensionManagementServerService.localExtensionManagementServer; + const anotherServer: IExtensionManagementServer = server === this.extensionManagementServerService.localExtensionManagementServer ? this.extensionManagementServerService.remoteExtensionManagementServer! : this.extensionManagementServerService.localExtensionManagementServer; const installed = await anotherServer.extensionManagementService.getInstalled(ExtensionType.User); extension = installed.filter(i => areSameExtensions(i.identifier, extension.identifier))[0]; if (extension) { @@ -81,7 +81,7 @@ export class MultiExtensionManagementService extends Disposable implements IExte private async uninstallInServer(extension: ILocalExtension, server: IExtensionManagementServer, force?: boolean): Promise { if (server === this.extensionManagementServerService.localExtensionManagementServer) { - const installedExtensions = await this.extensionManagementServerService.remoteExtensionManagementServer.extensionManagementService.getInstalled(ExtensionType.User); + const installedExtensions = await this.extensionManagementServerService.remoteExtensionManagementServer!.extensionManagementService.getInstalled(ExtensionType.User); const dependentNonUIExtensions = installedExtensions.filter(i => !isUIExtension(i.manifest, this.configurationService) && i.manifest.extensionDependencies && i.manifest.extensionDependencies.some(id => areSameExtensions({ id }, extension.identifier))); if (dependentNonUIExtensions.length) { @@ -157,7 +157,7 @@ export class MultiExtensionManagementService extends Disposable implements IExte if (manifest) { if (syncExtensions) { // Install on both servers - return Promise.all(this.servers.map(server => server.extensionManagementService.installFromGallery(gallery))).then(() => null); + return Promise.all(this.servers.map(server => server.extensionManagementService.installFromGallery(gallery))).then(() => undefined); } if (isUIExtension(manifest, this.configurationService)) { // Install only on local server From 2acc653b8e6555834eed0244bd42129bb492bcfd Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 25 Jan 2019 09:33:11 +0100 Subject: [PATCH 120/274] Ignore installing UI extensions on remote extension management server --- .../sharedProcess/sharedProcessMain.ts | 2 +- .../node/extensionManagementService.ts | 25 ++++++++++++++++--- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index 7c52379bc9b..a342222d046 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -123,7 +123,7 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I } server.registerChannel('telemetryAppender', new TelemetryAppenderChannel(appInsightsAppender)); - services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService)); + services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService, [false])); services.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService)); services.set(ILocalizationsService, new SyncDescriptor(LocalizationsService)); diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index 48f35949f1d..b3656aee654 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -44,7 +44,8 @@ import { Schemas } from 'vs/base/common/network'; import { CancellationToken } from 'vs/base/common/cancellation'; import { getPathFromAmdModule } from 'vs/base/common/amd'; import { getManifest } from 'vs/platform/extensionManagement/node/extensionManagementUtil'; -import { IExtensionManifest, ExtensionType, ExtensionIdentifierWithVersion } from 'vs/platform/extensions/common/extensions'; +import { IExtensionManifest, ExtensionType, ExtensionIdentifierWithVersion, isUIExtension } from 'vs/platform/extensions/common/extensions'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; const ERROR_SCANNING_SYS_EXTENSIONS = 'scanningSystem'; const ERROR_SCANNING_USER_EXTENSIONS = 'scanningUser'; @@ -127,7 +128,9 @@ export class ExtensionManagementService extends Disposable implements IExtension onDidUninstallExtension: Event = this._onDidUninstallExtension.event; constructor( + private readonly remote: boolean, @IEnvironmentService private readonly environmentService: IEnvironmentService, + @IConfigurationService private readonly configurationService: IConfigurationService, @IExtensionGalleryService private readonly galleryService: IExtensionGalleryService, @ILogService private readonly logService: ILogService, @optional(IDownloadService) private downloadService: IDownloadService, @@ -339,6 +342,13 @@ export class ExtensionManagementService extends Disposable implements IExtension return Promise.reject(new ExtensionManagementError(nls.localize('notFoundCompatibleDependency', "Unable to install because, the extension '{0}' compatible with current version '{1}' of VS Code is not found.", extension.identifier.id, pkg.version), INSTALL_ERROR_INCOMPATIBLE)); } + if (this.remote) { + const manifest = await this.galleryService.getManifest(extension, CancellationToken.None); + if (manifest && isUIExtension(manifest, this.configurationService)) { + return Promise.reject(new Error(nls.localize('notSupportedUIExtension', "Can't install extension {0} since UI Extensions are not supported", extension.identifier.id))); + } + } + return compatibleExtension; } @@ -480,7 +490,7 @@ export class ExtensionManagementService extends Disposable implements IExtension }); } - private installDependenciesAndPackExtensions(installed: ILocalExtension, existing: ILocalExtension | null): Promise { + private async installDependenciesAndPackExtensions(installed: ILocalExtension, existing: ILocalExtension | null): Promise { if (this.galleryService.isEnabled()) { const dependenciesAndPackExtensions: string[] = installed.manifest.extensionDependencies || []; if (installed.manifest.extensionPack) { @@ -502,7 +512,16 @@ export class ExtensionManagementService extends Disposable implements IExtension return this.galleryService.query({ names, pageSize: dependenciesAndPackExtensions.length }) .then(galleryResult => { const extensionsToInstall = galleryResult.firstPage; - return Promise.all(extensionsToInstall.map(e => this.installFromGallery(e))) + return Promise.all(extensionsToInstall.map(async e => { + if (this.remote) { + const manifest = await this.galleryService.getManifest(e, CancellationToken.None); + if (manifest && isUIExtension(manifest, this.configurationService)) { + this.logService.info('Ignored installing the UI dependency', e.identifier.id); + return; + } + } + return this.installFromGallery(e); + })) .then(() => null, errors => this.rollback(extensionsToInstall).then(() => Promise.reject(errors), () => Promise.reject(errors))); }); } From e5f18fe000a8fd5c542e47b8ff73c92d6dd32ea0 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 25 Jan 2019 10:00:23 +0100 Subject: [PATCH 121/274] Fix #66986 --- .../extensionManagement/common/extensionEnablementService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/extensionManagement/common/extensionEnablementService.ts b/src/vs/platform/extensionManagement/common/extensionEnablementService.ts index 91bce7d47d9..f4b564914e8 100644 --- a/src/vs/platform/extensionManagement/common/extensionEnablementService.ts +++ b/src/vs/platform/extensionManagement/common/extensionEnablementService.ts @@ -375,7 +375,7 @@ class StorageManager extends Disposable { } private _get(key: string, scope: StorageScope): string { - return this.storageService.get(key, scope, '[]'); + return this.storageService.get(key, scope, '[]') || '[]'; } private _set(key: string, value: string | undefined, scope: StorageScope): void { From 2cddb77756413f6caa664165d5c99a7bc082b10d Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 25 Jan 2019 10:25:13 +0100 Subject: [PATCH 122/274] Fix #66325 --- .../workbench/parts/markers/electron-browser/markersPanel.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts b/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts index 312517f9f86..61e67c71b45 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts @@ -408,13 +408,12 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { private onDidChangeModel(resources: URI[]) { for (const resource of resources) { + this.markersViewState.remove(resource); const resourceMarkers = this.markersWorkbenchService.markersModel.getResourceMarkers(resource); if (resourceMarkers) { for (const marker of resourceMarkers.markers) { this.markersViewState.add(marker); } - } else { - this.markersViewState.remove(resource); } } this.currentResourceGotAddedToMarkersData = this.currentResourceGotAddedToMarkersData || this.isCurrentResourceGotAddedToMarkersData(resources); From 7a9f7e5e1f7abf234d25b69a81885f22e84753d9 Mon Sep 17 00:00:00 2001 From: Christian Flach Date: Fri, 25 Jan 2019 11:13:00 +0100 Subject: [PATCH 123/274] Add terminalGroup to tasks to allow running them in split panes (#65973) * Return splitted instance from ITerminalService::splitInstance * Add support for terminalGroup to tasks * Early-exit when the tab could not be split https://github.com/Microsoft/vscode/pull/65973/files/42e3171a71ae4b6963e47318fd289a66eb48a96a#r245762395 * Use .get()! instead of the unsupported [] to access LinkedMap * Move api changes into `vscode.proposed.d.ts` * Rename "terminalGroup" to "group" * Only keep references to terminals in sameTaskTerminals and idleTaskTerminals if the terminal is not disposed * Type result variable Fixes #47265 --- src/vs/vscode.proposed.d.ts | 8 ++ src/vs/workbench/api/shared/tasks.ts | 1 + src/vs/workbench/parts/tasks/common/tasks.ts | 5 ++ .../tasks/electron-browser/jsonSchema_v2.ts | 6 +- .../electron-browser/terminalTaskSystem.ts | 76 ++++++++++++++----- .../parts/tasks/node/taskConfiguration.ts | 13 +++- .../parts/terminal/common/terminal.ts | 2 +- .../parts/terminal/common/terminalService.ts | 18 +++-- 8 files changed, 99 insertions(+), 30 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index e2c6fb84fdc..f68d3533d7f 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1111,6 +1111,14 @@ declare module 'vscode' { } //#endregion + //#region Tasks + export interface TaskPresentationOptions { + /** + * Controls whether the task is executed in a specific terminal group using split panes. + */ + group?: string; + } + //#endregion //#region Autofix - mjbvz export namespace CodeActionKind { diff --git a/src/vs/workbench/api/shared/tasks.ts b/src/vs/workbench/api/shared/tasks.ts index 06dc343e75b..0a1d3b6535b 100644 --- a/src/vs/workbench/api/shared/tasks.ts +++ b/src/vs/workbench/api/shared/tasks.ts @@ -18,6 +18,7 @@ export interface TaskPresentationOptionsDTO { panel?: number; showReuseMessage?: boolean; clear?: boolean; + group?: string; } export interface RunOptionsDTO { diff --git a/src/vs/workbench/parts/tasks/common/tasks.ts b/src/vs/workbench/parts/tasks/common/tasks.ts index fce06577c70..98128bbbfea 100644 --- a/src/vs/workbench/parts/tasks/common/tasks.ts +++ b/src/vs/workbench/parts/tasks/common/tasks.ts @@ -214,6 +214,11 @@ export interface PresentationOptions { * Controls whether to clear the terminal before executing the task. */ clear: boolean; + + /** + * Controls whether the task is executed in a specific terminal group using split panes. + */ + group?: string; } export namespace PresentationOptions { diff --git a/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.ts b/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.ts index fdc154f2ddb..f2a5ff58613 100644 --- a/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.ts +++ b/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.ts @@ -126,7 +126,11 @@ const presentation: IJSONSchema = { type: 'boolean', default: false, description: nls.localize('JsonSchema.tasks.presentation.clear', 'Controls whether the terminal is cleared before executing the task.') - } + }, + group: { + type: 'string', + description: nls.localize('JsonSchema.tasks.presentation.group', 'Controls whether the task is executed in a specific terminal group using split panes.') + }, } }; diff --git a/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts b/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts index 65fcf039535..7dac1287f27 100644 --- a/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts +++ b/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts @@ -10,7 +10,7 @@ import * as Objects from 'vs/base/common/objects'; import * as Types from 'vs/base/common/types'; import * as Platform from 'vs/base/common/platform'; import * as Async from 'vs/base/common/async'; -import { IStringDictionary } from 'vs/base/common/collections'; +import { IStringDictionary, values } from 'vs/base/common/collections'; import { LinkedMap, Touch } from 'vs/base/common/map'; import Severity from 'vs/base/common/severity'; import { Event, Emitter } from 'vs/base/common/event'; @@ -42,6 +42,7 @@ import { interface TerminalData { terminal: ITerminalInstance; lastTask: string; + group?: string; } interface ActiveTerminalData { @@ -538,13 +539,16 @@ export class TerminalTaskSystem implements ITaskSystem { let key = task.getMapKey(); delete this.activeTasks[key]; this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Changed)); - switch (task.command.presentation!.panel) { - case PanelKind.Dedicated: - this.sameTaskTerminals[key] = terminal!.id.toString(); - break; - case PanelKind.Shared: - this.idleTaskTerminals.set(key, terminal!.id.toString(), Touch.AsOld); - break; + if (exitCode !== undefined) { + // Only keep a reference to the terminal if it is not being disposed. + switch (task.command.presentation!.panel) { + case PanelKind.Dedicated: + this.sameTaskTerminals[key] = terminal!.id.toString(); + break; + case PanelKind.Shared: + this.idleTaskTerminals.set(key, terminal!.id.toString(), Touch.AsOld); + break; + } } let reveal = task.command.presentation!.reveal; if ((reveal === RevealKind.Silent) && ((exitCode !== 0) || (watchingProblemMatcher.numberOfMatches > 0) && watchingProblemMatcher.maxMarkerSeverity && @@ -601,13 +605,16 @@ export class TerminalTaskSystem implements ITaskSystem { let key = task.getMapKey(); delete this.activeTasks[key]; this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Changed)); - switch (task.command.presentation!.panel) { - case PanelKind.Dedicated: - this.sameTaskTerminals[key] = terminal!.id.toString(); - break; - case PanelKind.Shared: - this.idleTaskTerminals.set(key, terminal!.id.toString(), Touch.AsOld); - break; + if (exitCode !== undefined) { + // Only keep a reference to the terminal if it is not being disposed. + switch (task.command.presentation!.panel) { + case PanelKind.Dedicated: + this.sameTaskTerminals[key] = terminal!.id.toString(); + break; + case PanelKind.Shared: + this.idleTaskTerminals.set(key, terminal!.id.toString(), Touch.AsOld); + break; + } } let reveal = task.command.presentation!.reveal; if (terminal && (reveal === RevealKind.Silent) && ((exitCode !== 0) || (startStopProblemMatcher.numberOfMatches > 0) && startStopProblemMatcher.maxMarkerSeverity && @@ -857,6 +864,7 @@ export class TerminalTaskSystem implements ITaskSystem { } let prefersSameTerminal = presentationOptions.panel === PanelKind.Dedicated; let allowsSharedTerminal = presentationOptions.panel === PanelKind.Shared; + let group = presentationOptions.group; let taskKey = task.getMapKey(); let terminalToReuse: TerminalData | undefined; @@ -867,7 +875,20 @@ export class TerminalTaskSystem implements ITaskSystem { delete this.sameTaskTerminals[taskKey]; } } else if (allowsSharedTerminal) { - let terminalId = this.idleTaskTerminals.remove(taskKey) || this.idleTaskTerminals.shift(); + // Always allow to reuse the terminal previously used by the same task. + let terminalId = this.idleTaskTerminals.remove(taskKey); + if (!terminalId) { + // There is no idle terminal which was used by the same task. + // Search for any idle terminal used previously by a task of the same group + // (or, if the task has no group, a terminal used by a task without group). + for (const taskId of this.idleTaskTerminals.keys()) { + const idleTerminalId = this.idleTaskTerminals.get(taskId)!; + if (this.terminals[idleTerminalId].group === group) { + terminalId = this.idleTaskTerminals.remove(taskId); + break; + } + } + } if (terminalId) { terminalToReuse = this.terminals[terminalId]; } @@ -880,7 +901,26 @@ export class TerminalTaskSystem implements ITaskSystem { return [terminalToReuse.terminal, commandExecutable, undefined]; } - const result = this.terminalService.createTerminal(this.currentTask.shellLaunchConfig); + let result: ITerminalInstance | null = null; + if (group) { + // Try to find an existing terminal to split. + // Even if an existing terminal is found, the split can fail if the terminal width is too small. + for (const terminal of values(this.terminals)) { + if (terminal.group === group) { + const originalInstance = terminal.terminal; + const config = this.currentTask.shellLaunchConfig; + result = this.terminalService.splitInstance(originalInstance, config); + if (result) { + break; + } + } + } + } + if (!result) { + // Either no group is used, no terminal with the group exists or splitting an existing terminal failed. + result = this.terminalService.createTerminal(this.currentTask.shellLaunchConfig); + } + const terminalKey = result.id.toString(); result.onDisposed((terminal) => { let terminalData = this.terminals[terminalKey]; @@ -895,7 +935,7 @@ export class TerminalTaskSystem implements ITaskSystem { delete this.activeTasks[task.getMapKey()]; } }); - this.terminals[terminalKey] = { terminal: result, lastTask: taskKey }; + this.terminals[terminalKey] = { terminal: result, lastTask: taskKey, group }; return [result, commandExecutable, undefined]; } diff --git a/src/vs/workbench/parts/tasks/node/taskConfiguration.ts b/src/vs/workbench/parts/tasks/node/taskConfiguration.ts index 23a661b1c9f..afb7a097daf 100644 --- a/src/vs/workbench/parts/tasks/node/taskConfiguration.ts +++ b/src/vs/workbench/parts/tasks/node/taskConfiguration.ts @@ -118,6 +118,11 @@ export interface PresentationOptionsConfig { * Controls whether the terminal should be cleared before running the task. */ clear?: boolean; + + /** + * Controls whether the task is executed in a specific terminal group using split panes. + */ + group?: string; } export interface RunOptionsConfig { @@ -794,7 +799,7 @@ namespace CommandOptions { namespace CommandConfiguration { export namespace PresentationOptions { - const properties: MetaData[] = [{ property: 'echo' }, { property: 'reveal' }, { property: 'focus' }, { property: 'panel' }, { property: 'showReuseMessage' }, { property: 'clear' }]; + const properties: MetaData[] = [{ property: 'echo' }, { property: 'reveal' }, { property: 'focus' }, { property: 'panel' }, { property: 'showReuseMessage' }, { property: 'clear' }, { property: 'group' }]; interface PresentationOptionsShape extends LegacyCommandProperties { presentation?: PresentationOptionsConfig; @@ -807,6 +812,7 @@ namespace CommandConfiguration { let panel: Tasks.PanelKind; let showReuseMessage: boolean; let clear: boolean; + let group: string | undefined; let hasProps = false; if (Types.isBoolean(config.echoCommand)) { echo = config.echoCommand; @@ -836,12 +842,15 @@ namespace CommandConfiguration { if (Types.isBoolean(presentation.clear)) { clear = presentation.clear; } + if (Types.isString(presentation.group)) { + group = presentation.group; + } hasProps = true; } if (!hasProps) { return undefined; } - return { echo: echo!, reveal: reveal!, focus: focus!, panel: panel!, showReuseMessage: showReuseMessage!, clear: clear! }; + return { echo: echo!, reveal: reveal!, focus: focus!, panel: panel!, showReuseMessage: showReuseMessage!, clear: clear!, group }; } export function assignProperties(target: Tasks.PresentationOptions, source: Tasks.PresentationOptions | undefined): Tasks.PresentationOptions | undefined { diff --git a/src/vs/workbench/parts/terminal/common/terminal.ts b/src/vs/workbench/parts/terminal/common/terminal.ts index 28ff29643b2..155b7c68578 100644 --- a/src/vs/workbench/parts/terminal/common/terminal.ts +++ b/src/vs/workbench/parts/terminal/common/terminal.ts @@ -234,7 +234,7 @@ export interface ITerminalService { setActiveInstance(terminalInstance: ITerminalInstance): void; setActiveInstanceByIndex(terminalIndex: number): void; getActiveOrCreateInstance(wasNewTerminalAction?: boolean): ITerminalInstance; - splitInstance(instance: ITerminalInstance, shell?: IShellLaunchConfig): void; + splitInstance(instance: ITerminalInstance, shell?: IShellLaunchConfig): ITerminalInstance | null; getActiveTab(): ITerminalTab | null; setActiveTabToNext(): void; diff --git a/src/vs/workbench/parts/terminal/common/terminalService.ts b/src/vs/workbench/parts/terminal/common/terminalService.ts index 447418f27f5..9728d8318fa 100644 --- a/src/vs/workbench/parts/terminal/common/terminalService.ts +++ b/src/vs/workbench/parts/terminal/common/terminalService.ts @@ -264,21 +264,23 @@ export abstract class TerminalService implements ITerminalService { this.setActiveTabByIndex(newIndex); } - public splitInstance(instanceToSplit: ITerminalInstance, shellLaunchConfig: IShellLaunchConfig = {}): void { + public splitInstance(instanceToSplit: ITerminalInstance, shellLaunchConfig: IShellLaunchConfig = {}): ITerminalInstance | null { const tab = this._getTabForInstance(instanceToSplit); if (!tab) { - return; + return null; } const instance = tab.split(this._terminalFocusContextKey, this.configHelper, shellLaunchConfig); - if (instance) { - this._initInstanceListeners(instance); - this._onInstancesChanged.fire(); - - this._terminalTabs.forEach((t, i) => t.setVisible(i === this._activeTabIndex)); - } else { + if (!instance) { this._showNotEnoughSpaceToast(); + return null; } + + this._initInstanceListeners(instance); + this._onInstancesChanged.fire(); + + this._terminalTabs.forEach((t, i) => t.setVisible(i === this._activeTabIndex)); + return instance; } protected _initInstanceListeners(instance: ITerminalInstance): void { From a09175a59e0d1dfbc42b962ee735437dcd473a16 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 25 Jan 2019 11:43:11 +0100 Subject: [PATCH 124/274] real fix for #66986 --- .../common/extensionEnablementService.ts | 2 +- src/vs/platform/storage/common/storage.ts | 4 ++-- src/vs/platform/storage/node/storageIpc.ts | 2 +- .../platform/storage/node/storageMainService.ts | 15 ++++++++++++--- 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/vs/platform/extensionManagement/common/extensionEnablementService.ts b/src/vs/platform/extensionManagement/common/extensionEnablementService.ts index f4b564914e8..91bce7d47d9 100644 --- a/src/vs/platform/extensionManagement/common/extensionEnablementService.ts +++ b/src/vs/platform/extensionManagement/common/extensionEnablementService.ts @@ -375,7 +375,7 @@ class StorageManager extends Disposable { } private _get(key: string, scope: StorageScope): string { - return this.storageService.get(key, scope, '[]') || '[]'; + return this.storageService.get(key, scope, '[]'); } private _set(key: string, value: string | undefined, scope: StorageScope): void { diff --git a/src/vs/platform/storage/common/storage.ts b/src/vs/platform/storage/common/storage.ts index 54d0aeb6fe0..02513305828 100644 --- a/src/vs/platform/storage/common/storage.ts +++ b/src/vs/platform/storage/common/storage.ts @@ -67,8 +67,8 @@ export interface IStorageService { * The scope argument allows to define the scope of the storage * operation to either the current workspace only or all workspaces. */ - getInteger(key: string, scope: StorageScope, fallbackValue: number): number; - getInteger(key: string, scope: StorageScope, fallbackValue?: number): number | undefined; + getInteger(key: string, scope: StorageScope, fallbackValue: number): number; + getInteger(key: string, scope: StorageScope, fallbackValue?: number): number | undefined; /** * Store a value under the given key to storage. The value will be converted to a string. diff --git a/src/vs/platform/storage/node/storageIpc.ts b/src/vs/platform/storage/node/storageIpc.ts index 886049f1b15..40749376be2 100644 --- a/src/vs/platform/storage/node/storageIpc.ts +++ b/src/vs/platform/storage/node/storageIpc.ts @@ -57,7 +57,7 @@ export class GlobalStorageDatabaseChannel extends Disposable implements IServerC private serializeEvents(events: IStorageChangeEvent[]): ISerializableItemsChangeEvent { const items = new Map(); - events.forEach(event => items.set(event.key, this.storageMainService.get(event.key, ''))); + events.forEach(event => items.set(event.key, this.storageMainService.get(event.key))); return { items: mapToSerializable(items) } as ISerializableItemsChangeEvent; } diff --git a/src/vs/platform/storage/node/storageMainService.ts b/src/vs/platform/storage/node/storageMainService.ts index f8f5d6be928..71f7299951f 100644 --- a/src/vs/platform/storage/node/storageMainService.ts +++ b/src/vs/platform/storage/node/storageMainService.ts @@ -39,6 +39,7 @@ export interface IStorageMainService { * the provided defaultValue if the element is null or undefined. */ get(key: string, fallbackValue: string): string; + get(key: string, fallbackValue?: string): string | undefined; /** * Retrieve an element stored with the given key from storage. Use @@ -46,6 +47,7 @@ export interface IStorageMainService { * will be converted to a boolean. */ getBoolean(key: string, fallbackValue: boolean): boolean; + getBoolean(key: string, fallbackValue?: boolean): boolean | undefined; /** * Retrieve an element stored with the given key from storage. Use @@ -53,6 +55,7 @@ export interface IStorageMainService { * will be converted to a number using parseInt with a base of 10. */ getInteger(key: string, fallbackValue: number): number; + getInteger(key: string, fallbackValue?: number): number | undefined; /** * Store a string value under the given key to storage. The value will @@ -345,15 +348,21 @@ export class StorageMainService extends Disposable implements IStorageMainServic }); } - get(key: string, fallbackValue: string): string { + get(key: string, fallbackValue: string): string; + get(key: string, fallbackValue?: string): string | undefined; + get(key: string, fallbackValue?: string): string | undefined { return this.storage.get(key, fallbackValue); } - getBoolean(key: string, fallbackValue: boolean): boolean { + getBoolean(key: string, fallbackValue: boolean): boolean; + getBoolean(key: string, fallbackValue?: boolean): boolean | undefined; + getBoolean(key: string, fallbackValue?: boolean): boolean | undefined { return this.storage.getBoolean(key, fallbackValue); } - getInteger(key: string, fallbackValue: number): number { + getInteger(key: string, fallbackValue: number): number; + getInteger(key: string, fallbackValue?: number): number | undefined; + getInteger(key: string, fallbackValue?: number): number | undefined { return this.storage.getInteger(key, fallbackValue); } From 50a0e6e8c14418c2fbaa9a2af7466058e0de6583 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Thu, 24 Jan 2019 15:33:07 +0100 Subject: [PATCH 125/274] saveWorkspace on renderer --- src/vs/code/electron-main/windows.ts | 26 ++-- src/vs/platform/windows/common/windows.ts | 4 +- .../windows/electron-browser/windowService.ts | 2 +- .../platform/windows/electron-main/windows.ts | 2 +- .../windows/electron-main/windowsService.ts | 2 +- src/vs/platform/windows/node/windowsIpc.ts | 4 +- .../platform/workspaces/common/workspaces.ts | 4 +- .../electron-main/workspacesMainService.ts | 48 ++++--- src/vs/platform/workspaces/node/workspaces.ts | 49 ++++--- .../electron-browser/mainThreadWorkspace.ts | 2 +- .../browser/actions/workspaceActions.ts | 2 +- .../node/configurationService.ts | 11 +- .../workspace/common/workspaceEditing.ts | 4 +- .../workspace/node/workspaceEditingService.ts | 120 ++++++++++++++++-- .../workbench/test/workbenchTestServices.ts | 4 +- 15 files changed, 201 insertions(+), 83 deletions(-) diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 7eff695f79a..88faa4175f6 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -34,7 +34,7 @@ import { normalizeNFC } from 'vs/base/common/normalization'; import { URI } from 'vs/base/common/uri'; import { Queue, timeout } from 'vs/base/common/async'; import { exists } from 'vs/base/node/pfs'; -import { getComparisonKey, isEqual, normalizePath } from 'vs/base/common/resources'; +import { getComparisonKey, isEqual, normalizePath, basename as resourcesBasename } from 'vs/base/common/resources'; import { endsWith } from 'vs/base/common/strings'; import { getRemoteAuthority } from 'vs/platform/remote/common/remoteHosts'; @@ -1474,7 +1474,7 @@ export class WindowsManager implements IWindowsMainService { return this.workspacesManager.saveAndEnterWorkspace(win, path).then(result => this.doEnterWorkspace(win, result)); } - enterWorkspace(win: ICodeWindow, path: string): Promise { + enterWorkspace(win: ICodeWindow, path: URI): Promise { return this.workspacesManager.enterWorkspace(win, path).then(result => this.doEnterWorkspace(win, result)); } @@ -1968,14 +1968,14 @@ class WorkspacesManager { } saveAndEnterWorkspace(window: ICodeWindow, path: string): Promise { - if (!window || !window.win || !window.isReady || !window.openedWorkspace || !path || !this.isValidTargetWorkspacePath(window, path)) { + if (!window || !window.win || !window.isReady || !window.openedWorkspace || !path || !this.isValidTargetWorkspacePath(window, URI.file(path))) { return Promise.resolve(null); // return early if the window is not ready or disposed or does not have a workspace } return this.doSaveAndOpenWorkspace(window, window.openedWorkspace, path); } - enterWorkspace(window: ICodeWindow, path: string): Promise { + enterWorkspace(window: ICodeWindow, path: URI): Promise { if (!window || !window.win || !window.isReady) { return Promise.resolve(null); // return early if the window is not ready or disposed } @@ -1984,20 +1984,18 @@ class WorkspacesManager { if (!isValid) { return null; // return early if the workspace is not valid } - - return this.workspacesMainService.resolveWorkspace(path).then(workspace => { - return this.doOpenWorkspace(window, workspace); - }); + const workspaceIdentifier = this.workspacesMainService.getWorkspaceIdentifier(path); + return this.doOpenWorkspace(window, workspaceIdentifier); }); } createAndEnterWorkspace(window: ICodeWindow, folders?: IWorkspaceFolderCreationData[], path?: string): Promise { - if (!window || !window.win || !window.isReady) { + if (!window || !window.win || !window.isReady || !path) { return Promise.resolve(null); // return early if the window is not ready or disposed } - return this.isValidTargetWorkspacePath(window, path).then(isValid => { + return this.isValidTargetWorkspacePath(window, URI.file(path)).then(isValid => { if (!isValid) { return null; // return early if the workspace is not valid } @@ -2008,22 +2006,22 @@ class WorkspacesManager { }); } - private isValidTargetWorkspacePath(window: ICodeWindow, path?: string): Promise { + private isValidTargetWorkspacePath(window: ICodeWindow, path?: URI): Promise { if (!path) { return Promise.resolve(true); } - if (window.openedWorkspace && window.openedWorkspace.configPath === path) { + if (window.openedWorkspace && isEqual(URI.file(window.openedWorkspace.configPath), path)) { return Promise.resolve(false); // window is already opened on a workspace with that path } // Prevent overwriting a workspace that is currently opened in another window - if (findWindowOnWorkspace(this.windowsMainService.getWindows(), { id: this.workspacesMainService.getWorkspaceId(path), configPath: path })) { + if (findWindowOnWorkspace(this.windowsMainService.getWindows(), this.workspacesMainService.getWorkspaceIdentifier(path))) { const options: Electron.MessageBoxOptions = { title: product.nameLong, type: 'info', buttons: [localize('ok', "OK")], - message: localize('workspaceOpenedMessage', "Unable to save workspace '{0}'", basename(path)), + message: localize('workspaceOpenedMessage', "Unable to save workspace '{0}'", resourcesBasename(path)), detail: localize('workspaceOpenedDetail', "The workspace is already opened in another window. Please close that window first and then try again."), noLink: true }; diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index 2d1761c2446..78292433ce5 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -114,7 +114,7 @@ export interface IWindowsService { openDevTools(windowId: number, options?: IDevToolsOptions): Promise; toggleDevTools(windowId: number): Promise; closeWorkspace(windowId: number): Promise; - enterWorkspace(windowId: number, path: string): Promise; + enterWorkspace(windowId: number, path: URI): Promise; createAndEnterWorkspace(windowId: number, folders?: IWorkspaceFolderCreationData[], path?: string): Promise; saveAndEnterWorkspace(windowId: number, path: string): Promise; toggleFullScreen(windowId: number): Promise; @@ -207,7 +207,7 @@ export interface IWindowService { toggleDevTools(): Promise; closeWorkspace(): Promise; updateTouchBar(items: ISerializableCommandAction[][]): Promise; - enterWorkspace(path: string): Promise; + enterWorkspace(path: URI): Promise; createAndEnterWorkspace(folders?: IWorkspaceFolderCreationData[], path?: string): Promise; saveAndEnterWorkspace(path: string): Promise; toggleFullScreen(): Promise; diff --git a/src/vs/platform/windows/electron-browser/windowService.ts b/src/vs/platform/windows/electron-browser/windowService.ts index 60457efd10c..040f396f8d9 100644 --- a/src/vs/platform/windows/electron-browser/windowService.ts +++ b/src/vs/platform/windows/electron-browser/windowService.ts @@ -89,7 +89,7 @@ export class WindowService extends Disposable implements IWindowService { return this.windowsService.closeWorkspace(this.windowId); } - enterWorkspace(path: string): Promise { + enterWorkspace(path: URI): Promise { return this.windowsService.enterWorkspace(this.windowId, path); } diff --git a/src/vs/platform/windows/electron-main/windows.ts b/src/vs/platform/windows/electron-main/windows.ts index 839ba5b7b6f..b8756fd9130 100644 --- a/src/vs/platform/windows/electron-main/windows.ts +++ b/src/vs/platform/windows/electron-main/windows.ts @@ -94,7 +94,7 @@ export interface IWindowsMainService { // methods ready(initialUserEnv: IProcessEnvironment): void; reload(win: ICodeWindow, cli?: ParsedArgs): void; - enterWorkspace(win: ICodeWindow, path: string): Promise; + enterWorkspace(win: ICodeWindow, path: URI): Promise; createAndEnterWorkspace(win: ICodeWindow, folders?: IWorkspaceFolderCreationData[], path?: string): Promise; saveAndEnterWorkspace(win: ICodeWindow, path: string): Promise; closeWorkspace(win: ICodeWindow): void; diff --git a/src/vs/platform/windows/electron-main/windowsService.ts b/src/vs/platform/windows/electron-main/windowsService.ts index 8e3998a11e3..0f116f6bc9c 100644 --- a/src/vs/platform/windows/electron-main/windowsService.ts +++ b/src/vs/platform/windows/electron-main/windowsService.ts @@ -138,7 +138,7 @@ export class WindowsService implements IWindowsService, IURLHandler, IDisposable return this.withWindow(windowId, codeWindow => this.windowsMainService.closeWorkspace(codeWindow)); } - async enterWorkspace(windowId: number, path: string): Promise { + async enterWorkspace(windowId: number, path: URI): Promise { this.logService.trace('windowsService#enterWorkspace', windowId); return this.withWindow(windowId, codeWindow => this.windowsMainService.enterWorkspace(codeWindow, path)); diff --git a/src/vs/platform/windows/node/windowsIpc.ts b/src/vs/platform/windows/node/windowsIpc.ts index f590f6c7832..ddd42188cd6 100644 --- a/src/vs/platform/windows/node/windowsIpc.ts +++ b/src/vs/platform/windows/node/windowsIpc.ts @@ -56,7 +56,7 @@ export class WindowsChannel implements IServerChannel { case 'openDevTools': return this.service.openDevTools(arg[0], arg[1]); case 'toggleDevTools': return this.service.toggleDevTools(arg); case 'closeWorkspace': return this.service.closeWorkspace(arg); - case 'enterWorkspace': return this.service.enterWorkspace(arg[0], arg[1]); + case 'enterWorkspace': return this.service.enterWorkspace(arg[0], URI.revive(arg[1])); case 'createAndEnterWorkspace': { const rawFolders: IWorkspaceFolderCreationData[] = arg[1]; let folders: IWorkspaceFolderCreationData[] | undefined = undefined; @@ -179,7 +179,7 @@ export class WindowsChannelClient implements IWindowsService { return this.channel.call('closeWorkspace', windowId); } - enterWorkspace(windowId: number, path: string): Promise { + enterWorkspace(windowId: number, path: URI): Promise { return this.channel.call('enterWorkspace', [windowId, path]); } diff --git a/src/vs/platform/workspaces/common/workspaces.ts b/src/vs/platform/workspaces/common/workspaces.ts index d913586c132..7b8a8a09b23 100644 --- a/src/vs/platform/workspaces/common/workspaces.ts +++ b/src/vs/platform/workspaces/common/workspaces.ts @@ -84,8 +84,6 @@ export interface IWorkspacesMainService extends IWorkspacesService { createWorkspaceSync(folders?: IWorkspaceFolderCreationData[]): IWorkspaceIdentifier; - resolveWorkspace(path: string): Promise; - resolveWorkspaceSync(path: string): IResolvedWorkspace | null; isUntitledWorkspace(workspace: IWorkspaceIdentifier): boolean; @@ -95,6 +93,8 @@ export interface IWorkspacesMainService extends IWorkspacesService { getUntitledWorkspacesSync(): IWorkspaceIdentifier[]; getWorkspaceId(workspacePath: string): string; + + getWorkspaceIdentifier(workspacePath: URI): IWorkspaceIdentifier; } export interface IWorkspacesService { diff --git a/src/vs/platform/workspaces/electron-main/workspacesMainService.ts b/src/vs/platform/workspaces/electron-main/workspacesMainService.ts index 2828f1268ae..c7521c466ab 100644 --- a/src/vs/platform/workspaces/electron-main/workspacesMainService.ts +++ b/src/vs/platform/workspaces/electron-main/workspacesMainService.ts @@ -6,7 +6,7 @@ import { IWorkspacesMainService, IWorkspaceIdentifier, WORKSPACE_EXTENSION, IWorkspaceSavedEvent, UNTITLED_WORKSPACE_NAME, IResolvedWorkspace, IStoredWorkspaceFolder, isRawFileWorkspaceFolder, isStoredWorkspaceFolder, IWorkspaceFolderCreationData } from 'vs/platform/workspaces/common/workspaces'; import { isParent } from 'vs/platform/files/common/files'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { extname, join, dirname, isAbsolute, resolve } from 'path'; +import { join, dirname, isAbsolute, resolve, extname } from 'path'; import { mkdirp, writeFile, readFile } from 'vs/base/node/pfs'; import { readFileSync, existsSync, mkdirSync, writeFileSync } from 'fs'; import { isLinux, isMacintosh } from 'vs/base/common/platform'; @@ -23,6 +23,7 @@ import { toWorkspaceFolders } from 'vs/platform/workspace/common/workspace'; import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; import { Disposable } from 'vs/base/common/lifecycle'; +import { fsPath, dirname as resourcesDirname } from 'vs/base/common/resources'; export interface IStoredWorkspace { folders: IStoredWorkspaceFolder[]; @@ -49,14 +50,6 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain this.workspacesHome = environmentService.workspacesHome; } - resolveWorkspace(path: string): Promise { - if (!this.isWorkspacePath(path)) { - return Promise.resolve(null); // does not look like a valid workspace config file - } - - return readFile(path, 'utf8').then(contents => this.doResolveWorkspace(path, contents)); - } - resolveWorkspaceSync(path: string): IResolvedWorkspace | null { if (!this.isWorkspacePath(path)) { return null; // does not look like a valid workspace config file @@ -69,21 +62,21 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain return null; // invalid workspace } - return this.doResolveWorkspace(path, contents); + return this.doResolveWorkspace(URI.file(path), contents); } private isWorkspacePath(path: string): boolean { return this.isInsideWorkspacesHome(path) || extname(path) === `.${WORKSPACE_EXTENSION}`; } - private doResolveWorkspace(path: string, contents: string): IResolvedWorkspace | null { + private doResolveWorkspace(path: URI, contents: string): IResolvedWorkspace | null { try { const workspace = this.doParseStoredWorkspace(path, contents); - + const workspaceIdentifier = this.getWorkspaceIdentifier(path); return { - id: this.getWorkspaceId(path), - configPath: path, - folders: toWorkspaceFolders(workspace.folders, URI.file(dirname(path))) + id: workspaceIdentifier.id, + configPath: workspaceIdentifier.configPath, + folders: toWorkspaceFolders(workspace.folders, resourcesDirname(path)!) }; } catch (error) { this.logService.warn(error.toString()); @@ -92,7 +85,7 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain return null; } - private doParseStoredWorkspace(path: string, contents: string): IStoredWorkspace { + private doParseStoredWorkspace(path: URI, contents: string): IStoredWorkspace { // Parse workspace file let storedWorkspace: IStoredWorkspace = json.parse(contents); // use fault tolerant parser @@ -104,7 +97,7 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain // Validate if (!Array.isArray(storedWorkspace.folders)) { - throw new Error(`${path} looks like an invalid workspace file.`); + throw new Error(`${path.toString()} looks like an invalid workspace file.`); } return storedWorkspace; @@ -148,7 +141,7 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain // File URI if (folderResource.scheme === Schemas.file) { - storedWorkspace = { path: massageFolderPathForWorkspace(folderResource.fsPath, untitledWorkspaceConfigFolder, []) }; + storedWorkspace = { path: massageFolderPathForWorkspace(fsPath(folderResource), URI.file(untitledWorkspaceConfigFolder), []) }; } // Any URI @@ -182,6 +175,21 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain return createHash('md5').update(workspaceConfigPath).digest('hex'); } + getWorkspaceIdentifier(workspacePath: URI): IWorkspaceIdentifier { + if (workspacePath.scheme === Schemas.file) { + const configPath = fsPath(workspacePath); + return { + configPath, + id: this.getWorkspaceId(configPath) + }; + } + throw new Error('Not yet supported'); + /*return { + configPath: workspacePath + id: this.getWorkspaceId(workspacePath.toString()); + };*/ + } + isUntitledWorkspace(workspace: IWorkspaceIdentifier): boolean { return this.isInsideWorkspacesHome(workspace.configPath); } @@ -198,7 +206,7 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain const rawWorkspaceContents = raw.toString(); let storedWorkspace: IStoredWorkspace; try { - storedWorkspace = this.doParseStoredWorkspace(workspace.configPath, rawWorkspaceContents); + storedWorkspace = this.doParseStoredWorkspace(URI.file(workspace.configPath), rawWorkspaceContents); } catch (error) { return Promise.reject(error); } @@ -214,7 +222,7 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain if (!isAbsolute(folder.path)) { folder.path = resolve(sourceConfigFolder, folder.path); // relative paths get resolved against the workspace location } - folder.path = massageFolderPathForWorkspace(folder.path, targetConfigFolder, storedWorkspace.folders); + folder.path = massageFolderPathForWorkspace(folder.path, URI.file(targetConfigFolder), storedWorkspace.folders); } }); diff --git a/src/vs/platform/workspaces/node/workspaces.ts b/src/vs/platform/workspaces/node/workspaces.ts index 723d8ea2499..d34df96d54d 100644 --- a/src/vs/platform/workspaces/node/workspaces.ts +++ b/src/vs/platform/workspaces/node/workspaces.ts @@ -5,9 +5,12 @@ import { IStoredWorkspaceFolder, isRawFileWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces'; import { isWindows, isLinux } from 'vs/base/common/platform'; -import { isAbsolute, relative } from 'path'; -import { isEqualOrParent, normalize } from 'vs/base/common/paths'; +import { isAbsolute, relative, posix } from 'path'; +import { normalize, isEqualOrParent } from 'vs/base/common/paths'; import { normalizeDriveLetter } from 'vs/base/common/labels'; +import { URI } from 'vs/base/common/uri'; +import { fsPath } from 'vs/base/common/resources'; +import { Schemas } from 'vs/base/common/network'; const SLASH = '/'; @@ -19,27 +22,33 @@ const SLASH = '/'; * @param targetConfigFolder the folder where the workspace is living in * @param existingFolders a set of existing folders of the workspace */ -export function massageFolderPathForWorkspace(absoluteFolderPath: string, targetConfigFolder: string, existingFolders: IStoredWorkspaceFolder[]): string { - const useSlashesForPath = shouldUseSlashForPath(existingFolders); +export function massageFolderPathForWorkspace(absoluteFolderPath: string, targetConfigFolderURI: URI, existingFolders: IStoredWorkspaceFolder[]): string { - // Convert path to relative path if the target config folder - // is a parent of the path. - if (isEqualOrParent(absoluteFolderPath, targetConfigFolder, !isLinux)) { - absoluteFolderPath = relative(targetConfigFolder, absoluteFolderPath) || '.'; - } + if (targetConfigFolderURI.scheme === Schemas.file) { + const targetFolderPath = fsPath(targetConfigFolderURI); + // Convert path to relative path if the target config folder + // is a parent of the path. + if (isEqualOrParent(absoluteFolderPath, targetFolderPath, !isLinux)) { + absoluteFolderPath = relative(targetFolderPath, absoluteFolderPath) || '.'; + } - // Windows gets special treatment: - // - normalize all paths to get nice casing of drive letters - // - convert to slashes if we want to use slashes for paths - if (isWindows) { - if (isAbsolute(absoluteFolderPath)) { - if (useSlashesForPath) { - absoluteFolderPath = normalize(absoluteFolderPath, false /* do not use OS path separator */); + // Windows gets special treatment: + // - normalize all paths to get nice casing of drive letters + // - convert to slashes if we want to use slashes for paths + if (isWindows) { + if (isAbsolute(absoluteFolderPath)) { + if (shouldUseSlashForPath(existingFolders)) { + absoluteFolderPath = normalize(absoluteFolderPath, false /* do not use OS path separator */); + } + + absoluteFolderPath = normalizeDriveLetter(absoluteFolderPath); + } else if (shouldUseSlashForPath(existingFolders)) { + absoluteFolderPath = absoluteFolderPath.replace(/[\\]/g, SLASH); } - - absoluteFolderPath = normalizeDriveLetter(absoluteFolderPath); - } else if (useSlashesForPath) { - absoluteFolderPath = absoluteFolderPath.replace(/[\\]/g, SLASH); + } + } else { + if (isEqualOrParent(absoluteFolderPath, targetConfigFolderURI.path)) { + absoluteFolderPath = posix.relative(absoluteFolderPath, targetConfigFolderURI.path) || '.'; } } diff --git a/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts b/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts index 14baea58a97..6a435ed4660 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts @@ -245,5 +245,5 @@ CommandsRegistry.registerCommand('_workbench.enterWorkspace', async function (ac } } - return workspaceEditingService.enterWorkspace(workspace.fsPath); + return workspaceEditingService.enterWorkspace(workspace); }); diff --git a/src/vs/workbench/browser/actions/workspaceActions.ts b/src/vs/workbench/browser/actions/workspaceActions.ts index e37b7cdeb80..7da39edb68c 100644 --- a/src/vs/workbench/browser/actions/workspaceActions.ts +++ b/src/vs/workbench/browser/actions/workspaceActions.ts @@ -151,7 +151,7 @@ export class SaveWorkspaceAsAction extends Action { return this.workspaceEditingService.createAndEnterWorkspace(folders, configPath); case WorkbenchState.WORKSPACE: - return this.workspaceEditingService.saveAndEnterWorkspace(configPath); + return this.workspaceEditingService.saveAndEnterWorkspace(configPathUri); } } }); diff --git a/src/vs/workbench/services/configuration/node/configurationService.ts b/src/vs/workbench/services/configuration/node/configurationService.ts index 1148a763638..e07ca65249a 100644 --- a/src/vs/workbench/services/configuration/node/configurationService.ts +++ b/src/vs/workbench/services/configuration/node/configurationService.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from 'vs/base/common/uri'; -import { dirname } from 'path'; import * as assert from 'vs/base/common/assert'; import { Event, Emitter } from 'vs/base/common/event'; import { ResourceMap } from 'vs/base/common/map'; @@ -36,7 +35,7 @@ import { massageFolderPathForWorkspace } from 'vs/platform/workspaces/node/works import { UserConfiguration } from 'vs/platform/configuration/node/configuration'; import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema'; import { localize } from 'vs/nls'; -import { isEqual } from 'vs/base/common/resources'; +import { isEqual, dirname } from 'vs/base/common/resources'; import { mark } from 'vs/base/common/performance'; export class WorkspaceService extends Disposable implements IWorkspaceConfigurationService, IWorkspaceContextService { @@ -162,8 +161,8 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat if (foldersToAdd.length) { // Recompute current workspace folders if we have folders to add - const workspaceConfigFolder = dirname(this.getWorkspace().configuration.fsPath); - currentWorkspaceFolders = toWorkspaceFolders(newStoredFolders, URI.file(workspaceConfigFolder)); + const workspaceConfigFolder = dirname(this.getWorkspace().configuration); + currentWorkspaceFolders = toWorkspaceFolders(newStoredFolders, workspaceConfigFolder); const currentWorkspaceFolderUris = currentWorkspaceFolders.map(folder => folder.uri); const storedFoldersToAdd: IStoredWorkspaceFolder[] = []; @@ -342,7 +341,7 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat return this.workspaceConfiguration.load({ id: workspaceIdentifier.id, configPath: URI.file(workspaceIdentifier.configPath) }) .then(() => { const workspaceConfigPath = URI.file(workspaceIdentifier.configPath); - const workspaceFolders = toWorkspaceFolders(this.workspaceConfiguration.getFolders(), URI.file(dirname(workspaceConfigPath.fsPath))); + const workspaceFolders = toWorkspaceFolders(this.workspaceConfiguration.getFolders(), dirname(workspaceConfigPath)); const workspaceId = workspaceIdentifier.id; return new Workspace(workspaceId, workspaceFolders, workspaceConfigPath); }); @@ -535,7 +534,7 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat private onWorkspaceConfigurationChanged(): Promise { if (this.workspace && this.workspace.configuration && this._configuration) { const workspaceConfigurationChangeEvent = this._configuration.compareAndUpdateWorkspaceConfiguration(this.workspaceConfiguration.getConfiguration()); - let configuredFolders = toWorkspaceFolders(this.workspaceConfiguration.getFolders(), URI.file(dirname(this.workspace.configuration.fsPath))); + let configuredFolders = toWorkspaceFolders(this.workspaceConfiguration.getFolders(), dirname(this.workspace.configuration)); const changes = this.compareFolders(this.workspace.folders, configuredFolders); if (changes.added.length || changes.removed.length || changes.changed.length) { this.workspace.folders = configuredFolders; diff --git a/src/vs/workbench/services/workspace/common/workspaceEditing.ts b/src/vs/workbench/services/workspace/common/workspaceEditing.ts index db73788241f..8bd26a6efc3 100644 --- a/src/vs/workbench/services/workspace/common/workspaceEditing.ts +++ b/src/vs/workbench/services/workspace/common/workspaceEditing.ts @@ -34,7 +34,7 @@ export interface IWorkspaceEditingService { /** * enters the workspace with the provided path. */ - enterWorkspace(path: string): Promise; + enterWorkspace(path: URI): Promise; /** * creates a new workspace with the provided folders and opens it. if path is provided @@ -45,7 +45,7 @@ export interface IWorkspaceEditingService { /** * saves the workspace to the provided path and opens it. requires a workspace to be opened. */ - saveAndEnterWorkspace(path: string): Promise; + saveAndEnterWorkspace(path: URI): Promise; /** * copies current workspace settings to the target workspace. diff --git a/src/vs/workbench/services/workspace/node/workspaceEditingService.ts b/src/vs/workbench/services/workspace/node/workspaceEditingService.ts index 38bc550917e..2e9e7d8791d 100644 --- a/src/vs/workbench/services/workspace/node/workspaceEditingService.ts +++ b/src/vs/workbench/services/workspace/node/workspaceEditingService.ts @@ -7,9 +7,9 @@ import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common import { URI } from 'vs/base/common/uri'; import * as nls from 'vs/nls'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; -import { IWindowService, IEnterWorkspaceResult } from 'vs/platform/windows/common/windows'; +import { IWindowService, IEnterWorkspaceResult, MessageBoxOptions, IWindowsService } from 'vs/platform/windows/common/windows'; import { IJSONEditingService, JSONEditingError, JSONEditingErrorCode } from 'vs/workbench/services/configuration/common/jsonEditing'; -import { IWorkspaceIdentifier, IWorkspaceFolderCreationData } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspaceIdentifier, IWorkspaceFolderCreationData, IStoredWorkspace, isStoredWorkspaceFolder, isRawFileWorkspaceFolder, isWorkspaceIdentifier, toWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { WorkspaceService } from 'vs/workbench/services/configuration/node/configurationService'; import { IStorageService } from 'vs/platform/storage/common/storage'; @@ -21,9 +21,15 @@ import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { BackupFileService } from 'vs/workbench/services/backup/node/backupFileService'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { distinct } from 'vs/base/common/arrays'; -import { isLinux } from 'vs/base/common/platform'; -import { isEqual } from 'vs/base/common/resources'; +import { isLinux, isMacintosh } from 'vs/base/common/platform'; +import { isEqual, dirname, basename } from 'vs/base/common/resources'; +import * as json from 'vs/base/common/json'; +import * as jsonEdit from 'vs/base/common/jsonEdit'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; +import { IFileService } from 'vs/platform/files/common/files'; +import { isAbsolute, resolve } from 'path'; +import { massageFolderPathForWorkspace } from 'vs/platform/workspaces/node/workspaces'; +import { Schemas } from 'vs/base/common/network'; export class WorkspaceEditingService implements IWorkspaceEditingService { @@ -38,7 +44,9 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { @IExtensionService private readonly extensionService: IExtensionService, @IBackupFileService private readonly backupFileService: IBackupFileService, @INotificationService private readonly notificationService: INotificationService, - @ICommandService private readonly commandService: ICommandService + @ICommandService private readonly commandService: ICommandService, + @IFileService private readonly fileSystemService: IFileService, + @IWindowsService private readonly windowsService: IWindowsService, ) { } @@ -140,7 +148,7 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { return false; } - enterWorkspace(path: string): Promise { + enterWorkspace(path: URI): Promise { return this.doEnterWorkspace(() => this.windowService.enterWorkspace(path)); } @@ -148,8 +156,104 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { return this.doEnterWorkspace(() => this.windowService.createAndEnterWorkspace(folders, path)); } - saveAndEnterWorkspace(path: string): Promise { - return this.doEnterWorkspace(() => this.windowService.saveAndEnterWorkspace(path)); + async saveAndEnterWorkspace(path: URI): Promise { + if (!this.isValidTargetWorkspacePath(path)) { + return Promise.reject(null); + } + const currentWorkspaceIdentifier = toWorkspaceIdentifier(this.contextService.getWorkspace()); + if (!isWorkspaceIdentifier(currentWorkspaceIdentifier)) { + return Promise.reject(null); + } + await this.saveWorkspace(currentWorkspaceIdentifier, path); + + return this.enterWorkspace(path); + } + + + async isValidTargetWorkspacePath(path: URI): Promise { + + const windows = await this.windowsService.getWindows(); + + // Prevent overwriting a workspace that is currently opened in another window + if (windows.some(window => window.workspace && isEqual(URI.file(window.workspace.configPath), path))) { + const options: MessageBoxOptions = { + type: 'info', + buttons: [nls.localize('ok', "OK")], + message: nls.localize('workspaceOpenedMessage', "Unable to save workspace '{0}'", basename(path)), + detail: nls.localize('workspaceOpenedDetail', "The workspace is already opened in another window. Please close that window first and then try again."), + noLink: true + }; + return this.windowService.showMessageBox(options).then(() => false); + } + + return Promise.resolve(true); // OK + } + + private saveWorkspace(workspace: IWorkspaceIdentifier, targetConfigPathURI: URI): Promise { + const configPathURI = URI.file(workspace.configPath); + + // Return early if target is same as source + if (isEqual(configPathURI, targetConfigPathURI)) { + return Promise.resolve(targetConfigPathURI); + } + + // Read the contents of the workspace file and resolve it + return this.fileSystemService.resolveFile(configPathURI).then(raw => { + const rawWorkspaceContents = raw.toString(); + let storedWorkspace: IStoredWorkspace; + try { + storedWorkspace = this.doParseStoredWorkspace(configPathURI, rawWorkspaceContents); + } catch (error) { + return Promise.reject(error); + } + + const sourceConfigFolder = dirname(configPathURI); + const targetConfigFolder = dirname(targetConfigPathURI); + + // Rewrite absolute paths to relative paths if the target workspace folder + // is a parent of the location of the workspace file itself. Otherwise keep + // using absolute paths. + storedWorkspace.folders.forEach(folder => { + if (isRawFileWorkspaceFolder(folder)) { + if (sourceConfigFolder.scheme === Schemas.file) { + if (!isAbsolute(folder.path)) { + folder.path = resolve(sourceConfigFolder.path, folder.path); // relative paths get resolved against the workspace location + } + folder.path = massageFolderPathForWorkspace(folder.path, targetConfigFolder, storedWorkspace.folders); + } + } + }); + + // Preserve as much of the existing workspace as possible by using jsonEdit + // and only changing the folders portion. + let newRawWorkspaceContents = rawWorkspaceContents; + const edits = jsonEdit.setProperty(rawWorkspaceContents, ['folders'], storedWorkspace.folders, { insertSpaces: false, tabSize: 4, eol: (isLinux || isMacintosh) ? '\n' : '\r\n' }); + edits.forEach(edit => { + newRawWorkspaceContents = jsonEdit.applyEdit(rawWorkspaceContents, edit); + }); + + return this.fileSystemService.createFile(targetConfigPathURI, newRawWorkspaceContents, { overwrite: true }).then(() => { + return targetConfigPathURI; + }); + }); + } + + private doParseStoredWorkspace(path: URI, contents: string): IStoredWorkspace { + + // Parse workspace file + let storedWorkspace: IStoredWorkspace = json.parse(contents); // use fault tolerant parser + + // Filter out folders which do not have a path or uri set + if (Array.isArray(storedWorkspace.folders)) { + storedWorkspace.folders = storedWorkspace.folders.filter(folder => isStoredWorkspaceFolder(folder)); + } + + // Validate + if (!Array.isArray(storedWorkspace.folders)) { + throw new Error(`${path} looks like an invalid workspace file.`); + } + + return storedWorkspace; } private handleWorkspaceConfigurationEditingError(error: JSONEditingError): Promise { diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index 354243890f3..cef11f04692 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -1054,7 +1054,7 @@ export class TestWindowService implements IWindowService { return Promise.resolve(); } - enterWorkspace(_path: string): Promise { + enterWorkspace(_path: URI): Promise { return Promise.resolve(); } @@ -1223,7 +1223,7 @@ export class TestWindowsService implements IWindowsService { return Promise.resolve(); } - enterWorkspace(_windowId: number, _path: string): Promise { + enterWorkspace(_windowId: number, _path: URI): Promise { return Promise.resolve(); } From dfed6e1114329653e87eed62f93609ef2342bdef Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Thu, 24 Jan 2019 16:37:51 +0100 Subject: [PATCH 126/274] rename IWorkspacesService.createWorkspace to createUntitledWorkspace --- src/vs/code/electron-main/windows.ts | 4 ++-- src/vs/platform/workspaces/common/workspaces.ts | 4 ++-- .../electron-main/workspacesMainService.ts | 10 +++++----- src/vs/platform/workspaces/node/workspacesIpc.ts | 8 ++++---- .../test/electron-main/workspacesMainService.test.ts | 12 ++++++------ src/vs/workbench/browser/actions/workspaceActions.ts | 2 +- src/vs/workbench/browser/dnd.ts | 2 +- 7 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 88faa4175f6..a8db705bfdb 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -818,7 +818,7 @@ export class WindowsManager implements IWindowsMainService { if (!openConfig.addMode && isCommandLineOrAPICall) { const foldersToOpen = windowsToOpen.filter(path => !!path.folderUri); if (foldersToOpen.length > 1 && foldersToOpen.every(f => f.folderUri.scheme === Schemas.file)) { - const workspace = this.workspacesMainService.createWorkspaceSync(foldersToOpen.map(folder => ({ uri: folder.folderUri }))); + const workspace = this.workspacesMainService.createUntitledWorkspaceSync(foldersToOpen.map(folder => ({ uri: folder.folderUri }))); // Add workspace and remove folders thereby windowsToOpen.push({ workspace, remoteAuthority: foldersToOpen[0].remoteAuthority }); @@ -2000,7 +2000,7 @@ class WorkspacesManager { return null; // return early if the workspace is not valid } - return this.workspacesMainService.createWorkspace(folders).then(workspace => { + return this.workspacesMainService.createUntitledWorkspace(folders).then(workspace => { return this.doSaveAndOpenWorkspace(window, workspace, path); }); }); diff --git a/src/vs/platform/workspaces/common/workspaces.ts b/src/vs/platform/workspaces/common/workspaces.ts index 7b8a8a09b23..2ff6aaff74a 100644 --- a/src/vs/platform/workspaces/common/workspaces.ts +++ b/src/vs/platform/workspaces/common/workspaces.ts @@ -82,7 +82,7 @@ export interface IWorkspacesMainService extends IWorkspacesService { saveWorkspace(workspace: IWorkspaceIdentifier, target: string): Promise; - createWorkspaceSync(folders?: IWorkspaceFolderCreationData[]): IWorkspaceIdentifier; + createUntitledWorkspaceSync(folders?: IWorkspaceFolderCreationData[]): IWorkspaceIdentifier; resolveWorkspaceSync(path: string): IResolvedWorkspace | null; @@ -100,7 +100,7 @@ export interface IWorkspacesMainService extends IWorkspacesService { export interface IWorkspacesService { _serviceBrand: any; - createWorkspace(folders?: IWorkspaceFolderCreationData[]): Promise; + createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[]): Promise; } export function isSingleFolderWorkspaceIdentifier(obj: any): obj is ISingleFolderWorkspaceIdentifier { diff --git a/src/vs/platform/workspaces/electron-main/workspacesMainService.ts b/src/vs/platform/workspaces/electron-main/workspacesMainService.ts index c7521c466ab..fa8455f1da1 100644 --- a/src/vs/platform/workspaces/electron-main/workspacesMainService.ts +++ b/src/vs/platform/workspaces/electron-main/workspacesMainService.ts @@ -107,16 +107,16 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain return isParent(path, this.environmentService.workspacesHome, !isLinux /* ignore case */); } - createWorkspace(folders?: IWorkspaceFolderCreationData[]): Promise { - const { workspace, configParent, storedWorkspace } = this.createUntitledWorkspace(folders); + createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[]): Promise { + const { workspace, configParent, storedWorkspace } = this.newUntitledWorkspace(folders); return mkdirp(configParent).then(() => { return writeFile(workspace.configPath, JSON.stringify(storedWorkspace, null, '\t')).then(() => workspace); }); } - createWorkspaceSync(folders?: IWorkspaceFolderCreationData[]): IWorkspaceIdentifier { - const { workspace, configParent, storedWorkspace } = this.createUntitledWorkspace(folders); + createUntitledWorkspaceSync(folders?: IWorkspaceFolderCreationData[]): IWorkspaceIdentifier { + const { workspace, configParent, storedWorkspace } = this.newUntitledWorkspace(folders); if (!existsSync(this.workspacesHome)) { mkdirSync(this.workspacesHome); @@ -129,7 +129,7 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain return workspace; } - private createUntitledWorkspace(folders: IWorkspaceFolderCreationData[] = []): { workspace: IWorkspaceIdentifier, configParent: string, storedWorkspace: IStoredWorkspace } { + private newUntitledWorkspace(folders: IWorkspaceFolderCreationData[] = []): { workspace: IWorkspaceIdentifier, configParent: string, storedWorkspace: IStoredWorkspace } { const randomId = (Date.now() + Math.round(Math.random() * 1000)).toString(); const untitledWorkspaceConfigFolder = join(this.workspacesHome, randomId); const untitledWorkspaceConfigPath = join(untitledWorkspaceConfigFolder, UNTITLED_WORKSPACE_NAME); diff --git a/src/vs/platform/workspaces/node/workspacesIpc.ts b/src/vs/platform/workspaces/node/workspacesIpc.ts index bfd39ea3e0c..6b44c1dedea 100644 --- a/src/vs/platform/workspaces/node/workspacesIpc.ts +++ b/src/vs/platform/workspaces/node/workspacesIpc.ts @@ -18,7 +18,7 @@ export class WorkspacesChannel implements IServerChannel { call(_, command: string, arg?: any): Promise { switch (command) { - case 'createWorkspace': { + case 'createUntitledWorkspace': { const rawFolders: IWorkspaceFolderCreationData[] = arg; let folders: IWorkspaceFolderCreationData[] | undefined = undefined; if (Array.isArray(rawFolders)) { @@ -30,7 +30,7 @@ export class WorkspacesChannel implements IServerChannel { }); } - return this.service.createWorkspace(folders); + return this.service.createUntitledWorkspace(folders); } } @@ -44,7 +44,7 @@ export class WorkspacesChannelClient implements IWorkspacesService { constructor(private channel: IChannel) { } - createWorkspace(folders?: IWorkspaceFolderCreationData[]): Promise { - return this.channel.call('createWorkspace', folders); + createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[]): Promise { + return this.channel.call('createUntitledWorkspace', folders); } } diff --git a/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts b/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts index bd72f12615e..13bb75596aa 100644 --- a/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts +++ b/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts @@ -40,11 +40,11 @@ suite('WorkspacesMainService', () => { } function createWorkspace(folders: string[], names?: string[]) { - return service.createWorkspace(folders.map((folder, index) => ({ uri: URI.file(folder), name: names ? names[index] : undefined } as IWorkspaceFolderCreationData))); + return service.createUntitledWorkspace(folders.map((folder, index) => ({ uri: URI.file(folder), name: names ? names[index] : undefined } as IWorkspaceFolderCreationData))); } function createWorkspaceSync(folders: string[], names?: string[]) { - return service.createWorkspaceSync(folders.map((folder, index) => ({ uri: URI.file(folder), name: names ? names[index] : undefined } as IWorkspaceFolderCreationData))); + return service.createUntitledWorkspaceSync(folders.map((folder, index) => ({ uri: URI.file(folder), name: names ? names[index] : undefined } as IWorkspaceFolderCreationData))); } const environmentService = new TestEnvironmentService(parseArgs(process.argv), process.execPath); @@ -106,8 +106,8 @@ suite('WorkspacesMainService', () => { }); }); - test('createWorkspace (folders as other resource URIs)', () => { - return service.createWorkspace([{ uri: URI.from({ scheme: 'myScheme', path: process.cwd() }) }, { uri: URI.from({ scheme: 'myScheme', path: os.tmpdir() }) }]).then(workspace => { + test('createUntitledWorkspace (folders as other resource URIs)', () => { + return service.createUntitledWorkspace([{ uri: URI.from({ scheme: 'myScheme', path: process.cwd() }) }, { uri: URI.from({ scheme: 'myScheme', path: os.tmpdir() }) }]).then(workspace => { assert.ok(workspace); assert.ok(fs.existsSync(workspace.configPath)); assert.ok(service.isUntitledWorkspace(workspace)); @@ -152,8 +152,8 @@ suite('WorkspacesMainService', () => { assert.equal((ws.folders[1]).name, 'tempdir'); }); - test('createWorkspaceSync (folders as other resource URIs)', () => { - const workspace = service.createWorkspaceSync([{ uri: URI.from({ scheme: 'myScheme', path: process.cwd() }) }, { uri: URI.from({ scheme: 'myScheme', path: os.tmpdir() }) }]); + test('createUntitledWorkspaceSync (folders as other resource URIs)', () => { + const workspace = service.createUntitledWorkspaceSync([{ uri: URI.from({ scheme: 'myScheme', path: process.cwd() }) }, { uri: URI.from({ scheme: 'myScheme', path: os.tmpdir() }) }]); assert.ok(workspace); assert.ok(fs.existsSync(workspace.configPath)); assert.ok(service.isUntitledWorkspace(workspace)); diff --git a/src/vs/workbench/browser/actions/workspaceActions.ts b/src/vs/workbench/browser/actions/workspaceActions.ts index 7da39edb68c..e008e2e7809 100644 --- a/src/vs/workbench/browser/actions/workspaceActions.ts +++ b/src/vs/workbench/browser/actions/workspaceActions.ts @@ -255,7 +255,7 @@ export class DuplicateWorkspaceInNewWindowAction extends Action { run(): Promise { const folders = this.workspaceContextService.getWorkspace().folders; - return this.workspacesService.createWorkspace(folders).then(newWorkspace => { + return this.workspacesService.createUntitledWorkspace(folders).then(newWorkspace => { return this.workspaceEditingService.copyWorkspaceSettings(newWorkspace).then(() => { return this.windowService.openWindow([URI.file(newWorkspace.configPath)], { forceNewWindow: true }); }); diff --git a/src/vs/workbench/browser/dnd.ts b/src/vs/workbench/browser/dnd.ts index bde4319b2d3..e73acff052b 100644 --- a/src/vs/workbench/browser/dnd.ts +++ b/src/vs/workbench/browser/dnd.ts @@ -295,7 +295,7 @@ export class ResourcesDropHandler { // Multiple folders: Create new workspace with folders and open else if (folders.length > 1) { - workspacesToOpen = this.workspacesService.createWorkspace(folders.map(folder => ({ uri: folder }))).then(workspace => [URI.file(workspace.configPath)]); + workspacesToOpen = this.workspacesService.createUntitledWorkspace(folders.map(folder => ({ uri: folder }))).then(workspace => [URI.file(workspace.configPath)]); } // Open From 78e185d736059a188df315dfa7bd4e70738a00df Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Thu, 24 Jan 2019 17:08:35 +0100 Subject: [PATCH 127/274] fix createAndEnterWorkspace --- .../browser/actions/workspaceActions.ts | 3 +-- .../workspace/common/workspaceEditing.ts | 2 +- .../workspace/node/workspaceEditingService.ts | 25 +++++++++++++------ 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/browser/actions/workspaceActions.ts b/src/vs/workbench/browser/actions/workspaceActions.ts index e008e2e7809..9cfdc373556 100644 --- a/src/vs/workbench/browser/actions/workspaceActions.ts +++ b/src/vs/workbench/browser/actions/workspaceActions.ts @@ -143,12 +143,11 @@ export class SaveWorkspaceAsAction extends Action { run(): Promise { return this.getNewWorkspaceConfigPath().then((configPathUri): Promise | void => { if (configPathUri) { - const configPath = configPathUri.fsPath; switch (this.contextService.getWorkbenchState()) { case WorkbenchState.EMPTY: case WorkbenchState.FOLDER: const folders = this.contextService.getWorkspace().folders.map(folder => ({ uri: folder.uri })); - return this.workspaceEditingService.createAndEnterWorkspace(folders, configPath); + return this.workspaceEditingService.createAndEnterWorkspace(folders, configPathUri); case WorkbenchState.WORKSPACE: return this.workspaceEditingService.saveAndEnterWorkspace(configPathUri); diff --git a/src/vs/workbench/services/workspace/common/workspaceEditing.ts b/src/vs/workbench/services/workspace/common/workspaceEditing.ts index 8bd26a6efc3..abcb3fff36f 100644 --- a/src/vs/workbench/services/workspace/common/workspaceEditing.ts +++ b/src/vs/workbench/services/workspace/common/workspaceEditing.ts @@ -40,7 +40,7 @@ export interface IWorkspaceEditingService { * creates a new workspace with the provided folders and opens it. if path is provided * the workspace will be saved into that location. */ - createAndEnterWorkspace(folders?: IWorkspaceFolderCreationData[], path?: string): Promise; + createAndEnterWorkspace(folders: IWorkspaceFolderCreationData[], path?: URI): Promise; /** * saves the workspace to the provided path and opens it. requires a workspace to be opened. diff --git a/src/vs/workbench/services/workspace/node/workspaceEditingService.ts b/src/vs/workbench/services/workspace/node/workspaceEditingService.ts index 2e9e7d8791d..91a343ebaa6 100644 --- a/src/vs/workbench/services/workspace/node/workspaceEditingService.ts +++ b/src/vs/workbench/services/workspace/node/workspaceEditingService.ts @@ -9,7 +9,7 @@ import * as nls from 'vs/nls'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IWindowService, IEnterWorkspaceResult, MessageBoxOptions, IWindowsService } from 'vs/platform/windows/common/windows'; import { IJSONEditingService, JSONEditingError, JSONEditingErrorCode } from 'vs/workbench/services/configuration/common/jsonEditing'; -import { IWorkspaceIdentifier, IWorkspaceFolderCreationData, IStoredWorkspace, isStoredWorkspaceFolder, isRawFileWorkspaceFolder, isWorkspaceIdentifier, toWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspaceIdentifier, IWorkspaceFolderCreationData, IStoredWorkspace, isStoredWorkspaceFolder, isRawFileWorkspaceFolder, isWorkspaceIdentifier, toWorkspaceIdentifier, IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { WorkspaceService } from 'vs/workbench/services/configuration/node/configurationService'; import { IStorageService } from 'vs/platform/storage/common/storage'; @@ -47,6 +47,7 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { @ICommandService private readonly commandService: ICommandService, @IFileService private readonly fileSystemService: IFileService, @IWindowsService private readonly windowsService: IWindowsService, + @IWorkspacesService private readonly workspaceService: IWorkspacesService ) { } @@ -152,8 +153,18 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { return this.doEnterWorkspace(() => this.windowService.enterWorkspace(path)); } - createAndEnterWorkspace(folders?: IWorkspaceFolderCreationData[], path?: string): Promise { - return this.doEnterWorkspace(() => this.windowService.createAndEnterWorkspace(folders, path)); + async createAndEnterWorkspace(folders: IWorkspaceFolderCreationData[], path?: URI): Promise { + if (path && !this.isValidTargetWorkspacePath(path)) { + return Promise.reject(null); + } + + const untitledWorkspace = await this.workspaceService.createUntitledWorkspace(folders); + if (path) { + await this.saveWorkspace(untitledWorkspace, path); + } else { + path = URI.file(untitledWorkspace.configPath); + } + return this.enterWorkspace(path); } async saveAndEnterWorkspace(path: URI): Promise { @@ -189,12 +200,12 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { return Promise.resolve(true); // OK } - private saveWorkspace(workspace: IWorkspaceIdentifier, targetConfigPathURI: URI): Promise { + private saveWorkspace(workspace: IWorkspaceIdentifier, targetConfigPathURI: URI): Promise { const configPathURI = URI.file(workspace.configPath); // Return early if target is same as source if (isEqual(configPathURI, targetConfigPathURI)) { - return Promise.resolve(targetConfigPathURI); + return Promise.resolve(null); } // Read the contents of the workspace file and resolve it @@ -232,9 +243,7 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { newRawWorkspaceContents = jsonEdit.applyEdit(rawWorkspaceContents, edit); }); - return this.fileSystemService.createFile(targetConfigPathURI, newRawWorkspaceContents, { overwrite: true }).then(() => { - return targetConfigPathURI; - }); + return this.fileSystemService.createFile(targetConfigPathURI, newRawWorkspaceContents, { overwrite: true }); }); } From 1225a12bdb433892eac3f00189575524aff40ab4 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 25 Jan 2019 09:15:31 +0100 Subject: [PATCH 128/274] remove createAndEnterWorkspace & saveAndEnterWorkspace from IWindowsService --- src/vs/code/electron-main/windows.ts | 45 +------------------ src/vs/platform/windows/common/windows.ts | 6 +-- .../windows/electron-browser/windowService.ts | 9 ---- .../platform/windows/electron-main/windows.ts | 4 +- .../windows/electron-main/windowsService.ts | 14 +----- src/vs/platform/windows/node/windowsIpc.ts | 25 +---------- .../workspace/node/workspaceEditingService.ts | 10 ++--- .../workbench/test/workbenchTestServices.ts | 18 +------- 8 files changed, 9 insertions(+), 122 deletions(-) diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index a8db705bfdb..e01be8d28fd 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -26,7 +26,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IWindowsMainService, IOpenConfiguration, IWindowsCountChangedEvent, ICodeWindow, IWindowState as ISingleWindowState, WindowMode } from 'vs/platform/windows/electron-main/windows'; import { IHistoryMainService } from 'vs/platform/history/common/history'; import { IProcessEnvironment, isLinux, isMacintosh, isWindows } from 'vs/base/common/platform'; -import { IWorkspacesMainService, IWorkspaceIdentifier, WORKSPACE_FILTER, IWorkspaceFolderCreationData, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspacesMainService, IWorkspaceIdentifier, WORKSPACE_FILTER, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { mnemonicButtonLabel } from 'vs/base/common/labels'; import { Schemas } from 'vs/base/common/network'; @@ -1470,18 +1470,10 @@ export class WindowsManager implements IWindowsMainService { }); } - saveAndEnterWorkspace(win: ICodeWindow, path: string): Promise { - return this.workspacesManager.saveAndEnterWorkspace(win, path).then(result => this.doEnterWorkspace(win, result)); - } - enterWorkspace(win: ICodeWindow, path: URI): Promise { return this.workspacesManager.enterWorkspace(win, path).then(result => this.doEnterWorkspace(win, result)); } - createAndEnterWorkspace(win: ICodeWindow, folders?: IWorkspaceFolderCreationData[], path?: string): Promise { - return this.workspacesManager.createAndEnterWorkspace(win, folders, path).then(result => this.doEnterWorkspace(win, result)); - } - private doEnterWorkspace(win: ICodeWindow, result: IEnterWorkspaceResult): IEnterWorkspaceResult { // Mark as recently opened @@ -1967,14 +1959,6 @@ class WorkspacesManager { ) { } - saveAndEnterWorkspace(window: ICodeWindow, path: string): Promise { - if (!window || !window.win || !window.isReady || !window.openedWorkspace || !path || !this.isValidTargetWorkspacePath(window, URI.file(path))) { - return Promise.resolve(null); // return early if the window is not ready or disposed or does not have a workspace - } - - return this.doSaveAndOpenWorkspace(window, window.openedWorkspace, path); - } - enterWorkspace(window: ICodeWindow, path: URI): Promise { if (!window || !window.win || !window.isReady) { return Promise.resolve(null); // return early if the window is not ready or disposed @@ -1990,22 +1974,6 @@ class WorkspacesManager { } - createAndEnterWorkspace(window: ICodeWindow, folders?: IWorkspaceFolderCreationData[], path?: string): Promise { - if (!window || !window.win || !window.isReady || !path) { - return Promise.resolve(null); // return early if the window is not ready or disposed - } - - return this.isValidTargetWorkspacePath(window, URI.file(path)).then(isValid => { - if (!isValid) { - return null; // return early if the workspace is not valid - } - - return this.workspacesMainService.createUntitledWorkspace(folders).then(workspace => { - return this.doSaveAndOpenWorkspace(window, workspace, path); - }); - }); - } - private isValidTargetWorkspacePath(window: ICodeWindow, path?: URI): Promise { if (!path) { return Promise.resolve(true); @@ -2032,17 +2000,6 @@ class WorkspacesManager { return Promise.resolve(true); // OK } - private doSaveAndOpenWorkspace(window: ICodeWindow, workspace: IWorkspaceIdentifier, path?: string): Promise { - let savePromise: Promise; - if (path) { - savePromise = this.workspacesMainService.saveWorkspace(workspace, path); - } else { - savePromise = Promise.resolve(workspace); - } - - return savePromise.then(workspace => this.doOpenWorkspace(window, workspace)); - } - private doOpenWorkspace(window: ICodeWindow, workspace: IWorkspaceIdentifier): IEnterWorkspaceResult { window.focus(); diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index 78292433ce5..649ef74160c 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -8,7 +8,7 @@ import { Event } from 'vs/base/common/event'; import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry'; import { IProcessEnvironment, isMacintosh, isWindows } from 'vs/base/common/platform'; import { ParsedArgs, IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { IWorkspaceIdentifier, IWorkspaceFolderCreationData, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { IRecentlyOpened } from 'vs/platform/history/common/history'; import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; import { ExportData } from 'vs/base/common/performance'; @@ -115,8 +115,6 @@ export interface IWindowsService { toggleDevTools(windowId: number): Promise; closeWorkspace(windowId: number): Promise; enterWorkspace(windowId: number, path: URI): Promise; - createAndEnterWorkspace(windowId: number, folders?: IWorkspaceFolderCreationData[], path?: string): Promise; - saveAndEnterWorkspace(windowId: number, path: string): Promise; toggleFullScreen(windowId: number): Promise; setRepresentedFilename(windowId: number, fileName: string): Promise; addRecentlyOpened(files: URI[]): Promise; @@ -208,8 +206,6 @@ export interface IWindowService { closeWorkspace(): Promise; updateTouchBar(items: ISerializableCommandAction[][]): Promise; enterWorkspace(path: URI): Promise; - createAndEnterWorkspace(folders?: IWorkspaceFolderCreationData[], path?: string): Promise; - saveAndEnterWorkspace(path: string): Promise; toggleFullScreen(): Promise; setRepresentedFilename(fileName: string): Promise; getRecentlyOpened(): Promise; diff --git a/src/vs/platform/windows/electron-browser/windowService.ts b/src/vs/platform/windows/electron-browser/windowService.ts index 040f396f8d9..9c9cc78d419 100644 --- a/src/vs/platform/windows/electron-browser/windowService.ts +++ b/src/vs/platform/windows/electron-browser/windowService.ts @@ -7,7 +7,6 @@ import { Event } from 'vs/base/common/event'; import { IWindowService, IWindowsService, INativeOpenDialogOptions, IEnterWorkspaceResult, IMessageBoxResult, IWindowConfiguration, IDevToolsOptions, IOpenSettings } from 'vs/platform/windows/common/windows'; import { IRecentlyOpened } from 'vs/platform/history/common/history'; import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; -import { IWorkspaceFolderCreationData } from 'vs/platform/workspaces/common/workspaces'; import { ParsedArgs } from 'vs/platform/environment/common/environment'; import { URI } from 'vs/base/common/uri'; import { Disposable } from 'vs/base/common/lifecycle'; @@ -93,14 +92,6 @@ export class WindowService extends Disposable implements IWindowService { return this.windowsService.enterWorkspace(this.windowId, path); } - createAndEnterWorkspace(folders?: IWorkspaceFolderCreationData[], path?: string): Promise { - return this.windowsService.createAndEnterWorkspace(this.windowId, folders, path); - } - - saveAndEnterWorkspace(path: string): Promise { - return this.windowsService.saveAndEnterWorkspace(this.windowId, path); - } - openWindow(paths: URI[], options?: IOpenSettings): Promise { return this.windowsService.openWindow(this.windowId, paths, options); } diff --git a/src/vs/platform/windows/electron-main/windows.ts b/src/vs/platform/windows/electron-main/windows.ts index b8756fd9130..e067b62e96d 100644 --- a/src/vs/platform/windows/electron-main/windows.ts +++ b/src/vs/platform/windows/electron-main/windows.ts @@ -8,7 +8,7 @@ import { ParsedArgs } from 'vs/platform/environment/common/environment'; import { Event } from 'vs/base/common/event'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IProcessEnvironment } from 'vs/base/common/platform'; -import { IWorkspaceIdentifier, IWorkspaceFolderCreationData } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; import { URI } from 'vs/base/common/uri'; @@ -95,8 +95,6 @@ export interface IWindowsMainService { ready(initialUserEnv: IProcessEnvironment): void; reload(win: ICodeWindow, cli?: ParsedArgs): void; enterWorkspace(win: ICodeWindow, path: URI): Promise; - createAndEnterWorkspace(win: ICodeWindow, folders?: IWorkspaceFolderCreationData[], path?: string): Promise; - saveAndEnterWorkspace(win: ICodeWindow, path: string): Promise; closeWorkspace(win: ICodeWindow): void; open(openConfig: IOpenConfiguration): ICodeWindow[]; openExtensionDevelopmentHostWindow(openConfig: IOpenConfiguration): void; diff --git a/src/vs/platform/windows/electron-main/windowsService.ts b/src/vs/platform/windows/electron-main/windowsService.ts index 0f116f6bc9c..940cbe2d1cd 100644 --- a/src/vs/platform/windows/electron-main/windowsService.ts +++ b/src/vs/platform/windows/electron-main/windowsService.ts @@ -17,7 +17,7 @@ import { IURLService, IURLHandler } from 'vs/platform/url/common/url'; import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain'; import { IWindowsMainService, ISharedProcess, ICodeWindow } from 'vs/platform/windows/electron-main/windows'; import { IHistoryMainService, IRecentlyOpened } from 'vs/platform/history/common/history'; -import { IWorkspaceIdentifier, IWorkspaceFolderCreationData, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; import { Schemas } from 'vs/base/common/network'; import { mnemonicButtonLabel } from 'vs/base/common/labels'; @@ -144,18 +144,6 @@ export class WindowsService implements IWindowsService, IURLHandler, IDisposable return this.withWindow(windowId, codeWindow => this.windowsMainService.enterWorkspace(codeWindow, path)); } - async createAndEnterWorkspace(windowId: number, folders?: IWorkspaceFolderCreationData[], path?: string): Promise { - this.logService.trace('windowsService#createAndEnterWorkspace', windowId); - - return this.withWindow(windowId, codeWindow => this.windowsMainService.createAndEnterWorkspace(codeWindow, folders, path)); - } - - async saveAndEnterWorkspace(windowId: number, path: string): Promise { - this.logService.trace('windowsService#saveAndEnterWorkspace', windowId); - - return this.withWindow(windowId, codeWindow => this.windowsMainService.saveAndEnterWorkspace(codeWindow, path)); - } - async toggleFullScreen(windowId: number): Promise { this.logService.trace('windowsService#toggleFullScreen', windowId); diff --git a/src/vs/platform/windows/node/windowsIpc.ts b/src/vs/platform/windows/node/windowsIpc.ts index ddd42188cd6..dc10702ca5b 100644 --- a/src/vs/platform/windows/node/windowsIpc.ts +++ b/src/vs/platform/windows/node/windowsIpc.ts @@ -6,7 +6,7 @@ import { Event } from 'vs/base/common/event'; import { IChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc'; import { IWindowsService, INativeOpenDialogOptions, IEnterWorkspaceResult, CrashReporterStartOptions, IMessageBoxResult, MessageBoxOptions, SaveDialogOptions, OpenDialogOptions, IDevToolsOptions, INewWindowOptions } from 'vs/platform/windows/common/windows'; -import { IWorkspaceIdentifier, IWorkspaceFolderCreationData, ISingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { IRecentlyOpened } from 'vs/platform/history/common/history'; import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; import { URI } from 'vs/base/common/uri'; @@ -57,21 +57,6 @@ export class WindowsChannel implements IServerChannel { case 'toggleDevTools': return this.service.toggleDevTools(arg); case 'closeWorkspace': return this.service.closeWorkspace(arg); case 'enterWorkspace': return this.service.enterWorkspace(arg[0], URI.revive(arg[1])); - case 'createAndEnterWorkspace': { - const rawFolders: IWorkspaceFolderCreationData[] = arg[1]; - let folders: IWorkspaceFolderCreationData[] | undefined = undefined; - if (Array.isArray(rawFolders)) { - folders = rawFolders.map(rawFolder => { - return { - uri: URI.revive(rawFolder.uri), // convert raw URI back to real URI - name: rawFolder.name - } as IWorkspaceFolderCreationData; - }); - } - - return this.service.createAndEnterWorkspace(arg[0], folders, arg[2]); - } - case 'saveAndEnterWorkspace': return this.service.saveAndEnterWorkspace(arg[0], arg[1]); case 'toggleFullScreen': return this.service.toggleFullScreen(arg); case 'setRepresentedFilename': return this.service.setRepresentedFilename(arg[0], arg[1]); case 'addRecentlyOpened': return this.service.addRecentlyOpened(arg.map(URI.revive)); @@ -183,14 +168,6 @@ export class WindowsChannelClient implements IWindowsService { return this.channel.call('enterWorkspace', [windowId, path]); } - createAndEnterWorkspace(windowId: number, folders?: IWorkspaceFolderCreationData[], path?: string): Promise { - return this.channel.call('createAndEnterWorkspace', [windowId, folders, path]); - } - - saveAndEnterWorkspace(windowId: number, path: string): Promise { - return this.channel.call('saveAndEnterWorkspace', [windowId, path]); - } - toggleFullScreen(windowId: number): Promise { return this.channel.call('toggleFullScreen', windowId); } diff --git a/src/vs/workbench/services/workspace/node/workspaceEditingService.ts b/src/vs/workbench/services/workspace/node/workspaceEditingService.ts index 91a343ebaa6..2427501df0f 100644 --- a/src/vs/workbench/services/workspace/node/workspaceEditingService.ts +++ b/src/vs/workbench/services/workspace/node/workspaceEditingService.ts @@ -7,7 +7,7 @@ import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common import { URI } from 'vs/base/common/uri'; import * as nls from 'vs/nls'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; -import { IWindowService, IEnterWorkspaceResult, MessageBoxOptions, IWindowsService } from 'vs/platform/windows/common/windows'; +import { IWindowService, MessageBoxOptions, IWindowsService } from 'vs/platform/windows/common/windows'; import { IJSONEditingService, JSONEditingError, JSONEditingErrorCode } from 'vs/workbench/services/configuration/common/jsonEditing'; import { IWorkspaceIdentifier, IWorkspaceFolderCreationData, IStoredWorkspace, isStoredWorkspaceFolder, isRawFileWorkspaceFolder, isWorkspaceIdentifier, toWorkspaceIdentifier, IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; @@ -149,10 +149,6 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { return false; } - enterWorkspace(path: URI): Promise { - return this.doEnterWorkspace(() => this.windowService.enterWorkspace(path)); - } - async createAndEnterWorkspace(folders: IWorkspaceFolderCreationData[], path?: URI): Promise { if (path && !this.isValidTargetWorkspacePath(path)) { return Promise.reject(null); @@ -298,7 +294,7 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { ); } - private doEnterWorkspace(mainSidePromise: () => Promise): Promise { + enterWorkspace(path: URI): Promise { // Stop the extension host first to give extensions most time to shutdown this.extensionService.stopExtensionHost(); @@ -309,7 +305,7 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { extensionHostStarted = true; }; - return mainSidePromise().then(result => { + return this.windowService.enterWorkspace(path).then(result => { // Migrate storage and settings if we are to enter a workspace if (result) { diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index cef11f04692..010050b0397 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -44,7 +44,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' import { IThemeService } from 'vs/platform/theme/common/themeService'; import { generateUuid } from 'vs/base/common/uuid'; import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; -import { IWorkspaceIdentifier, IWorkspaceFolderCreationData, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { IRecentlyOpened } from 'vs/platform/history/common/history'; import { ITextResourceConfigurationService, ITextResourcePropertiesService } from 'vs/editor/common/services/resourceConfiguration'; import { IPosition, Position as EditorPosition } from 'vs/editor/common/core/position'; @@ -1058,14 +1058,6 @@ export class TestWindowService implements IWindowService { return Promise.resolve(); } - createAndEnterWorkspace(_folders?: IWorkspaceFolderCreationData[], _path?: string): Promise { - return Promise.resolve(); - } - - saveAndEnterWorkspace(_path: string): Promise { - return Promise.resolve(); - } - toggleFullScreen(): Promise { return Promise.resolve(); } @@ -1227,14 +1219,6 @@ export class TestWindowsService implements IWindowsService { return Promise.resolve(); } - createAndEnterWorkspace(_windowId: number, _folders?: IWorkspaceFolderCreationData[], _path?: string): Promise { - return Promise.resolve(); - } - - saveAndEnterWorkspace(_windowId: number, _path: string): Promise { - return Promise.resolve(); - } - toggleFullScreen(_windowId: number): Promise { return Promise.resolve(); } From 9226fffe05cde8c1019effbfea4fe804f93cd30b Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 25 Jan 2019 11:35:29 +0100 Subject: [PATCH 129/274] share rewriteWorkspaceFile code for main and renderer --- src/vs/platform/workspaces/node/workspaces.ts | 62 +++++++++++++++++-- 1 file changed, 58 insertions(+), 4 deletions(-) diff --git a/src/vs/platform/workspaces/node/workspaces.ts b/src/vs/platform/workspaces/node/workspaces.ts index d34df96d54d..aaa3cdcac36 100644 --- a/src/vs/platform/workspaces/node/workspaces.ts +++ b/src/vs/platform/workspaces/node/workspaces.ts @@ -3,14 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IStoredWorkspaceFolder, isRawFileWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces'; -import { isWindows, isLinux } from 'vs/base/common/platform'; -import { isAbsolute, relative, posix } from 'path'; +import { IStoredWorkspaceFolder, isRawFileWorkspaceFolder, IStoredWorkspace, isStoredWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces'; +import { isWindows, isLinux, isMacintosh } from 'vs/base/common/platform'; +import { isAbsolute, relative, posix, resolve } from 'path'; import { normalize, isEqualOrParent } from 'vs/base/common/paths'; import { normalizeDriveLetter } from 'vs/base/common/labels'; import { URI } from 'vs/base/common/uri'; -import { fsPath } from 'vs/base/common/resources'; +import { fsPath, dirname } from 'vs/base/common/resources'; import { Schemas } from 'vs/base/common/network'; +import * as jsonEdit from 'vs/base/common/jsonEdit'; +import * as json from 'vs/base/common/json'; const SLASH = '/'; @@ -55,6 +57,58 @@ export function massageFolderPathForWorkspace(absoluteFolderPath: string, target return absoluteFolderPath; } +/** + * Rewrites the content of a workspace file to be saved at a new location. + * Throws an exception if file is not a valid workspace file + */ +export function rewriteWorkspaceFileForNewLocation(rawWorkspaceContents: string, configPathURI: URI, targetConfigPathURI: URI) { + let storedWorkspace = doParseStoredWorkspace(configPathURI, rawWorkspaceContents); + + const sourceConfigFolder = dirname(configPathURI); + const targetConfigFolder = dirname(targetConfigPathURI); + + // Rewrite absolute paths to relative paths if the target workspace folder + // is a parent of the location of the workspace file itself. Otherwise keep + // using absolute paths. + for (const folder of storedWorkspace.folders) { + if (isRawFileWorkspaceFolder(folder)) { + if (sourceConfigFolder.scheme === Schemas.file) { + if (!isAbsolute(folder.path)) { + folder.path = resolve(sourceConfigFolder.path, folder.path); // relative paths get resolved against the workspace location + } + folder.path = massageFolderPathForWorkspace(folder.path, targetConfigFolder, storedWorkspace.folders); + } + } + } + + // Preserve as much of the existing workspace as possible by using jsonEdit + // and only changing the folders portion. + let newRawWorkspaceContents = rawWorkspaceContents; + const edits = jsonEdit.setProperty(rawWorkspaceContents, ['folders'], storedWorkspace.folders, { insertSpaces: false, tabSize: 4, eol: (isLinux || isMacintosh) ? '\n' : '\r\n' }); + edits.forEach(edit => { + newRawWorkspaceContents = jsonEdit.applyEdit(rawWorkspaceContents, edit); + }); + return newRawWorkspaceContents; +} + +function doParseStoredWorkspace(path: URI, contents: string): IStoredWorkspace { + + // Parse workspace file + let storedWorkspace: IStoredWorkspace = json.parse(contents); // use fault tolerant parser + + // Filter out folders which do not have a path or uri set + if (Array.isArray(storedWorkspace.folders)) { + storedWorkspace.folders = storedWorkspace.folders.filter(folder => isStoredWorkspaceFolder(folder)); + } + + // Validate + if (!Array.isArray(storedWorkspace.folders)) { + throw new Error(`${path} looks like an invalid workspace file.`); + } + + return storedWorkspace; +} + function shouldUseSlashForPath(storedFolders: IStoredWorkspaceFolder[]): boolean { // Determine which path separator to use: From 2331b990f2bcfd82ce4533c76c899aa9a1361709 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 25 Jan 2019 11:36:42 +0100 Subject: [PATCH 130/274] after save workspace: remove untitled WS and add to recent --- src/vs/code/electron-main/windows.ts | 11 ++- .../electron-main/historyMainService.ts | 14 +--- .../platform/workspaces/common/workspaces.ts | 3 +- .../electron-main/workspacesMainService.ts | 59 +++----------- .../platform/workspaces/node/workspacesIpc.ts | 9 +++ .../workspacesMainService.test.ts | 20 +---- .../workspace/common/workspaceEditing.ts | 2 +- .../workspace/node/workspaceEditingService.ts | 79 +++---------------- 8 files changed, 46 insertions(+), 151 deletions(-) diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index e01be8d28fd..42656aa18b3 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -167,7 +167,7 @@ export class WindowsManager implements IWindowsMainService { } this.dialogs = new Dialogs(environmentService, telemetryService, stateService, this); - this.workspacesManager = new WorkspacesManager(workspacesMainService, backupMainService, environmentService, this); + this.workspacesManager = new WorkspacesManager(workspacesMainService, backupMainService, environmentService, historyMainService, this); } private getWindowsState(): IWindowsState { @@ -1955,7 +1955,8 @@ class WorkspacesManager { private workspacesMainService: IWorkspacesMainService, private backupMainService: IBackupMainService, private environmentService: IEnvironmentService, - private windowsMainService: IWindowsMainService + private historyMainService: IHistoryMainService, + private windowsMainService: IWindowsMainService, ) { } @@ -2090,7 +2091,11 @@ class WorkspacesManager { defaultPath: this.getUntitledWorkspaceSaveDialogDefaultPath(workspace) }, window).then(target => { if (target) { - return this.workspacesMainService.saveWorkspace(workspace, target).then(() => false, () => false); + return this.workspacesMainService.saveWorkspace(workspace, target).then(savedWorkspace => { + this.historyMainService.addRecentlyOpened([savedWorkspace], []); + return false; + }, + () => false); } return true; // keep veto if no target was provided diff --git a/src/vs/platform/history/electron-main/historyMainService.ts b/src/vs/platform/history/electron-main/historyMainService.ts index 85ecbea3aed..1b0d20dc036 100644 --- a/src/vs/platform/history/electron-main/historyMainService.ts +++ b/src/vs/platform/history/electron-main/historyMainService.ts @@ -12,7 +12,7 @@ import { getBaseLabel, getPathLabel } from 'vs/base/common/labels'; import { IPath } from 'vs/platform/windows/common/windows'; import { Event as CommonEvent, Emitter } from 'vs/base/common/event'; import { isWindows, isMacintosh, isLinux } from 'vs/base/common/platform'; -import { IWorkspaceIdentifier, IWorkspacesMainService, IWorkspaceSavedEvent, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspaceIdentifier, IWorkspacesMainService, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { IHistoryMainService, IRecentlyOpened } from 'vs/platform/history/common/history'; import { isEqual } from 'vs/base/common/paths'; import { RunOnceScheduler } from 'vs/base/common/async'; @@ -54,18 +54,6 @@ export class HistoryMainService implements IHistoryMainService { @IEnvironmentService private readonly environmentService: IEnvironmentService ) { this.macOSRecentDocumentsUpdater = new RunOnceScheduler(() => this.updateMacOSRecentDocuments(), 800); - - this.registerListeners(); - } - - private registerListeners(): void { - this.workspacesMainService.onWorkspaceSaved(e => this.onWorkspaceSaved(e)); - } - - private onWorkspaceSaved(e: IWorkspaceSavedEvent): void { - - // Make sure to add newly saved workspaces to the list of recent workspaces - this.addRecentlyOpened([e.workspace], []); } addRecentlyOpened(workspaces: Array, files: URI[]): void { diff --git a/src/vs/platform/workspaces/common/workspaces.ts b/src/vs/platform/workspaces/common/workspaces.ts index 2ff6aaff74a..0d1c0d017c6 100644 --- a/src/vs/platform/workspaces/common/workspaces.ts +++ b/src/vs/platform/workspaces/common/workspaces.ts @@ -77,7 +77,6 @@ export interface IWorkspaceFolderCreationData { export interface IWorkspacesMainService extends IWorkspacesService { _serviceBrand: any; - onWorkspaceSaved: Event; onUntitledWorkspaceDeleted: Event; saveWorkspace(workspace: IWorkspaceIdentifier, target: string): Promise; @@ -101,6 +100,8 @@ export interface IWorkspacesService { _serviceBrand: any; createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[]): Promise; + + deleteIfUntitledWorkspace(workspace: IWorkspaceIdentifier): Promise; } export function isSingleFolderWorkspaceIdentifier(obj: any): obj is ISingleFolderWorkspaceIdentifier { diff --git a/src/vs/platform/workspaces/electron-main/workspacesMainService.ts b/src/vs/platform/workspaces/electron-main/workspacesMainService.ts index fa8455f1da1..9435ca0a714 100644 --- a/src/vs/platform/workspaces/electron-main/workspacesMainService.ts +++ b/src/vs/platform/workspaces/electron-main/workspacesMainService.ts @@ -3,13 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IWorkspacesMainService, IWorkspaceIdentifier, WORKSPACE_EXTENSION, IWorkspaceSavedEvent, UNTITLED_WORKSPACE_NAME, IResolvedWorkspace, IStoredWorkspaceFolder, isRawFileWorkspaceFolder, isStoredWorkspaceFolder, IWorkspaceFolderCreationData } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspacesMainService, IWorkspaceIdentifier, WORKSPACE_EXTENSION, UNTITLED_WORKSPACE_NAME, IResolvedWorkspace, IStoredWorkspaceFolder, isStoredWorkspaceFolder, IWorkspaceFolderCreationData } from 'vs/platform/workspaces/common/workspaces'; import { isParent } from 'vs/platform/files/common/files'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { join, dirname, isAbsolute, resolve, extname } from 'path'; +import { join, dirname, extname } from 'path'; import { mkdirp, writeFile, readFile } from 'vs/base/node/pfs'; import { readFileSync, existsSync, mkdirSync, writeFileSync } from 'fs'; -import { isLinux, isMacintosh } from 'vs/base/common/platform'; +import { isLinux } from 'vs/base/common/platform'; import { delSync, readdirSync, writeFileAndFlushSync } from 'vs/base/node/extfs'; import { Event, Emitter } from 'vs/base/common/event'; import { ILogService } from 'vs/platform/log/common/log'; @@ -17,8 +17,7 @@ import { isEqual } from 'vs/base/common/paths'; import { coalesce } from 'vs/base/common/arrays'; import { createHash } from 'crypto'; import * as json from 'vs/base/common/json'; -import * as jsonEdit from 'vs/base/common/jsonEdit'; -import { massageFolderPathForWorkspace } from 'vs/platform/workspaces/node/workspaces'; +import { massageFolderPathForWorkspace, rewriteWorkspaceFileForNewLocation } from 'vs/platform/workspaces/node/workspaces'; import { toWorkspaceFolders } from 'vs/platform/workspace/common/workspace'; import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; @@ -35,9 +34,6 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain private workspacesHome: string; - private readonly _onWorkspaceSaved = this._register(new Emitter()); - get onWorkspaceSaved(): Event { return this._onWorkspaceSaved.event; } - private readonly _onUntitledWorkspaceDeleted = this._register(new Emitter()); get onUntitledWorkspaceDeleted(): Event { return this._onUntitledWorkspaceDeleted.event; } @@ -203,48 +199,12 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain // Read the contents of the workspace file and resolve it return readFile(workspace.configPath).then(raw => { - const rawWorkspaceContents = raw.toString(); - let storedWorkspace: IStoredWorkspace; - try { - storedWorkspace = this.doParseStoredWorkspace(URI.file(workspace.configPath), rawWorkspaceContents); - } catch (error) { - return Promise.reject(error); - } - - const sourceConfigFolder = dirname(workspace.configPath); - const targetConfigFolder = dirname(targetConfigPath); - - // Rewrite absolute paths to relative paths if the target workspace folder - // is a parent of the location of the workspace file itself. Otherwise keep - // using absolute paths. - storedWorkspace.folders.forEach(folder => { - if (isRawFileWorkspaceFolder(folder)) { - if (!isAbsolute(folder.path)) { - folder.path = resolve(sourceConfigFolder, folder.path); // relative paths get resolved against the workspace location - } - folder.path = massageFolderPathForWorkspace(folder.path, URI.file(targetConfigFolder), storedWorkspace.folders); - } - - }); - - // Preserve as much of the existing workspace as possible by using jsonEdit - // and only changing the folders portion. - let newRawWorkspaceContents = rawWorkspaceContents; - const edits = jsonEdit.setProperty(rawWorkspaceContents, ['folders'], storedWorkspace.folders, { insertSpaces: false, tabSize: 4, eol: (isLinux || isMacintosh) ? '\n' : '\r\n' }); - edits.forEach(edit => { - newRawWorkspaceContents = jsonEdit.applyEdit(rawWorkspaceContents, edit); - }); + const targetConfigPathURI = URI.file(targetConfigPath); + const newRawWorkspaceContents = rewriteWorkspaceFileForNewLocation(raw.toString(), URI.file(workspace.configPath), targetConfigPathURI); return writeFile(targetConfigPath, newRawWorkspaceContents).then(() => { - const savedWorkspaceIdentifier = { id: this.getWorkspaceId(targetConfigPath), configPath: targetConfigPath }; - - // Event - this._onWorkspaceSaved.fire({ workspace: savedWorkspaceIdentifier, oldConfigPath: workspace.configPath }); - - // Delete untitled workspace this.deleteUntitledWorkspaceSync(workspace); - - return savedWorkspaceIdentifier; + return this.getWorkspaceIdentifier(targetConfigPathURI); }); }); } @@ -261,6 +221,11 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain this._onUntitledWorkspaceDeleted.fire(workspace); } + deleteIfUntitledWorkspace(workspace: IWorkspaceIdentifier): Promise { + this.deleteUntitledWorkspaceSync(workspace); + return Promise.resolve(); + } + private doDeleteUntitledWorkspaceSync(configPath: string): void { try { diff --git a/src/vs/platform/workspaces/node/workspacesIpc.ts b/src/vs/platform/workspaces/node/workspacesIpc.ts index 6b44c1dedea..e7a44f5bea7 100644 --- a/src/vs/platform/workspaces/node/workspacesIpc.ts +++ b/src/vs/platform/workspaces/node/workspacesIpc.ts @@ -32,6 +32,11 @@ export class WorkspacesChannel implements IServerChannel { return this.service.createUntitledWorkspace(folders); } + case 'deleteIfUntitledWorkspace': { + const rawWorkspace: IWorkspaceIdentifier = arg; + // TODO aeschli: resolve IWorkspaceIdentifier when switching to URI + return this.service.deleteIfUntitledWorkspace(rawWorkspace); + } } throw new Error(`Call not found: ${command}`); @@ -47,4 +52,8 @@ export class WorkspacesChannelClient implements IWorkspacesService { createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[]): Promise { return this.channel.call('createUntitledWorkspace', folders); } + + deleteIfUntitledWorkspace(workspace: IWorkspaceIdentifier): Promise { + return this.channel.call('deleteIfUntitledWorkspace', workspace); + } } diff --git a/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts b/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts index 13bb75596aa..b863c62f175 100644 --- a/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts +++ b/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts @@ -12,7 +12,7 @@ import * as pfs from 'vs/base/node/pfs'; import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; import { parseArgs } from 'vs/platform/environment/node/argv'; import { WorkspacesMainService, IStoredWorkspace } from 'vs/platform/workspaces/electron-main/workspacesMainService'; -import { WORKSPACE_EXTENSION, IWorkspaceSavedEvent, IWorkspaceIdentifier, IRawFileWorkspaceFolder, IWorkspaceFolderCreationData, IRawUriWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces'; +import { WORKSPACE_EXTENSION, IWorkspaceIdentifier, IRawFileWorkspaceFolder, IWorkspaceFolderCreationData, IRawUriWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces'; import { NullLogService } from 'vs/platform/log/common/log'; import { URI } from 'vs/base/common/uri'; import { getRandomTestPath } from 'vs/workbench/test/workbenchTestServices'; @@ -224,16 +224,6 @@ suite('WorkspacesMainService', () => { }); test('saveWorkspace (untitled)', () => { - let savedEvent: IWorkspaceSavedEvent; - const listener = service.onWorkspaceSaved(e => { - savedEvent = e; - }); - - let deletedEvent: IWorkspaceIdentifier; - const listener2 = service.onUntitledWorkspaceDeleted(e => { - deletedEvent = e; - }); - return createWorkspace([process.cwd(), os.tmpdir(), path.join(os.tmpdir(), 'somefolder')]).then(workspace => { const workspaceConfigPath = path.join(os.tmpdir(), `myworkspace.${Date.now()}.${WORKSPACE_EXTENSION}`); @@ -250,14 +240,6 @@ suite('WorkspacesMainService', () => { assertPathEquals((ws.folders[1]).path, '.'); // relative assertPathEquals((ws.folders[2]).path, path.relative(path.dirname(workspaceConfigPath), path.join(os.tmpdir(), 'somefolder'))); // relative - assert.equal(savedWorkspace, savedEvent.workspace); - assertPathEquals(workspace.configPath, savedEvent.oldConfigPath); - - assert.deepEqual(deletedEvent, workspace); - - listener.dispose(); - listener2.dispose(); - extfs.delSync(workspaceConfigPath); }); }); diff --git a/src/vs/workbench/services/workspace/common/workspaceEditing.ts b/src/vs/workbench/services/workspace/common/workspaceEditing.ts index abcb3fff36f..e65b8e1c29e 100644 --- a/src/vs/workbench/services/workspace/common/workspaceEditing.ts +++ b/src/vs/workbench/services/workspace/common/workspaceEditing.ts @@ -43,7 +43,7 @@ export interface IWorkspaceEditingService { createAndEnterWorkspace(folders: IWorkspaceFolderCreationData[], path?: URI): Promise; /** - * saves the workspace to the provided path and opens it. requires a workspace to be opened. + * saves the current workspace to the provided path and opens it. requires a workspace to be opened. */ saveAndEnterWorkspace(path: URI): Promise; diff --git a/src/vs/workbench/services/workspace/node/workspaceEditingService.ts b/src/vs/workbench/services/workspace/node/workspaceEditingService.ts index 2427501df0f..9ac1f58843e 100644 --- a/src/vs/workbench/services/workspace/node/workspaceEditingService.ts +++ b/src/vs/workbench/services/workspace/node/workspaceEditingService.ts @@ -9,7 +9,7 @@ import * as nls from 'vs/nls'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IWindowService, MessageBoxOptions, IWindowsService } from 'vs/platform/windows/common/windows'; import { IJSONEditingService, JSONEditingError, JSONEditingErrorCode } from 'vs/workbench/services/configuration/common/jsonEditing'; -import { IWorkspaceIdentifier, IWorkspaceFolderCreationData, IStoredWorkspace, isStoredWorkspaceFolder, isRawFileWorkspaceFolder, isWorkspaceIdentifier, toWorkspaceIdentifier, IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspaceIdentifier, IWorkspaceFolderCreationData, isWorkspaceIdentifier, toWorkspaceIdentifier, IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { WorkspaceService } from 'vs/workbench/services/configuration/node/configurationService'; import { IStorageService } from 'vs/platform/storage/common/storage'; @@ -21,15 +21,11 @@ import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { BackupFileService } from 'vs/workbench/services/backup/node/backupFileService'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { distinct } from 'vs/base/common/arrays'; -import { isLinux, isMacintosh } from 'vs/base/common/platform'; -import { isEqual, dirname, basename } from 'vs/base/common/resources'; -import * as json from 'vs/base/common/json'; -import * as jsonEdit from 'vs/base/common/jsonEdit'; +import { isLinux } from 'vs/base/common/platform'; +import { isEqual, basename } from 'vs/base/common/resources'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { IFileService } from 'vs/platform/files/common/files'; -import { isAbsolute, resolve } from 'path'; -import { massageFolderPathForWorkspace } from 'vs/platform/workspaces/node/workspaces'; -import { Schemas } from 'vs/base/common/network'; +import { rewriteWorkspaceFileForNewLocation } from 'vs/platform/workspaces/node/workspaces'; export class WorkspaceEditingService implements IWorkspaceEditingService { @@ -156,7 +152,7 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { const untitledWorkspace = await this.workspaceService.createUntitledWorkspace(folders); if (path) { - await this.saveWorkspace(untitledWorkspace, path); + await this.saveWorkspaceAs(untitledWorkspace, path); } else { path = URI.file(untitledWorkspace.configPath); } @@ -171,12 +167,11 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { if (!isWorkspaceIdentifier(currentWorkspaceIdentifier)) { return Promise.reject(null); } - await this.saveWorkspace(currentWorkspaceIdentifier, path); + await this.saveWorkspaceAs(currentWorkspaceIdentifier, path); return this.enterWorkspace(path); } - async isValidTargetWorkspacePath(path: URI): Promise { const windows = await this.windowsService.getWindows(); @@ -196,7 +191,7 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { return Promise.resolve(true); // OK } - private saveWorkspace(workspace: IWorkspaceIdentifier, targetConfigPathURI: URI): Promise { + private async saveWorkspaceAs(workspace: IWorkspaceIdentifier, targetConfigPathURI: URI): Promise { const configPathURI = URI.file(workspace.configPath); // Return early if target is same as source @@ -204,61 +199,11 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { return Promise.resolve(null); } - // Read the contents of the workspace file and resolve it - return this.fileSystemService.resolveFile(configPathURI).then(raw => { - const rawWorkspaceContents = raw.toString(); - let storedWorkspace: IStoredWorkspace; - try { - storedWorkspace = this.doParseStoredWorkspace(configPathURI, rawWorkspaceContents); - } catch (error) { - return Promise.reject(error); - } - - const sourceConfigFolder = dirname(configPathURI); - const targetConfigFolder = dirname(targetConfigPathURI); - - // Rewrite absolute paths to relative paths if the target workspace folder - // is a parent of the location of the workspace file itself. Otherwise keep - // using absolute paths. - storedWorkspace.folders.forEach(folder => { - if (isRawFileWorkspaceFolder(folder)) { - if (sourceConfigFolder.scheme === Schemas.file) { - if (!isAbsolute(folder.path)) { - folder.path = resolve(sourceConfigFolder.path, folder.path); // relative paths get resolved against the workspace location - } - folder.path = massageFolderPathForWorkspace(folder.path, targetConfigFolder, storedWorkspace.folders); - } - } - }); - - // Preserve as much of the existing workspace as possible by using jsonEdit - // and only changing the folders portion. - let newRawWorkspaceContents = rawWorkspaceContents; - const edits = jsonEdit.setProperty(rawWorkspaceContents, ['folders'], storedWorkspace.folders, { insertSpaces: false, tabSize: 4, eol: (isLinux || isMacintosh) ? '\n' : '\r\n' }); - edits.forEach(edit => { - newRawWorkspaceContents = jsonEdit.applyEdit(rawWorkspaceContents, edit); - }); - - return this.fileSystemService.createFile(targetConfigPathURI, newRawWorkspaceContents, { overwrite: true }); - }); - } - - private doParseStoredWorkspace(path: URI, contents: string): IStoredWorkspace { - - // Parse workspace file - let storedWorkspace: IStoredWorkspace = json.parse(contents); // use fault tolerant parser - - // Filter out folders which do not have a path or uri set - if (Array.isArray(storedWorkspace.folders)) { - storedWorkspace.folders = storedWorkspace.folders.filter(folder => isStoredWorkspaceFolder(folder)); - } - - // Validate - if (!Array.isArray(storedWorkspace.folders)) { - throw new Error(`${path} looks like an invalid workspace file.`); - } - - return storedWorkspace; + // Read the contents of the workspace file, update it to new location and save it. + const raw = await this.fileSystemService.resolveContent(configPathURI); + const newRawWorkspaceContents = rewriteWorkspaceFileForNewLocation(raw.value, configPathURI, targetConfigPathURI); + await this.fileSystemService.createFile(targetConfigPathURI, newRawWorkspaceContents, { overwrite: true }); + await this.workspaceService.deleteIfUntitledWorkspace(workspace); } private handleWorkspaceConfigurationEditingError(error: JSONEditingError): Promise { From ca9f48980d1fa5c790fb39b604e5dfc1ec21ed92 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 25 Jan 2019 12:14:51 +0100 Subject: [PATCH 131/274] fixes #66902 --- src/vs/base/browser/ui/tree/abstractTree.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index cf6122a3b6b..d03193cd180 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -511,6 +511,7 @@ class TypeFilterController implements IDisposable { const onDragOver = (event: DragEvent) => { const x = event.screenX - left; + event.dataTransfer.dropEffect = 'none'; if (x < midContainerWidth) { positionClassName = 'nw'; @@ -1050,4 +1051,4 @@ class TreeNavigator, TFilterData, TRef> implements IT this.index = this.view.length - 1; return this.current(); } -} \ No newline at end of file +} From f8e86c035a7a045e38b17f490c42555b4dad4fd4 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 25 Jan 2019 12:25:49 +0100 Subject: [PATCH 132/274] polish settings description fixes #66900 --- src/vs/platform/list/browser/listService.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/platform/list/browser/listService.ts b/src/vs/platform/list/browser/listService.ts index abf900f8577..9a1548a49cd 100644 --- a/src/vs/platform/list/browser/listService.ts +++ b/src/vs/platform/list/browser/listService.ts @@ -1197,12 +1197,12 @@ configurationRegistry.registerConfiguration({ 'type': 'string', 'enum': ['simple', 'highlight', 'filter'], 'enumDescriptions': [ - localize('keyboardNavigationSettingKey.simple', "Sets simple keyboard navigation for lists and trees."), - localize('keyboardNavigationSettingKey.highlight', "Enables highlighting keyboard navigation for lists and trees."), - localize('keyboardNavigationSettingKey.filter', "Enables filtering keyboard navigation for lists and trees.") + localize('keyboardNavigationSettingKey.simple', "Simple keyboard navigation focuses elements which match the keyboard input. Matching is done only on prefixes."), + localize('keyboardNavigationSettingKey.highlight', "Highlight keyboard navigation highlights elements which match the keyboard input. Further up and down navigation will traverse only the highlighted elements."), + localize('keyboardNavigationSettingKey.filter', "Filter keyboard navigation will filter out and hide all the elements which do not match the keyboard input.") ], 'default': 'highlight', - 'description': localize('keyboardNavigationSettingKey', "Controls the keyboard navigation style for lists and trees.") + 'description': localize('keyboardNavigationSettingKey', "Controls the keyboard navigation style for lists and trees in the workbench. Can be simple, highlight and filter.") }, } }); From bdae5ef54f3c7adbfd8d8bf347f483268d3abe23 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 25 Jan 2019 12:30:04 +0100 Subject: [PATCH 133/274] fix strict null --- src/vs/base/browser/ui/tree/abstractTree.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index d03193cd180..2b5bc0486cc 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -511,7 +511,9 @@ class TypeFilterController implements IDisposable { const onDragOver = (event: DragEvent) => { const x = event.screenX - left; - event.dataTransfer.dropEffect = 'none'; + if (event.dataTransfer) { + event.dataTransfer.dropEffect = 'none'; + } if (x < midContainerWidth) { positionClassName = 'nw'; From 1abe1c7ca031e4b6b670a2c926bbe928e2a918bf Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 25 Jan 2019 12:52:54 +0100 Subject: [PATCH 134/274] delete untitled workspace on enter --- src/vs/code/electron-main/windows.ts | 8 +++++++- src/vs/platform/workspaces/common/workspaces.ts | 4 +--- .../electron-main/workspacesMainService.ts | 8 +------- src/vs/platform/workspaces/node/workspacesIpc.ts | 9 --------- .../electron-main/workspacesMainService.test.ts | 16 ++++++++-------- .../workspace/node/workspaceEditingService.ts | 1 - 6 files changed, 17 insertions(+), 29 deletions(-) diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 42656aa18b3..5b0c18ecd87 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -2010,6 +2010,11 @@ class WorkspacesManager { backupPath = this.backupMainService.registerWorkspaceBackupSync(workspace, window.config.backupPath); } + // if the window was opened on an untitled workspace, delete it. + if (window.openedWorkspace && this.workspacesMainService.isUntitledWorkspace(window.openedWorkspace)) { + this.workspacesMainService.deleteUntitledWorkspaceSync(window.openedWorkspace); + } + // Update window configuration properly based on transition to workspace window.config.folderUri = undefined; window.config.workspace = workspace; @@ -2091,8 +2096,9 @@ class WorkspacesManager { defaultPath: this.getUntitledWorkspaceSaveDialogDefaultPath(workspace) }, window).then(target => { if (target) { - return this.workspacesMainService.saveWorkspace(workspace, target).then(savedWorkspace => { + return this.workspacesMainService.saveWorkspaceAs(workspace, target).then(savedWorkspace => { this.historyMainService.addRecentlyOpened([savedWorkspace], []); + this.workspacesMainService.deleteUntitledWorkspaceSync(workspace); return false; }, () => false); diff --git a/src/vs/platform/workspaces/common/workspaces.ts b/src/vs/platform/workspaces/common/workspaces.ts index 0d1c0d017c6..ec81bacefb9 100644 --- a/src/vs/platform/workspaces/common/workspaces.ts +++ b/src/vs/platform/workspaces/common/workspaces.ts @@ -79,7 +79,7 @@ export interface IWorkspacesMainService extends IWorkspacesService { onUntitledWorkspaceDeleted: Event; - saveWorkspace(workspace: IWorkspaceIdentifier, target: string): Promise; + saveWorkspaceAs(workspace: IWorkspaceIdentifier, target: string): Promise; createUntitledWorkspaceSync(folders?: IWorkspaceFolderCreationData[]): IWorkspaceIdentifier; @@ -100,8 +100,6 @@ export interface IWorkspacesService { _serviceBrand: any; createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[]): Promise; - - deleteIfUntitledWorkspace(workspace: IWorkspaceIdentifier): Promise; } export function isSingleFolderWorkspaceIdentifier(obj: any): obj is ISingleFolderWorkspaceIdentifier { diff --git a/src/vs/platform/workspaces/electron-main/workspacesMainService.ts b/src/vs/platform/workspaces/electron-main/workspacesMainService.ts index 9435ca0a714..015156751a9 100644 --- a/src/vs/platform/workspaces/electron-main/workspacesMainService.ts +++ b/src/vs/platform/workspaces/electron-main/workspacesMainService.ts @@ -190,7 +190,7 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain return this.isInsideWorkspacesHome(workspace.configPath); } - saveWorkspace(workspace: IWorkspaceIdentifier, targetConfigPath: string): Promise { + saveWorkspaceAs(workspace: IWorkspaceIdentifier, targetConfigPath: string): Promise { // Return early if target is same as source if (isEqual(workspace.configPath, targetConfigPath, !isLinux)) { @@ -203,7 +203,6 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain const newRawWorkspaceContents = rewriteWorkspaceFileForNewLocation(raw.toString(), URI.file(workspace.configPath), targetConfigPathURI); return writeFile(targetConfigPath, newRawWorkspaceContents).then(() => { - this.deleteUntitledWorkspaceSync(workspace); return this.getWorkspaceIdentifier(targetConfigPathURI); }); }); @@ -221,11 +220,6 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain this._onUntitledWorkspaceDeleted.fire(workspace); } - deleteIfUntitledWorkspace(workspace: IWorkspaceIdentifier): Promise { - this.deleteUntitledWorkspaceSync(workspace); - return Promise.resolve(); - } - private doDeleteUntitledWorkspaceSync(configPath: string): void { try { diff --git a/src/vs/platform/workspaces/node/workspacesIpc.ts b/src/vs/platform/workspaces/node/workspacesIpc.ts index e7a44f5bea7..6b44c1dedea 100644 --- a/src/vs/platform/workspaces/node/workspacesIpc.ts +++ b/src/vs/platform/workspaces/node/workspacesIpc.ts @@ -32,11 +32,6 @@ export class WorkspacesChannel implements IServerChannel { return this.service.createUntitledWorkspace(folders); } - case 'deleteIfUntitledWorkspace': { - const rawWorkspace: IWorkspaceIdentifier = arg; - // TODO aeschli: resolve IWorkspaceIdentifier when switching to URI - return this.service.deleteIfUntitledWorkspace(rawWorkspace); - } } throw new Error(`Call not found: ${command}`); @@ -52,8 +47,4 @@ export class WorkspacesChannelClient implements IWorkspacesService { createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[]): Promise { return this.channel.call('createUntitledWorkspace', folders); } - - deleteIfUntitledWorkspace(workspace: IWorkspaceIdentifier): Promise { - return this.channel.call('deleteIfUntitledWorkspace', workspace); - } } diff --git a/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts b/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts index b863c62f175..10c01dc60dc 100644 --- a/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts +++ b/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts @@ -227,7 +227,7 @@ suite('WorkspacesMainService', () => { return createWorkspace([process.cwd(), os.tmpdir(), path.join(os.tmpdir(), 'somefolder')]).then(workspace => { const workspaceConfigPath = path.join(os.tmpdir(), `myworkspace.${Date.now()}.${WORKSPACE_EXTENSION}`); - return service.saveWorkspace(workspace, workspaceConfigPath).then(savedWorkspace => { + return service.saveWorkspaceAs(workspace, workspaceConfigPath).then(savedWorkspace => { assert.ok(savedWorkspace.id); assert.notEqual(savedWorkspace.id, workspace.id); assert.equal(savedWorkspace.configPath, workspaceConfigPath); @@ -250,8 +250,8 @@ suite('WorkspacesMainService', () => { const workspaceConfigPath = path.join(os.tmpdir(), `myworkspace.${Date.now()}.${WORKSPACE_EXTENSION}`); const newWorkspaceConfigPath = path.join(os.tmpdir(), `mySavedWorkspace.${Date.now()}.${WORKSPACE_EXTENSION}`); - return service.saveWorkspace(workspace, workspaceConfigPath).then(savedWorkspace => { - return service.saveWorkspace(savedWorkspace, newWorkspaceConfigPath).then(newSavedWorkspace => { + return service.saveWorkspaceAs(workspace, workspaceConfigPath).then(savedWorkspace => { + return service.saveWorkspaceAs(savedWorkspace, newWorkspaceConfigPath).then(newSavedWorkspace => { assert.ok(newSavedWorkspace.id); assert.notEqual(newSavedWorkspace.id, workspace.id); assertPathEquals(newSavedWorkspace.configPath, newWorkspaceConfigPath); @@ -274,11 +274,11 @@ suite('WorkspacesMainService', () => { const workspaceConfigPath = path.join(os.tmpdir(), `myworkspace.${Date.now()}.${WORKSPACE_EXTENSION}`); const newWorkspaceConfigPath = path.join(os.tmpdir(), `mySavedWorkspace.${Date.now()}.${WORKSPACE_EXTENSION}`); - return service.saveWorkspace(workspace, workspaceConfigPath).then(savedWorkspace => { + return service.saveWorkspaceAs(workspace, workspaceConfigPath).then(savedWorkspace => { const contents = fs.readFileSync(savedWorkspace.configPath).toString(); fs.writeFileSync(savedWorkspace.configPath, `// this is a comment\n${contents}`); - return service.saveWorkspace(savedWorkspace, newWorkspaceConfigPath).then(newSavedWorkspace => { + return service.saveWorkspaceAs(savedWorkspace, newWorkspaceConfigPath).then(newSavedWorkspace => { assert.ok(newSavedWorkspace.id); assert.notEqual(newSavedWorkspace.id, workspace.id); assertPathEquals(newSavedWorkspace.configPath, newWorkspaceConfigPath); @@ -298,11 +298,11 @@ suite('WorkspacesMainService', () => { const workspaceConfigPath = path.join(os.tmpdir(), `myworkspace.${Date.now()}.${WORKSPACE_EXTENSION}`); const newWorkspaceConfigPath = path.join(os.tmpdir(), `mySavedWorkspace.${Date.now()}.${WORKSPACE_EXTENSION}`); - return service.saveWorkspace(workspace, workspaceConfigPath).then(savedWorkspace => { + return service.saveWorkspaceAs(workspace, workspaceConfigPath).then(savedWorkspace => { const contents = fs.readFileSync(savedWorkspace.configPath).toString(); fs.writeFileSync(savedWorkspace.configPath, contents.replace(/[\\]/g, '/')); // convert backslash to slash - return service.saveWorkspace(savedWorkspace, newWorkspaceConfigPath).then(newSavedWorkspace => { + return service.saveWorkspaceAs(savedWorkspace, newWorkspaceConfigPath).then(newSavedWorkspace => { assert.ok(newSavedWorkspace.id); assert.notEqual(newSavedWorkspace.id, workspace.id); assertPathEquals(newSavedWorkspace.configPath, newWorkspaceConfigPath); @@ -331,7 +331,7 @@ suite('WorkspacesMainService', () => { return createWorkspace([process.cwd(), os.tmpdir()]).then(workspace => { const workspaceConfigPath = path.join(os.tmpdir(), `myworkspace.${Date.now()}.${WORKSPACE_EXTENSION}`); - return service.saveWorkspace(workspace, workspaceConfigPath).then(savedWorkspace => { + return service.saveWorkspaceAs(workspace, workspaceConfigPath).then(savedWorkspace => { assert.ok(fs.existsSync(savedWorkspace.configPath)); service.deleteUntitledWorkspaceSync(savedWorkspace); diff --git a/src/vs/workbench/services/workspace/node/workspaceEditingService.ts b/src/vs/workbench/services/workspace/node/workspaceEditingService.ts index 9ac1f58843e..6172c7e23fb 100644 --- a/src/vs/workbench/services/workspace/node/workspaceEditingService.ts +++ b/src/vs/workbench/services/workspace/node/workspaceEditingService.ts @@ -203,7 +203,6 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { const raw = await this.fileSystemService.resolveContent(configPathURI); const newRawWorkspaceContents = rewriteWorkspaceFileForNewLocation(raw.value, configPathURI, targetConfigPathURI); await this.fileSystemService.createFile(targetConfigPathURI, newRawWorkspaceContents, { overwrite: true }); - await this.workspaceService.deleteIfUntitledWorkspace(workspace); } private handleWorkspaceConfigurationEditingError(error: JSONEditingError): Promise { From 723797d7e410a38b7b0cdfcf4327fd589b44f357 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 25 Jan 2019 13:33:12 +0100 Subject: [PATCH 135/274] add arguments to extension management service --- src/vs/code/node/cliProcessMain.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index 17624961e5f..98b1c6dfe3f 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -268,7 +268,7 @@ export function main(argv: ParsedArgs): Promise { const services = new ServiceCollection(); services.set(IConfigurationService, new SyncDescriptor(ConfigurationService)); services.set(IRequestService, new SyncDescriptor(RequestService)); - services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService)); + services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService, [false])); services.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService)); const appenders: AppInsightsAppender[] = []; From 1f69c42a6fb9eec9fd578692edd9b637a52af87e Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 25 Jan 2019 13:52:46 +0100 Subject: [PATCH 136/274] fix null checks --- src/vs/platform/workspaces/node/workspaces.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/workspaces/node/workspaces.ts b/src/vs/platform/workspaces/node/workspaces.ts index aaa3cdcac36..b37b4c7f402 100644 --- a/src/vs/platform/workspaces/node/workspaces.ts +++ b/src/vs/platform/workspaces/node/workspaces.ts @@ -64,8 +64,8 @@ export function massageFolderPathForWorkspace(absoluteFolderPath: string, target export function rewriteWorkspaceFileForNewLocation(rawWorkspaceContents: string, configPathURI: URI, targetConfigPathURI: URI) { let storedWorkspace = doParseStoredWorkspace(configPathURI, rawWorkspaceContents); - const sourceConfigFolder = dirname(configPathURI); - const targetConfigFolder = dirname(targetConfigPathURI); + const sourceConfigFolder = dirname(configPathURI)!; + const targetConfigFolder = dirname(targetConfigPathURI)!; // Rewrite absolute paths to relative paths if the target workspace folder // is a parent of the location of the workspace file itself. Otherwise keep From 22e9ae366d6e239b6d6e49f6ea1168638e524c2d Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 25 Jan 2019 14:30:50 +0100 Subject: [PATCH 137/274] fix tests --- .../workspaces/test/electron-main/workspacesMainService.test.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts b/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts index 10c01dc60dc..914930bb9ff 100644 --- a/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts +++ b/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts @@ -232,8 +232,6 @@ suite('WorkspacesMainService', () => { assert.notEqual(savedWorkspace.id, workspace.id); assert.equal(savedWorkspace.configPath, workspaceConfigPath); - assert.equal(service.deleteWorkspaceCall, workspace); - const ws = JSON.parse(fs.readFileSync(savedWorkspace.configPath).toString()) as IStoredWorkspace; assert.equal(ws.folders.length, 3); assertPathEquals((ws.folders[0]).path, process.cwd()); // absolute From 1c30527493b18bbcbddbbd247b4cf6ccd6689552 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 25 Jan 2019 14:35:06 +0100 Subject: [PATCH 138/274] fix #67067 --- .../files/electron-browser/explorerViewlet.ts | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/parts/files/electron-browser/explorerViewlet.ts b/src/vs/workbench/parts/files/electron-browser/explorerViewlet.ts index 7c01ed22445..5d546ccb1a6 100644 --- a/src/vs/workbench/parts/files/electron-browser/explorerViewlet.ts +++ b/src/vs/workbench/parts/files/electron-browser/explorerViewlet.ts @@ -62,27 +62,27 @@ export class ExplorerViewletViewsContribution extends Disposable implements IWor let viewDescriptorsToDeregister: IViewDescriptor[] = []; const openEditorsViewDescriptor = this.createOpenEditorsViewDescriptor(); - const openEditorsViewDescriptorExists = viewDescriptors.some(v => v.id === openEditorsViewDescriptor.id); - const explorerViewDescriptor = this.createExplorerViewDescriptor(); - const explorerViewDescriptorExists = viewDescriptors.some(v => v.id === explorerViewDescriptor.id); - const emptyViewDescriptor = this.createEmptyViewDescriptor(); - const emptyViewDescriptorExists = viewDescriptors.some(v => v.id === emptyViewDescriptor.id); - - if (!openEditorsViewDescriptorExists) { + if (!viewDescriptors.some(v => v.id === openEditorsViewDescriptor.id)) { viewDescriptorsToRegister.push(openEditorsViewDescriptor); } + + const explorerViewDescriptor = this.createExplorerViewDescriptor(); + const registeredExplorerViewDescriptor = viewDescriptors.filter(v => v.id === explorerViewDescriptor.id)[0]; + const emptyViewDescriptor = this.createEmptyViewDescriptor(); + const registeredEmptyViewDescriptor = viewDescriptors.filter(v => v.id === emptyViewDescriptor.id)[0]; + if (this.workspaceContextService.getWorkbenchState() === WorkbenchState.EMPTY || this.workspaceContextService.getWorkspace().folders.length === 0) { - if (explorerViewDescriptorExists) { - viewDescriptorsToDeregister.push(explorerViewDescriptor); + if (registeredExplorerViewDescriptor) { + viewDescriptorsToDeregister.push(registeredExplorerViewDescriptor); } - if (!emptyViewDescriptorExists) { + if (!registeredEmptyViewDescriptor) { viewDescriptorsToRegister.push(emptyViewDescriptor); } } else { - if (emptyViewDescriptorExists) { - viewDescriptorsToDeregister.push(emptyViewDescriptor); + if (registeredEmptyViewDescriptor) { + viewDescriptorsToDeregister.push(registeredEmptyViewDescriptor); } - if (!explorerViewDescriptorExists) { + if (!registeredExplorerViewDescriptor) { viewDescriptorsToRegister.push(explorerViewDescriptor); } } From 4902e53a522e4b54b466a2f109ba79d619d59640 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 25 Jan 2019 14:57:27 +0100 Subject: [PATCH 139/274] Fix #66954 --- .../parts/markers/electron-browser/markers.ts | 37 ++++++++++++------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/parts/markers/electron-browser/markers.ts b/src/vs/workbench/parts/markers/electron-browser/markers.ts index 6c8c060eefd..2ae1c928903 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markers.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markers.ts @@ -23,8 +23,7 @@ import { CodeAction } from 'vs/editor/common/modes'; import { Range } from 'vs/editor/common/core/range'; import { getCodeActions } from 'vs/editor/contrib/codeAction/codeAction'; import { CodeActionKind } from 'vs/editor/contrib/codeAction/codeActionTrigger'; -import { timeout } from 'vs/base/common/async'; -import { CancellationToken } from 'vs/base/common/cancellation'; +import { timeout, CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; export const IMarkersWorkbenchService = createDecorator('markersWorkbenchService'); @@ -45,8 +44,8 @@ export class MarkersWorkbenchService extends Disposable implements IMarkersWorkb readonly markersModel: MarkersModel; - private readonly allFixesCache: Map> = new Map>(); - private readonly codeActionsPromises: Map>> = new Map>>(); + private readonly allFixesCache: Map> = new Map>(); + private readonly codeActionsPromises: Map>> = new Map>>(); private readonly codeActions: Map> = new Map>(); constructor( @@ -69,8 +68,16 @@ export class MarkersWorkbenchService extends Disposable implements IMarkersWorkb private onMarkerChanged(resources: URI[]): void { for (const resource of resources) { - this.allFixesCache.delete(resource.toString()); - this.codeActionsPromises.delete(resource.toString()); + const allFixes = this.allFixesCache.get(resource.toString()); + if (allFixes) { + allFixes.cancel(); + this.allFixesCache.delete(resource.toString()); + } + const codeActions = this.codeActionsPromises.get(resource.toString()); + if (codeActions) { + codeActions.forEach(promise => promise.cancel()); + this.codeActionsPromises.delete(resource.toString()); + } this.codeActions.delete(resource.toString()); this.markersModel.setResourceMarkers(resource, this.readMarkers(resource)); } @@ -93,7 +100,7 @@ export class MarkersWorkbenchService extends Disposable implements IMarkersWorkb } else { let codeActionsPromisesPerMarker = this.codeActionsPromises.get(marker.resource.toString()); if (!codeActionsPromisesPerMarker) { - codeActionsPromisesPerMarker = new Map>(); + codeActionsPromisesPerMarker = new Map>(); this.codeActionsPromises.set(marker.resource.toString(), codeActionsPromisesPerMarker); } if (!codeActionsPromisesPerMarker.has(markerKey)) { @@ -153,16 +160,18 @@ export class MarkersWorkbenchService extends Disposable implements IMarkersWorkb }, ACTIVE_GROUP).then(() => undefined); } - private getFixes(marker: Marker): Promise { + private getFixes(marker: Marker): CancelablePromise { return this._getFixes(marker.resource, new Range(marker.range.startLineNumber, marker.range.startColumn, marker.range.endLineNumber, marker.range.endColumn)); } - private async _getFixes(uri: URI, range?: Range): Promise { - const model = this.modelService.getModel(uri); - if (model) { - return getCodeActions(model, range ? range : model.getFullModelRange(), { type: 'manual', filter: { kind: CodeActionKind.QuickFix } }, CancellationToken.None /* TODO: use cancellation here */); - } - return []; + private _getFixes(uri: URI, range?: Range): CancelablePromise { + return createCancelablePromise(cancellationToken => { + const model = this.modelService.getModel(uri); + if (model) { + return getCodeActions(model, range ? range : model.getFullModelRange(), { type: 'manual', filter: { kind: CodeActionKind.QuickFix } }, cancellationToken); + } + return Promise.resolve([]); + }); } } From 35a19d701da4e79f02d0f82ccb868289637468dd Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 25 Jan 2019 15:03:48 +0100 Subject: [PATCH 140/274] fixes #67111 --- src/vs/workbench/parts/files/electron-browser/fileActions.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/parts/files/electron-browser/fileActions.ts b/src/vs/workbench/parts/files/electron-browser/fileActions.ts index cce911a6104..3c64e250710 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileActions.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileActions.ts @@ -899,8 +899,8 @@ export function validateFileName(item: ExplorerItem, name: string): string { if (name !== item.name) { // Do not allow to overwrite existing file - const childExists = parent && !!parent.getChild(name); - if (childExists) { + const child = parent && parent.getChild(name); + if (child && child !== item) { return nls.localize('fileNameExistsError', "A file or folder **{0}** already exists at this location. Please choose a different name.", name); } } From ade3b368a6a6de277197b9d22b0433e14196bfad Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 25 Jan 2019 15:10:53 +0100 Subject: [PATCH 141/274] fix test --- src/vs/platform/workspaces/node/workspaces.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/workspaces/node/workspaces.ts b/src/vs/platform/workspaces/node/workspaces.ts index b37b4c7f402..e5e872b9f19 100644 --- a/src/vs/platform/workspaces/node/workspaces.ts +++ b/src/vs/platform/workspaces/node/workspaces.ts @@ -74,7 +74,7 @@ export function rewriteWorkspaceFileForNewLocation(rawWorkspaceContents: string, if (isRawFileWorkspaceFolder(folder)) { if (sourceConfigFolder.scheme === Schemas.file) { if (!isAbsolute(folder.path)) { - folder.path = resolve(sourceConfigFolder.path, folder.path); // relative paths get resolved against the workspace location + folder.path = resolve(fsPath(sourceConfigFolder), folder.path); // relative paths get resolved against the workspace location } folder.path = massageFolderPathForWorkspace(folder.path, targetConfigFolder, storedWorkspace.folders); } From e83cefd1e79f3082825c72c633434e99d09f9104 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 25 Jan 2019 15:14:11 +0100 Subject: [PATCH 142/274] Cleanup workbench.main file (#67117) * cleanup modules * more modules cleanup * more contribution cleanup --- src/tsconfig.strictNullChecks.json | 4 +- .../common/configurationExtensionPoint.ts | 0 .../common}/menusExtensionPoint.ts | 0 ...t.contribution.ts => quickInputActions.ts} | 0 ...en.contribution.ts => quickOpenActions.ts} | 0 .../browser/workbench.contribution.ts | 287 ++++++++++++++++++ ....contribution.ts => shell.contribution.ts} | 279 +---------------- .../codeEditor/codeEditor.contribution.ts | 18 -- .../codeEditor.contribution.ts | 18 ++ src/vs/workbench/workbench.main.ts | 117 ++++--- 10 files changed, 388 insertions(+), 335 deletions(-) rename src/vs/workbench/{services/configuration => api}/common/configurationExtensionPoint.ts (100%) rename src/vs/workbench/{services/actions/electron-browser => api/common}/menusExtensionPoint.ts (100%) rename src/vs/workbench/browser/parts/quickinput/{quickInput.contribution.ts => quickInputActions.ts} (100%) rename src/vs/workbench/browser/parts/quickopen/{quickopen.contribution.ts => quickOpenActions.ts} (100%) create mode 100644 src/vs/workbench/browser/workbench.contribution.ts rename src/vs/workbench/electron-browser/{main.contribution.ts => shell.contribution.ts} (69%) delete mode 100644 src/vs/workbench/parts/codeEditor/codeEditor.contribution.ts create mode 100644 src/vs/workbench/parts/codeEditor/electron-browser/codeEditor.contribution.ts diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index 838b9d7033e..530d76501d5 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -747,7 +747,8 @@ "./vs/workbench/parts/welcome/walkThrough/node/walkThroughContentProvider.ts", "./vs/workbench/parts/welcome/walkThrough/node/walkThroughInput.ts", "./vs/workbench/parts/welcome/walkThrough/node/walkThroughUtils.ts", - "./vs/workbench/services/actions/electron-browser/menusExtensionPoint.ts", + "./vs/workbench/api/common/menusExtensionPoint.ts", + "./vs/workbench/api/common/configurationExtensionPoint.ts", "./vs/workbench/services/activity/common/activity.ts", "./vs/workbench/services/backup/common/backup.ts", "./vs/workbench/services/backup/node/backupFileService.ts", @@ -756,7 +757,6 @@ "./vs/workbench/services/commands/common/commandService.ts", "./vs/workbench/services/commands/test/common/commandService.test.ts", "./vs/workbench/services/configuration/common/configuration.ts", - "./vs/workbench/services/configuration/common/configurationExtensionPoint.ts", "./vs/workbench/services/configuration/common/configurationModels.ts", "./vs/workbench/services/configuration/common/jsonEditing.ts", "./vs/workbench/services/configuration/node/jsonEditingService.ts", diff --git a/src/vs/workbench/services/configuration/common/configurationExtensionPoint.ts b/src/vs/workbench/api/common/configurationExtensionPoint.ts similarity index 100% rename from src/vs/workbench/services/configuration/common/configurationExtensionPoint.ts rename to src/vs/workbench/api/common/configurationExtensionPoint.ts diff --git a/src/vs/workbench/services/actions/electron-browser/menusExtensionPoint.ts b/src/vs/workbench/api/common/menusExtensionPoint.ts similarity index 100% rename from src/vs/workbench/services/actions/electron-browser/menusExtensionPoint.ts rename to src/vs/workbench/api/common/menusExtensionPoint.ts diff --git a/src/vs/workbench/browser/parts/quickinput/quickInput.contribution.ts b/src/vs/workbench/browser/parts/quickinput/quickInputActions.ts similarity index 100% rename from src/vs/workbench/browser/parts/quickinput/quickInput.contribution.ts rename to src/vs/workbench/browser/parts/quickinput/quickInputActions.ts diff --git a/src/vs/workbench/browser/parts/quickopen/quickopen.contribution.ts b/src/vs/workbench/browser/parts/quickopen/quickOpenActions.ts similarity index 100% rename from src/vs/workbench/browser/parts/quickopen/quickopen.contribution.ts rename to src/vs/workbench/browser/parts/quickopen/quickOpenActions.ts diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts new file mode 100644 index 00000000000..3fcab416418 --- /dev/null +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -0,0 +1,287 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Registry } from 'vs/platform/registry/common/platform'; +import * as nls from 'vs/nls'; +import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; +import { isMacintosh } from 'vs/base/common/platform'; + +const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); + +// Configuration: Workbench +configurationRegistry.registerConfiguration({ + 'id': 'workbench', + 'order': 7, + 'title': nls.localize('workbenchConfigurationTitle', "Workbench"), + 'type': 'object', + 'properties': { + 'workbench.editor.showTabs': { + 'type': 'boolean', + 'description': nls.localize('showEditorTabs', "Controls whether opened editors should show in tabs or not."), + 'default': true + }, + 'workbench.editor.highlightModifiedTabs': { + 'type': 'boolean', + 'description': nls.localize('highlightModifiedTabs', "Controls whether a top border is drawn on modified (dirty) editor tabs or not."), + 'default': false + }, + 'workbench.editor.labelFormat': { + 'type': 'string', + 'enum': ['default', 'short', 'medium', 'long'], + 'enumDescriptions': [ + nls.localize('workbench.editor.labelFormat.default', "Show the name of the file. When tabs are enabled and two files have the same name in one group the distinguishing sections of each file's path are added. When tabs are disabled, the path relative to the workspace folder is shown if the editor is active."), + nls.localize('workbench.editor.labelFormat.short', "Show the name of the file followed by its directory name."), + nls.localize('workbench.editor.labelFormat.medium', "Show the name of the file followed by its path relative to the workspace folder."), + nls.localize('workbench.editor.labelFormat.long', "Show the name of the file followed by its absolute path.") + ], + 'default': 'default', + 'description': nls.localize({ + comment: ['This is the description for a setting. Values surrounded by parenthesis are not to be translated.'], + key: 'tabDescription' + }, "Controls the format of the label for an editor."), + }, + 'workbench.editor.tabCloseButton': { + 'type': 'string', + 'enum': ['left', 'right', 'off'], + 'default': 'right', + 'description': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'editorTabCloseButton' }, "Controls the position of the editor's tabs close buttons, or disables them when set to 'off'.") + }, + 'workbench.editor.tabSizing': { + 'type': 'string', + 'enum': ['fit', 'shrink'], + 'default': 'fit', + 'enumDescriptions': [ + nls.localize('workbench.editor.tabSizing.fit', "Always keep tabs large enough to show the full editor label."), + nls.localize('workbench.editor.tabSizing.shrink', "Allow tabs to get smaller when the available space is not enough to show all tabs at once.") + ], + 'description': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'tabSizing' }, "Controls the sizing of editor tabs.") + }, + 'workbench.editor.closeTabsInMRUOrder': { + 'type': 'boolean', + 'description': nls.localize('closeTabsInMRUOrder', "Controls whether tabs are closed in most recently used order or from left to right."), + 'default': true + }, + 'workbench.editor.showIcons': { + 'type': 'boolean', + 'description': nls.localize('showIcons', "Controls whether opened editors should show with an icon or not. This requires an icon theme to be enabled as well."), + 'default': true + }, + 'workbench.editor.enablePreview': { + 'type': 'boolean', + 'description': nls.localize('enablePreview', "Controls whether opened editors show as preview. Preview editors are reused until they are pinned (e.g. via double click or editing) and show up with an italic font style."), + 'default': true + }, + 'workbench.editor.enablePreviewFromQuickOpen': { + 'type': 'boolean', + 'description': nls.localize('enablePreviewFromQuickOpen', "Controls whether opened editors from Quick Open show as preview. Preview editors are reused until they are pinned (e.g. via double click or editing)."), + 'default': true + }, + 'workbench.editor.closeOnFileDelete': { + 'type': 'boolean', + 'description': nls.localize('closeOnFileDelete', "Controls whether editors showing a file that was opened during the session should close automatically when getting deleted or renamed by some other process. Disabling this will keep the editor open on such an event. Note that deleting from within the application will always close the editor and that dirty files will never close to preserve your data."), + 'default': false + }, + 'workbench.editor.openPositioning': { + 'type': 'string', + 'enum': ['left', 'right', 'first', 'last'], + 'default': 'right', + 'markdownDescription': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'editorOpenPositioning' }, "Controls where editors open. Select `left` or `right` to open editors to the left or right of the currently active one. Select `first` or `last` to open editors independently from the currently active one.") + }, + 'workbench.editor.openSideBySideDirection': { + 'type': 'string', + 'enum': ['right', 'down'], + 'default': 'right', + 'markdownDescription': nls.localize('sideBySideDirection', "Controls the default direction of editors that are opened side by side (e.g. from the explorer). By default, editors will open on the right hand side of the currently active one. If changed to `down`, the editors will open below the currently active one.") + }, + 'workbench.editor.closeEmptyGroups': { + 'type': 'boolean', + 'description': nls.localize('closeEmptyGroups', "Controls the behavior of empty editor groups when the last tab in the group is closed. When enabled, empty groups will automatically close. When disabled, empty groups will remain part of the grid."), + 'default': true + }, + 'workbench.editor.revealIfOpen': { + 'type': 'boolean', + 'description': nls.localize('revealIfOpen', "Controls whether an editor is revealed in any of the visible groups if opened. If disabled, an editor will prefer to open in the currently active editor group. If enabled, an already opened editor will be revealed instead of opened again in the currently active editor group. Note that there are some cases where this setting is ignored, e.g. when forcing an editor to open in a specific group or to the side of the currently active group."), + 'default': false + }, + 'workbench.editor.swipeToNavigate': { + 'type': 'boolean', + 'description': nls.localize('swipeToNavigate', "Navigate between open files using three-finger swipe horizontally."), + 'default': false, + 'included': isMacintosh + }, + 'workbench.editor.restoreViewState': { + 'type': 'boolean', + 'description': nls.localize('restoreViewState', "Restores the last view state (e.g. scroll position) when re-opening files after they have been closed."), + 'default': true, + }, + 'workbench.editor.centeredLayoutAutoResize': { + 'type': 'boolean', + 'default': true, + 'description': nls.localize('centeredLayoutAutoResize', "Controls if the centered layout should automatically resize to maximum width when more than one group is open. Once only one group is open it will resize back to the original centered width.") + }, + 'workbench.commandPalette.history': { + 'type': 'number', + 'description': nls.localize('commandHistory', "Controls the number of recently used commands to keep in history for the command palette. Set to 0 to disable command history."), + 'default': 50 + }, + 'workbench.commandPalette.preserveInput': { + 'type': 'boolean', + 'description': nls.localize('preserveInput', "Controls whether the last typed input to the command palette should be restored when opening it the next time."), + 'default': false + }, + 'workbench.quickOpen.closeOnFocusLost': { + 'type': 'boolean', + 'description': nls.localize('closeOnFocusLost', "Controls whether Quick Open should close automatically once it loses focus."), + 'default': true + }, + 'workbench.quickOpen.preserveInput': { + 'type': 'boolean', + 'description': nls.localize('workbench.quickOpen.preserveInput', "Controls whether the last typed input to Quick Open should be restored when opening it the next time."), + 'default': false + }, + 'workbench.settings.openDefaultSettings': { + 'type': 'boolean', + 'description': nls.localize('openDefaultSettings', "Controls whether opening settings also opens an editor showing all default settings."), + 'default': false + }, + 'workbench.settings.useSplitJSON': { + 'type': 'boolean', + 'markdownDescription': nls.localize('useSplitJSON', "Controls whether to use the split JSON editor when editing settings as JSON."), + 'default': false + }, + 'workbench.settings.openDefaultKeybindings': { + 'type': 'boolean', + 'description': nls.localize('openDefaultKeybindings', "Controls whether opening keybinding settings also opens an editor showing all default keybindings."), + 'default': true + }, + 'workbench.sideBar.location': { + 'type': 'string', + 'enum': ['left', 'right'], + 'default': 'left', + 'description': nls.localize('sideBarLocation', "Controls the location of the sidebar. It can either show on the left or right of the workbench.") + }, + 'workbench.panel.defaultLocation': { + 'type': 'string', + 'enum': ['bottom', 'right'], + 'default': 'bottom', + 'description': nls.localize('panelDefaultLocation', "Controls the default location of the panel (terminal, debug console, output, problems). It can either show at the bottom or on the right of the workbench.") + }, + 'workbench.statusBar.visible': { + 'type': 'boolean', + 'default': true, + 'description': nls.localize('statusBarVisibility', "Controls the visibility of the status bar at the bottom of the workbench.") + }, + 'workbench.activityBar.visible': { + 'type': 'boolean', + 'default': true, + 'description': nls.localize('activityBarVisibility', "Controls the visibility of the activity bar in the workbench.") + }, + 'workbench.view.alwaysShowHeaderActions': { + 'type': 'boolean', + 'default': false, + 'description': nls.localize('viewVisibility', "Controls the visibility of view header actions. View header actions may either be always visible, or only visible when that view is focused or hovered over.") + }, + 'workbench.fontAliasing': { + 'type': 'string', + 'enum': ['default', 'antialiased', 'none', 'auto'], + 'default': 'default', + 'description': + nls.localize('fontAliasing', "Controls font aliasing method in the workbench."), + 'enumDescriptions': [ + nls.localize('workbench.fontAliasing.default', "Sub-pixel font smoothing. On most non-retina displays this will give the sharpest text."), + nls.localize('workbench.fontAliasing.antialiased', "Smooth the font on the level of the pixel, as opposed to the subpixel. Can make the font appear lighter overall."), + nls.localize('workbench.fontAliasing.none', "Disables font smoothing. Text will show with jagged sharp edges."), + nls.localize('workbench.fontAliasing.auto', "Applies `default` or `antialiased` automatically based on the DPI of displays.") + ], + 'included': isMacintosh + }, + 'workbench.settings.enableNaturalLanguageSearch': { + 'type': 'boolean', + 'description': nls.localize('enableNaturalLanguageSettingsSearch', "Controls whether to enable the natural language search mode for settings. The natural language search is provided by a Microsoft online service."), + 'default': true, + 'scope': ConfigurationScope.WINDOW, + 'tags': ['usesOnlineServices'] + }, + 'workbench.settings.settingsSearchTocBehavior': { + 'type': 'string', + 'enum': ['hide', 'filter'], + 'enumDescriptions': [ + nls.localize('settingsSearchTocBehavior.hide', "Hide the Table of Contents while searching."), + nls.localize('settingsSearchTocBehavior.filter', "Filter the Table of Contents to just categories that have matching settings. Clicking a category will filter the results to that category."), + ], + 'description': nls.localize('settingsSearchTocBehavior', "Controls the behavior of the settings editor Table of Contents while searching."), + 'default': 'filter', + 'scope': ConfigurationScope.WINDOW + }, + 'workbench.settings.editor': { + 'type': 'string', + 'enum': ['ui', 'json'], + 'enumDescriptions': [ + nls.localize('settings.editor.ui', "Use the settings UI editor."), + nls.localize('settings.editor.json', "Use the JSON file editor."), + ], + 'description': nls.localize('settings.editor.desc', "Determines which settings editor to use by default."), + 'default': 'ui', + 'scope': ConfigurationScope.WINDOW + }, + 'workbench.enableExperiments': { + 'type': 'boolean', + 'description': nls.localize('workbench.enableExperiments', "Fetches experiments to run from a Microsoft online service."), + 'default': true, + 'tags': ['usesOnlineServices'] + }, + 'workbench.useExperimentalGridLayout': { + 'type': 'boolean', + 'description': nls.localize('workbench.useExperimentalGridLayout', "Enables the grid layout for the workbench. This setting may enable additional layout options for workbench components."), + 'default': false, + 'scope': ConfigurationScope.APPLICATION + } + } +}); + +// Configuration: Zen Mode +configurationRegistry.registerConfiguration({ + 'id': 'zenMode', + 'order': 9, + 'title': nls.localize('zenModeConfigurationTitle', "Zen Mode"), + 'type': 'object', + 'properties': { + 'zenMode.fullScreen': { + 'type': 'boolean', + 'default': true, + 'description': nls.localize('zenMode.fullScreen', "Controls whether turning on Zen Mode also puts the workbench into full screen mode.") + }, + 'zenMode.centerLayout': { + 'type': 'boolean', + 'default': true, + 'description': nls.localize('zenMode.centerLayout', "Controls whether turning on Zen Mode also centers the layout.") + }, + 'zenMode.hideTabs': { + 'type': 'boolean', + 'default': true, + 'description': nls.localize('zenMode.hideTabs', "Controls whether turning on Zen Mode also hides workbench tabs.") + }, + 'zenMode.hideStatusBar': { + 'type': 'boolean', + 'default': true, + 'description': nls.localize('zenMode.hideStatusBar', "Controls whether turning on Zen Mode also hides the status bar at the bottom of the workbench.") + }, + 'zenMode.hideActivityBar': { + 'type': 'boolean', + 'default': true, + 'description': nls.localize('zenMode.hideActivityBar', "Controls whether turning on Zen Mode also hides the activity bar at the left of the workbench.") + }, + 'zenMode.hideLineNumbers': { + 'type': 'boolean', + 'default': true, + 'description': nls.localize('zenMode.hideLineNumbers', "Controls whether turning on Zen Mode also hides the editor line numbers.") + }, + 'zenMode.restore': { + 'type': 'boolean', + 'default': false, + 'description': nls.localize('zenMode.restore', "Controls whether a window should restore to zen mode if it was exited in zen mode.") + } + } +}); \ No newline at end of file diff --git a/src/vs/workbench/electron-browser/main.contribution.ts b/src/vs/workbench/electron-browser/shell.contribution.ts similarity index 69% rename from src/vs/workbench/electron-browser/main.contribution.ts rename to src/vs/workbench/electron-browser/shell.contribution.ts index d88705f06e4..55877e94029 100644 --- a/src/vs/workbench/electron-browser/main.contribution.ts +++ b/src/vs/workbench/electron-browser/shell.contribution.ts @@ -471,241 +471,8 @@ MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { when: IsMacContext.toNegated() }); -// Configuration: Workbench -const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); - -configurationRegistry.registerConfiguration({ - 'id': 'workbench', - 'order': 7, - 'title': nls.localize('workbenchConfigurationTitle', "Workbench"), - 'type': 'object', - 'properties': { - 'workbench.editor.showTabs': { - 'type': 'boolean', - 'description': nls.localize('showEditorTabs', "Controls whether opened editors should show in tabs or not."), - 'default': true - }, - 'workbench.editor.highlightModifiedTabs': { - 'type': 'boolean', - 'description': nls.localize('highlightModifiedTabs', "Controls whether a top border is drawn on modified (dirty) editor tabs or not."), - 'default': false - }, - 'workbench.editor.labelFormat': { - 'type': 'string', - 'enum': ['default', 'short', 'medium', 'long'], - 'enumDescriptions': [ - nls.localize('workbench.editor.labelFormat.default', "Show the name of the file. When tabs are enabled and two files have the same name in one group the distinguishing sections of each file's path are added. When tabs are disabled, the path relative to the workspace folder is shown if the editor is active."), - nls.localize('workbench.editor.labelFormat.short', "Show the name of the file followed by its directory name."), - nls.localize('workbench.editor.labelFormat.medium', "Show the name of the file followed by its path relative to the workspace folder."), - nls.localize('workbench.editor.labelFormat.long', "Show the name of the file followed by its absolute path.") - ], - 'default': 'default', - 'description': nls.localize({ - comment: ['This is the description for a setting. Values surrounded by parenthesis are not to be translated.'], - key: 'tabDescription' - }, "Controls the format of the label for an editor."), - }, - 'workbench.editor.tabCloseButton': { - 'type': 'string', - 'enum': ['left', 'right', 'off'], - 'default': 'right', - 'description': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'editorTabCloseButton' }, "Controls the position of the editor's tabs close buttons, or disables them when set to 'off'.") - }, - 'workbench.editor.tabSizing': { - 'type': 'string', - 'enum': ['fit', 'shrink'], - 'default': 'fit', - 'enumDescriptions': [ - nls.localize('workbench.editor.tabSizing.fit', "Always keep tabs large enough to show the full editor label."), - nls.localize('workbench.editor.tabSizing.shrink', "Allow tabs to get smaller when the available space is not enough to show all tabs at once.") - ], - 'description': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'tabSizing' }, "Controls the sizing of editor tabs.") - }, - 'workbench.editor.closeTabsInMRUOrder': { - 'type': 'boolean', - 'description': nls.localize('closeTabsInMRUOrder', "Controls whether tabs are closed in most recently used order or from left to right."), - 'default': true - }, - 'workbench.editor.showIcons': { - 'type': 'boolean', - 'description': nls.localize('showIcons', "Controls whether opened editors should show with an icon or not. This requires an icon theme to be enabled as well."), - 'default': true - }, - 'workbench.editor.enablePreview': { - 'type': 'boolean', - 'description': nls.localize('enablePreview', "Controls whether opened editors show as preview. Preview editors are reused until they are pinned (e.g. via double click or editing) and show up with an italic font style."), - 'default': true - }, - 'workbench.editor.enablePreviewFromQuickOpen': { - 'type': 'boolean', - 'description': nls.localize('enablePreviewFromQuickOpen', "Controls whether opened editors from Quick Open show as preview. Preview editors are reused until they are pinned (e.g. via double click or editing)."), - 'default': true - }, - 'workbench.editor.closeOnFileDelete': { - 'type': 'boolean', - 'description': nls.localize('closeOnFileDelete', "Controls whether editors showing a file that was opened during the session should close automatically when getting deleted or renamed by some other process. Disabling this will keep the editor open on such an event. Note that deleting from within the application will always close the editor and that dirty files will never close to preserve your data."), - 'default': false - }, - 'workbench.editor.openPositioning': { - 'type': 'string', - 'enum': ['left', 'right', 'first', 'last'], - 'default': 'right', - 'markdownDescription': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'editorOpenPositioning' }, "Controls where editors open. Select `left` or `right` to open editors to the left or right of the currently active one. Select `first` or `last` to open editors independently from the currently active one.") - }, - 'workbench.editor.openSideBySideDirection': { - 'type': 'string', - 'enum': ['right', 'down'], - 'default': 'right', - 'markdownDescription': nls.localize('sideBySideDirection', "Controls the default direction of editors that are opened side by side (e.g. from the explorer). By default, editors will open on the right hand side of the currently active one. If changed to `down`, the editors will open below the currently active one.") - }, - 'workbench.editor.closeEmptyGroups': { - 'type': 'boolean', - 'description': nls.localize('closeEmptyGroups', "Controls the behavior of empty editor groups when the last tab in the group is closed. When enabled, empty groups will automatically close. When disabled, empty groups will remain part of the grid."), - 'default': true - }, - 'workbench.editor.revealIfOpen': { - 'type': 'boolean', - 'description': nls.localize('revealIfOpen', "Controls whether an editor is revealed in any of the visible groups if opened. If disabled, an editor will prefer to open in the currently active editor group. If enabled, an already opened editor will be revealed instead of opened again in the currently active editor group. Note that there are some cases where this setting is ignored, e.g. when forcing an editor to open in a specific group or to the side of the currently active group."), - 'default': false - }, - 'workbench.editor.swipeToNavigate': { - 'type': 'boolean', - 'description': nls.localize('swipeToNavigate', "Navigate between open files using three-finger swipe horizontally."), - 'default': false, - 'included': isMacintosh - }, - 'workbench.editor.restoreViewState': { - 'type': 'boolean', - 'description': nls.localize('restoreViewState', "Restores the last view state (e.g. scroll position) when re-opening files after they have been closed."), - 'default': true, - }, - 'workbench.editor.centeredLayoutAutoResize': { - 'type': 'boolean', - 'default': true, - 'description': nls.localize('centeredLayoutAutoResize', "Controls if the centered layout should automatically resize to maximum width when more than one group is open. Once only one group is open it will resize back to the original centered width.") - }, - 'workbench.commandPalette.history': { - 'type': 'number', - 'description': nls.localize('commandHistory', "Controls the number of recently used commands to keep in history for the command palette. Set to 0 to disable command history."), - 'default': 50 - }, - 'workbench.commandPalette.preserveInput': { - 'type': 'boolean', - 'description': nls.localize('preserveInput', "Controls whether the last typed input to the command palette should be restored when opening it the next time."), - 'default': false - }, - 'workbench.quickOpen.closeOnFocusLost': { - 'type': 'boolean', - 'description': nls.localize('closeOnFocusLost', "Controls whether Quick Open should close automatically once it loses focus."), - 'default': true - }, - 'workbench.quickOpen.preserveInput': { - 'type': 'boolean', - 'description': nls.localize('workbench.quickOpen.preserveInput', "Controls whether the last typed input to Quick Open should be restored when opening it the next time."), - 'default': false - }, - 'workbench.settings.openDefaultSettings': { - 'type': 'boolean', - 'description': nls.localize('openDefaultSettings', "Controls whether opening settings also opens an editor showing all default settings."), - 'default': false - }, - 'workbench.settings.useSplitJSON': { - 'type': 'boolean', - 'markdownDescription': nls.localize('useSplitJSON', "Controls whether to use the split JSON editor when editing settings as JSON."), - 'default': false - }, - 'workbench.settings.openDefaultKeybindings': { - 'type': 'boolean', - 'description': nls.localize('openDefaultKeybindings', "Controls whether opening keybinding settings also opens an editor showing all default keybindings."), - 'default': true - }, - 'workbench.sideBar.location': { - 'type': 'string', - 'enum': ['left', 'right'], - 'default': 'left', - 'description': nls.localize('sideBarLocation', "Controls the location of the sidebar. It can either show on the left or right of the workbench.") - }, - 'workbench.panel.defaultLocation': { - 'type': 'string', - 'enum': ['bottom', 'right'], - 'default': 'bottom', - 'description': nls.localize('panelDefaultLocation', "Controls the default location of the panel (terminal, debug console, output, problems). It can either show at the bottom or on the right of the workbench.") - }, - 'workbench.statusBar.visible': { - 'type': 'boolean', - 'default': true, - 'description': nls.localize('statusBarVisibility', "Controls the visibility of the status bar at the bottom of the workbench.") - }, - 'workbench.activityBar.visible': { - 'type': 'boolean', - 'default': true, - 'description': nls.localize('activityBarVisibility', "Controls the visibility of the activity bar in the workbench.") - }, - 'workbench.view.alwaysShowHeaderActions': { - 'type': 'boolean', - 'default': false, - 'description': nls.localize('viewVisibility', "Controls the visibility of view header actions. View header actions may either be always visible, or only visible when that view is focused or hovered over.") - }, - 'workbench.fontAliasing': { - 'type': 'string', - 'enum': ['default', 'antialiased', 'none', 'auto'], - 'default': 'default', - 'description': - nls.localize('fontAliasing', "Controls font aliasing method in the workbench."), - 'enumDescriptions': [ - nls.localize('workbench.fontAliasing.default', "Sub-pixel font smoothing. On most non-retina displays this will give the sharpest text."), - nls.localize('workbench.fontAliasing.antialiased', "Smooth the font on the level of the pixel, as opposed to the subpixel. Can make the font appear lighter overall."), - nls.localize('workbench.fontAliasing.none', "Disables font smoothing. Text will show with jagged sharp edges."), - nls.localize('workbench.fontAliasing.auto', "Applies `default` or `antialiased` automatically based on the DPI of displays.") - ], - 'included': isMacintosh - }, - 'workbench.settings.enableNaturalLanguageSearch': { - 'type': 'boolean', - 'description': nls.localize('enableNaturalLanguageSettingsSearch', "Controls whether to enable the natural language search mode for settings. The natural language search is provided by a Microsoft online service."), - 'default': true, - 'scope': ConfigurationScope.WINDOW, - 'tags': ['usesOnlineServices'] - }, - 'workbench.settings.settingsSearchTocBehavior': { - 'type': 'string', - 'enum': ['hide', 'filter'], - 'enumDescriptions': [ - nls.localize('settingsSearchTocBehavior.hide', "Hide the Table of Contents while searching."), - nls.localize('settingsSearchTocBehavior.filter', "Filter the Table of Contents to just categories that have matching settings. Clicking a category will filter the results to that category."), - ], - 'description': nls.localize('settingsSearchTocBehavior', "Controls the behavior of the settings editor Table of Contents while searching."), - 'default': 'filter', - 'scope': ConfigurationScope.WINDOW - }, - 'workbench.settings.editor': { - 'type': 'string', - 'enum': ['ui', 'json'], - 'enumDescriptions': [ - nls.localize('settings.editor.ui', "Use the settings UI editor."), - nls.localize('settings.editor.json', "Use the JSON file editor."), - ], - 'description': nls.localize('settings.editor.desc', "Determines which settings editor to use by default."), - 'default': 'ui', - 'scope': ConfigurationScope.WINDOW - }, - 'workbench.enableExperiments': { - 'type': 'boolean', - 'description': nls.localize('workbench.enableExperiments', "Fetches experiments to run from a Microsoft online service."), - 'default': true, - 'tags': ['usesOnlineServices'] - }, - 'workbench.useExperimentalGridLayout': { - 'type': 'boolean', - 'description': nls.localize('workbench.useExperimentalGridLayout', "Enables the grid layout for the workbench. This setting may enable additional layout options for workbench components."), - 'default': false, - 'scope': ConfigurationScope.APPLICATION - } - } -}); - // Configuration: Window - +const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); configurationRegistry.registerConfiguration({ 'id': 'window', 'order': 8, @@ -870,47 +637,3 @@ configurationRegistry.registerConfiguration({ } }); -// Configuration: Zen Mode -configurationRegistry.registerConfiguration({ - 'id': 'zenMode', - 'order': 9, - 'title': nls.localize('zenModeConfigurationTitle', "Zen Mode"), - 'type': 'object', - 'properties': { - 'zenMode.fullScreen': { - 'type': 'boolean', - 'default': true, - 'description': nls.localize('zenMode.fullScreen', "Controls whether turning on Zen Mode also puts the workbench into full screen mode.") - }, - 'zenMode.centerLayout': { - 'type': 'boolean', - 'default': true, - 'description': nls.localize('zenMode.centerLayout', "Controls whether turning on Zen Mode also centers the layout.") - }, - 'zenMode.hideTabs': { - 'type': 'boolean', - 'default': true, - 'description': nls.localize('zenMode.hideTabs', "Controls whether turning on Zen Mode also hides workbench tabs.") - }, - 'zenMode.hideStatusBar': { - 'type': 'boolean', - 'default': true, - 'description': nls.localize('zenMode.hideStatusBar', "Controls whether turning on Zen Mode also hides the status bar at the bottom of the workbench.") - }, - 'zenMode.hideActivityBar': { - 'type': 'boolean', - 'default': true, - 'description': nls.localize('zenMode.hideActivityBar', "Controls whether turning on Zen Mode also hides the activity bar at the left of the workbench.") - }, - 'zenMode.hideLineNumbers': { - 'type': 'boolean', - 'default': true, - 'description': nls.localize('zenMode.hideLineNumbers', "Controls whether turning on Zen Mode also hides the editor line numbers.") - }, - 'zenMode.restore': { - 'type': 'boolean', - 'default': false, - 'description': nls.localize('zenMode.restore', "Controls whether a window should restore to zen mode if it was exited in zen mode.") - } - } -}); diff --git a/src/vs/workbench/parts/codeEditor/codeEditor.contribution.ts b/src/vs/workbench/parts/codeEditor/codeEditor.contribution.ts deleted file mode 100644 index 871d6ab2d6b..00000000000 --- a/src/vs/workbench/parts/codeEditor/codeEditor.contribution.ts +++ /dev/null @@ -1,18 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import './browser/menuPreventer'; -import './electron-browser/accessibility'; -import './electron-browser/inspectKeybindings'; -import './electron-browser/largeFileOptimizations'; -import './electron-browser/selectionClipboard'; -import './electron-browser/sleepResumeRepaintMinimap'; -import './electron-browser/textMate/inspectTMScopes'; -import './electron-browser/toggleMinimap'; -import './electron-browser/toggleMultiCursorModifier'; -import './electron-browser/toggleRenderControlCharacter'; -import './electron-browser/toggleRenderWhitespace'; -import './electron-browser/toggleWordWrap'; -import './electron-browser/workbenchReferenceSearch'; \ No newline at end of file diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/codeEditor.contribution.ts b/src/vs/workbench/parts/codeEditor/electron-browser/codeEditor.contribution.ts new file mode 100644 index 00000000000..2f9faa53af6 --- /dev/null +++ b/src/vs/workbench/parts/codeEditor/electron-browser/codeEditor.contribution.ts @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import '../browser/menuPreventer'; +import './accessibility'; +import './inspectKeybindings'; +import './largeFileOptimizations'; +import './selectionClipboard'; +import './sleepResumeRepaintMinimap'; +import './textMate/inspectTMScopes'; +import './toggleMinimap'; +import './toggleMultiCursorModifier'; +import './toggleRenderControlCharacter'; +import './toggleRenderWhitespace'; +import './toggleWordWrap'; +import './workbenchReferenceSearch'; \ No newline at end of file diff --git a/src/vs/workbench/workbench.main.ts b/src/vs/workbench/workbench.main.ts index 448a34dabbb..1ad274696dc 100644 --- a/src/vs/workbench/workbench.main.ts +++ b/src/vs/workbench/workbench.main.ts @@ -3,133 +3,176 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -// Base -import 'vs/base/common/strings'; -import 'vs/base/common/errors'; +//#region --- workbench/editor core -// Configuration -import 'vs/workbench/services/configuration/common/configurationExtensionPoint'; - -// Editor import 'vs/editor/editor.all'; -// Platform -import 'vs/platform/widget/browser/contextScopedHistoryWidget'; +import 'vs/workbench/api/electron-browser/extensionHost.contribution'; -// Menus/Actions -import 'vs/workbench/services/actions/electron-browser/menusExtensionPoint'; +import 'vs/workbench/electron-browser/shell.contribution'; +import 'vs/workbench/browser/workbench.contribution'; -// Views +import 'vs/workbench/electron-browser/main'; + +//#endregion + + +//#region --- workbench actions + +import 'vs/workbench/browser/actions/layoutActions'; +import 'vs/workbench/browser/actions/listCommands'; +import 'vs/workbench/browser/actions/navigationActions'; +import 'vs/workbench/browser/parts/quickopen/quickopenActions'; +import 'vs/workbench/browser/parts/quickinput/quickInputActions'; + +//#endregion + + +//#region --- API Extension Points + +import 'vs/workbench/api/common/menusExtensionPoint'; +import 'vs/workbench/api/common/configurationExtensionPoint'; import 'vs/workbench/api/browser/viewsExtensionPoint'; +//#endregion + + +//#region --- workbench services + +import 'vs/workbench/services/bulkEdit/electron-browser/bulkEditService'; + +//#endregion + + +//#region --- workbench parts + // Localizations import 'vs/workbench/parts/localizations/electron-browser/localizations.contribution'; -// Workbench -import 'vs/workbench/browser/actions/layoutActions'; -import 'vs/workbench/browser/actions/listCommands'; -import 'vs/workbench/browser/actions/navigationActions'; +// Preferences import 'vs/workbench/parts/preferences/electron-browser/preferences.contribution'; import 'vs/workbench/parts/preferences/browser/keybindingsEditorContribution'; + +// Logs import 'vs/workbench/parts/logs/electron-browser/logs.contribution'; -import 'vs/workbench/browser/parts/quickopen/quickopen.contribution'; +// Quick Open Handlers import 'vs/workbench/parts/quickopen/browser/quickopen.contribution'; -import 'vs/workbench/browser/parts/editor/editorPicker'; -import 'vs/workbench/browser/parts/quickinput/quickInput.contribution'; +// Explorer import 'vs/workbench/parts/files/electron-browser/explorerViewlet'; import 'vs/workbench/parts/files/electron-browser/fileActions.contribution'; import 'vs/workbench/parts/files/electron-browser/files.contribution'; +// Backup import 'vs/workbench/parts/backup/common/backup.contribution'; +// Stats import 'vs/workbench/parts/stats/node/stats.contribution'; +// Rapid Render Splash import 'vs/workbench/parts/splash/electron-browser/partsSplash.contribution'; +// Search import 'vs/workbench/parts/search/electron-browser/search.contribution'; import 'vs/workbench/parts/search/browser/searchView'; import 'vs/workbench/parts/search/browser/openAnythingHandler'; +// SCM import 'vs/workbench/parts/scm/electron-browser/scm.contribution'; import 'vs/workbench/parts/scm/electron-browser/scmViewlet'; +// Debug import 'vs/workbench/parts/debug/electron-browser/debug.contribution'; import 'vs/workbench/parts/debug/browser/debugQuickOpen'; import 'vs/workbench/parts/debug/electron-browser/repl'; import 'vs/workbench/parts/debug/browser/debugViewlet'; +// Markers import 'vs/workbench/parts/markers/electron-browser/markers.contribution'; + +// Comments import 'vs/workbench/parts/comments/electron-browser/comments.contribution'; +// HTML Preview import 'vs/workbench/parts/html/electron-browser/html.contribution'; +// URL Support import 'vs/workbench/parts/url/electron-browser/url.contribution'; + +// Webview import 'vs/workbench/parts/webview/electron-browser/webview.contribution'; -import 'vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution'; - +// Extensions Management import 'vs/workbench/parts/extensions/electron-browser/extensions.contribution'; import 'vs/workbench/parts/extensions/browser/extensionsQuickOpen'; import 'vs/workbench/parts/extensions/electron-browser/extensionsViewlet'; -import 'vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution'; - +// Output Panel import 'vs/workbench/parts/output/electron-browser/output.contribution'; import 'vs/workbench/parts/output/browser/outputPanel'; +// Terminal import 'vs/workbench/parts/terminal/electron-browser/terminal.contribution'; import 'vs/workbench/parts/terminal/browser/terminalQuickOpen'; import 'vs/workbench/parts/terminal/electron-browser/terminalPanel'; -import 'vs/workbench/electron-browser/workbench'; - +// Relauncher import 'vs/workbench/parts/relauncher/electron-browser/relauncher.contribution'; +// Tasks import 'vs/workbench/parts/tasks/electron-browser/task.contribution'; +// Emmet import 'vs/workbench/parts/emmet/browser/emmet.browser.contribution'; import 'vs/workbench/parts/emmet/electron-browser/emmet.contribution'; -import 'vs/workbench/parts/codeEditor/codeEditor.contribution'; +// CodeEditor Contributions +import 'vs/workbench/parts/codeEditor/electron-browser/codeEditor.contribution'; +// Execution import 'vs/workbench/parts/execution/electron-browser/execution.contribution'; +// Snippets import 'vs/workbench/parts/snippets/electron-browser/snippets.contribution'; import 'vs/workbench/parts/snippets/electron-browser/snippetsService'; import 'vs/workbench/parts/snippets/electron-browser/insertSnippet'; import 'vs/workbench/parts/snippets/electron-browser/configureSnippets'; import 'vs/workbench/parts/snippets/electron-browser/tabCompletion'; -import 'vs/workbench/parts/themes/electron-browser/themes.contribution'; - +// Send a Smile import 'vs/workbench/parts/feedback/electron-browser/feedback.contribution'; -import 'vs/workbench/parts/welcome/gettingStarted/electron-browser/gettingStarted.contribution'; - +// Update import 'vs/workbench/parts/update/electron-browser/update.contribution'; +// Surveys import 'vs/workbench/parts/surveys/electron-browser/nps.contribution'; import 'vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution'; +// Performance import 'vs/workbench/parts/performance/electron-browser/performance.contribution'; +// CLI import 'vs/workbench/parts/cli/electron-browser/cli.contribution'; -import 'vs/workbench/api/electron-browser/extensionHost.contribution'; - -import 'vs/workbench/electron-browser/main.contribution'; -import 'vs/workbench/electron-browser/main'; - +// Themes Support +import 'vs/workbench/parts/themes/electron-browser/themes.contribution'; import 'vs/workbench/parts/themes/test/electron-browser/themes.test.contribution'; +// Watermark import 'vs/workbench/parts/watermark/electron-browser/watermark'; +// Welcome +import 'vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution'; +import 'vs/workbench/parts/welcome/gettingStarted/electron-browser/gettingStarted.contribution'; import 'vs/workbench/parts/welcome/overlay/browser/welcomeOverlay'; +import 'vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution'; +// Outline import 'vs/workbench/parts/outline/electron-browser/outline.contribution'; -import 'vs/workbench/services/bulkEdit/electron-browser/bulkEditService'; - +// Experiments import 'vs/workbench/parts/experiments/electron-browser/experiments.contribution'; + +//#endregion \ No newline at end of file From 580acc5037d23d86b5d507794addbddadff2fdf3 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Fri, 25 Jan 2019 11:47:48 +0100 Subject: [PATCH 143/274] remove unused code --- src/vs/base/parts/tree/browser/tree.ts | 5 ----- src/vs/base/parts/tree/browser/treeImpl.ts | 7 ------- src/vs/base/parts/tree/browser/treeView.ts | 15 --------------- 3 files changed, 27 deletions(-) diff --git a/src/vs/base/parts/tree/browser/tree.ts b/src/vs/base/parts/tree/browser/tree.ts index 1de72e5670b..a74f5cad56e 100644 --- a/src/vs/base/parts/tree/browser/tree.ts +++ b/src/vs/base/parts/tree/browser/tree.ts @@ -78,11 +78,6 @@ export interface ITree { */ refresh(element?: any, recursive?: boolean): Promise; - /** - * Updates an element's width. - */ - updateWidth(element: any): void; - /** * Expands an element. * The returned promise returns a boolean for whether the element was expanded or not. diff --git a/src/vs/base/parts/tree/browser/treeImpl.ts b/src/vs/base/parts/tree/browser/treeImpl.ts index 652c59d17a0..1b6c0f28da6 100644 --- a/src/vs/base/parts/tree/browser/treeImpl.ts +++ b/src/vs/base/parts/tree/browser/treeImpl.ts @@ -161,13 +161,6 @@ export class Tree implements _.ITree { return this.model.refresh(element, recursive); } - public updateWidth(element: any): void { - const item = this.model.getItem(element); - if (item) { - this.view.updateWidth(item); - } - } - public expand(element: any): Promise { return this.model.expand(element); } diff --git a/src/vs/base/parts/tree/browser/treeView.ts b/src/vs/base/parts/tree/browser/treeView.ts index 110ff2f8e53..fa0a7dcbe2b 100644 --- a/src/vs/base/parts/tree/browser/treeView.ts +++ b/src/vs/base/parts/tree/browser/treeView.ts @@ -1043,21 +1043,6 @@ export class TreeView extends HeightMap { } } - public updateWidth(item: Model.Item): void { - if (!item || !item.isVisible()) { - return; - } - - const viewItem = this.items[item.id]; - - if (!viewItem) { - return; - } - - viewItem.updateWidth(); - this.updateScrollWidth(); - } - public getRelativeTop(item: Model.Item): number { if (item && item.isVisible()) { let viewItem = this.items[item.id]; From 0ed567b7223d2a43d62835b45818bee1234162f0 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Fri, 25 Jan 2019 16:41:59 +0100 Subject: [PATCH 144/274] list horizontal scrolling, workbench setting --- src/vs/base/browser/ui/list/list.css | 5 + src/vs/base/browser/ui/list/listPaging.ts | 4 +- src/vs/base/browser/ui/list/listView.ts | 125 +++++++++++++++--- src/vs/base/browser/ui/list/listWidget.ts | 9 +- src/vs/base/browser/ui/splitview/panelview.ts | 22 ++- src/vs/base/browser/ui/tree/abstractTree.ts | 8 +- src/vs/base/browser/ui/tree/asyncDataTree.ts | 4 +- .../referenceSearch/referencesWidget.ts | 2 +- src/vs/platform/list/browser/listService.ts | 33 +++-- .../parts/notifications/notificationsList.ts | 3 +- .../parts/quickinput/quickInputList.ts | 3 +- .../browser/parts/views/panelViewlet.ts | 2 +- .../parts/debug/browser/breakpointsView.ts | 4 +- .../parts/debug/browser/loadedScriptsView.ts | 4 +- .../debug/electron-browser/callStackView.ts | 4 +- .../debug/electron-browser/debugHover.ts | 3 +- .../parts/debug/electron-browser/repl.ts | 2 +- .../debug/electron-browser/variablesView.ts | 4 +- .../electron-browser/watchExpressionsView.ts | 4 +- .../electron-browser/extensionsViews.ts | 9 +- .../runtimeExtensionsEditor.ts | 3 +- .../electron-browser/views/explorerView.ts | 4 +- .../electron-browser/views/openEditorsView.ts | 4 +- .../markers/electron-browser/markersPanel.ts | 2 +- .../preferences/browser/keybindingsEditor.ts | 7 +- .../electron-browser/settingsEditor2.ts | 3 +- .../parts/scm/electron-browser/scmViewlet.ts | 7 +- .../parts/search/browser/searchView.ts | 2 +- 28 files changed, 204 insertions(+), 82 deletions(-) diff --git a/src/vs/base/browser/ui/list/list.css b/src/vs/base/browser/ui/list/list.css index 88e9c43e78b..666bdaa9167 100644 --- a/src/vs/base/browser/ui/list/list.css +++ b/src/vs/base/browser/ui/list/list.css @@ -29,6 +29,11 @@ height: 100%; } +.monaco-list.horizontal-scrolling .monaco-list-rows { + width: auto; + min-width: 100%; +} + .monaco-list-row { position: absolute; -moz-box-sizing: border-box; diff --git a/src/vs/base/browser/ui/list/listPaging.ts b/src/vs/base/browser/ui/list/listPaging.ts index 51129e1bd97..1acd51e015f 100644 --- a/src/vs/base/browser/ui/list/listPaging.ts +++ b/src/vs/base/browser/ui/list/listPaging.ts @@ -190,8 +190,8 @@ export class PagedList implements IDisposable { return this.list.getSelection(); } - layout(height?: number): void { - this.list.layout(height); + layout(height?: number, width?: number): void { + this.list.layout(height, width); } reveal(index: number, relativeTop?: number): void { diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 701ab69ceb2..056bfa2b29f 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -21,7 +21,7 @@ import { memoize } from 'vs/base/common/decorators'; import { Range, IRange } from 'vs/base/common/range'; import { equals, distinct } from 'vs/base/common/arrays'; import { DataTransfers, StaticDND, IDragAndDropData } from 'vs/base/browser/dnd'; -import { disposableTimeout } from 'vs/base/common/async'; +import { disposableTimeout, Delayer } from 'vs/base/common/async'; interface IItem { readonly id: string; @@ -29,8 +29,9 @@ interface IItem { readonly templateId: string; row: IRow | null; size: number; + width: number | undefined; hasDynamicHeight: boolean; - renderWidth: number | undefined; + lastDynamicHeightWidth: number | undefined; uri: string | undefined; dropTarget: boolean; dragStartDisposable: IDisposable; @@ -47,6 +48,7 @@ export interface IListViewOptions { readonly setRowLineHeight?: boolean; readonly supportDynamicHeights?: boolean; readonly mouseSupport?: boolean; + readonly horizontalScrolling?: boolean; } const DefaultOptions = { @@ -60,7 +62,8 @@ const DefaultOptions = { onDragStart(): void { }, onDragOver() { return false; }, drop() { } - } + }, + horizontalScrolling: false }; export class ElementsDragAndDropData implements IDragAndDropData { @@ -154,12 +157,14 @@ export class ListView implements ISpliceable, IDisposable { private scrollableElement: ScrollableElement; private _scrollHeight: number; private scrollableElementUpdateDisposable: IDisposable | null = null; + private scrollableElementWidthDelayer = new Delayer(50); private splicing = false; private dragOverAnimationDisposable: IDisposable | undefined; private dragOverAnimationStopDisposable: IDisposable = Disposable.None; private dragOverMouseY: number; private setRowLineHeight: boolean; private supportDynamicHeights: boolean; + private horizontalScrolling: boolean; private canUseTranslate3d: boolean | undefined = undefined; private dnd: IListViewDragAndDrop; @@ -189,6 +194,10 @@ export class ListView implements ISpliceable, IDisposable { renderers: IListRenderer[], options: IListViewOptions = DefaultOptions ) { + if (options.horizontalScrolling && options.supportDynamicHeights) { + throw new Error('Horizontal scrolling and dynamic heights not supported simultaneously'); + } + this.items = []; this.itemId = 0; this.rangeMap = new RangeMap(); @@ -206,13 +215,16 @@ export class ListView implements ISpliceable, IDisposable { this.domNode.className = 'monaco-list'; DOM.toggleClass(this.domNode, 'mouse-support', typeof options.mouseSupport === 'boolean' ? options.mouseSupport : true); + this.horizontalScrolling = getOrDefault(options, o => o.horizontalScrolling, DefaultOptions.horizontalScrolling); + DOM.toggleClass(this.domNode, 'horizontal-scrolling', this.horizontalScrolling); + this.rowsContainer = document.createElement('div'); this.rowsContainer.className = 'monaco-list-rows'; Gesture.addTarget(this.rowsContainer); this.scrollableElement = new ScrollableElement(this.rowsContainer, { alwaysConsumeMouseWheel: true, - horizontal: ScrollbarVisibility.Hidden, + horizontal: this.horizontalScrolling ? ScrollbarVisibility.Auto : ScrollbarVisibility.Hidden, vertical: getOrDefault(options, o => o.verticalScrollMode, DefaultOptions.verticalScrollMode), useShadows: getOrDefault(options, o => o.useShadows, DefaultOptions.useShadows) }); @@ -275,8 +287,9 @@ export class ListView implements ISpliceable, IDisposable { element, templateId: this.virtualDelegate.getTemplateId(element), size: this.virtualDelegate.getHeight(element), + width: undefined, hasDynamicHeight: !!this.virtualDelegate.hasDynamicHeight && this.virtualDelegate.hasDynamicHeight(element), - renderWidth: undefined, + lastDynamicHeightWidth: undefined, row: null, uri: undefined, dropTarget: false, @@ -324,7 +337,7 @@ export class ListView implements ISpliceable, IDisposable { } } - this.updateScrollHeight(); + this.eventuallyUpdateScrollDimensions(); if (this.supportDynamicHeights) { this.rerender(this.scrollTop, this.renderHeight); @@ -333,18 +346,47 @@ export class ListView implements ISpliceable, IDisposable { return deleted.map(i => i.element); } - private updateScrollHeight(): void { + private eventuallyUpdateScrollDimensions(): void { this._scrollHeight = this.contentHeight; this.rowsContainer.style.height = `${this._scrollHeight}px`; if (!this.scrollableElementUpdateDisposable) { this.scrollableElementUpdateDisposable = DOM.scheduleAtNextAnimationFrame(() => { this.scrollableElement.setScrollDimensions({ scrollHeight: this._scrollHeight }); + this.updateScrollWidth(); this.scrollableElementUpdateDisposable = null; }); } } + private eventuallyUpdateScrollWidth(): void { + if (!this.horizontalScrolling) { + return; + } + + this.scrollableElementWidthDelayer.trigger(() => this.updateScrollWidth()); + } + + private updateScrollWidth(): void { + if (!this.horizontalScrolling) { + return; + } + + if (this.items.length === 0) { + this.scrollableElement.setScrollDimensions({ scrollWidth: 0 }); + } + + let scrollWidth = 0; + + for (const item of this.items) { + if (typeof item.width !== 'undefined') { + scrollWidth = Math.max(scrollWidth, item.width); + } + } + + this.scrollableElement.setScrollDimensions({ scrollWidth: scrollWidth + 10 }); + } + get length(): number { return this.items.length; } @@ -379,7 +421,7 @@ export class ListView implements ISpliceable, IDisposable { return this.rangeMap.indexAfter(position); } - layout(height?: number): void { + layout(height?: number, width?: number): void { let scrollDimensions: INewScrollDimensions = { height: height || DOM.getContentHeight(this.domNode) }; @@ -391,19 +433,25 @@ export class ListView implements ISpliceable, IDisposable { } this.scrollableElement.setScrollDimensions(scrollDimensions); - } - layoutWidth(width: number): void { - this.renderWidth = width; + if (typeof width !== 'undefined') { + this.renderWidth = width; - if (this.supportDynamicHeights) { - this.rerender(this.scrollTop, this.renderHeight); + if (this.supportDynamicHeights) { + this.rerender(this.scrollTop, this.renderHeight); + } + + if (this.horizontalScrolling) { + this.scrollableElement.setScrollDimensions({ + width: width || DOM.getContentWidth(this.domNode) + }); + } } } // Render - private render(renderTop: number, renderHeight: number): void { + private render(renderTop: number, renderHeight: number, renderLeft: number, scrollWidth: number): void { const previousRenderRange = this.getRenderRange(this.lastRenderTop, this.lastRenderHeight); const renderRange = this.getRenderRange(renderTop, renderHeight); @@ -426,14 +474,16 @@ export class ListView implements ISpliceable, IDisposable { const canUseTranslate3d = !isWindows && !browser.isFirefox && browser.getZoomLevel() === 0; if (canUseTranslate3d) { - const transform = `translate3d(0px, -${renderTop}px, 0px)`; + const transform = `translate3d(-${renderLeft}px, -${renderTop}px, 0px)`; this.rowsContainer.style.transform = transform; this.rowsContainer.style.webkitTransform = transform; if (canUseTranslate3d !== this.canUseTranslate3d) { + this.rowsContainer.style.left = '0'; this.rowsContainer.style.top = '0'; } } else { + this.rowsContainer.style.left = `-${renderLeft}px`; this.rowsContainer.style.top = `-${renderTop}px`; if (canUseTranslate3d !== this.canUseTranslate3d) { @@ -442,7 +492,12 @@ export class ListView implements ISpliceable, IDisposable { } } + if (this.horizontalScrolling) { + this.rowsContainer.style.width = `${Math.max(scrollWidth, this.renderWidth)}px`; + } + this.canUseTranslate3d = canUseTranslate3d; + this.lastRenderTop = renderTop; this.lastRenderHeight = renderHeight; } @@ -467,10 +522,34 @@ export class ListView implements ISpliceable, IDisposable { this.updateItemInDOM(item, index); const renderer = this.renderers.get(item.templateId); + + if (!renderer) { + throw new Error(`No renderer found for template id ${item.templateId}`); + } + + if (this.horizontalScrolling) { + item.row.domNode!.style.width = 'fit-content'; + } + if (renderer) { renderer.renderElement(item.element, index, item.row.templateData); } + if (this.horizontalScrolling) { + item.width = DOM.getContentWidth(item.row.domNode!); + const style = window.getComputedStyle(item.row.domNode!); + + if (style.paddingLeft) { + item.width += parseFloat(style.paddingLeft); + } + + if (style.paddingRight) { + item.width += parseFloat(style.paddingRight); + } + + item.row.domNode!.style.width = ''; + } + const uri = this.dnd.getDragURI(item.element); item.dragStartDisposable.dispose(); @@ -479,6 +558,10 @@ export class ListView implements ISpliceable, IDisposable { const onDragStart = domEvent(item.row.domNode!, 'dragstart'); item.dragStartDisposable = onDragStart(event => this.onDragStart(item.element, uri, event)); } + + if (this.horizontalScrolling) { + this.eventuallyUpdateScrollWidth(); + } } private updateItemInDOM(item: IItem, index: number): void { @@ -507,6 +590,10 @@ export class ListView implements ISpliceable, IDisposable { this.cache.release(item.row!); item.row = null; + + if (this.horizontalScrolling) { + this.eventuallyUpdateScrollWidth(); + } } getScrollTop(): number { @@ -580,7 +667,7 @@ export class ListView implements ISpliceable, IDisposable { private onScroll(e: ScrollEvent): void { try { - this.render(e.scrollTop, e.height); + this.render(e.scrollTop, e.height, e.scrollLeft, e.scrollWidth); if (this.supportDynamicHeights) { this.rerender(e.scrollTop, e.height); @@ -879,7 +966,7 @@ export class ListView implements ISpliceable, IDisposable { if (!didChange) { if (heightDiff !== 0) { - this.updateScrollHeight(); + this.eventuallyUpdateScrollDimensions(); } const unrenderRanges = Range.relativeComplement(previousRenderRange, renderRange); @@ -922,7 +1009,7 @@ export class ListView implements ISpliceable, IDisposable { private probeDynamicHeight(index: number): number { const item = this.items[index]; - if (!item.hasDynamicHeight || item.renderWidth === this.renderWidth) { + if (!item.hasDynamicHeight || item.lastDynamicHeightWidth === this.renderWidth) { return 0; } @@ -936,7 +1023,7 @@ export class ListView implements ISpliceable, IDisposable { renderer.renderElement(item.element, index, row.templateData); } item.size = row.domNode!.offsetHeight; - item.renderWidth = this.renderWidth; + item.lastDynamicHeightWidth = this.renderWidth; this.rowsContainer.removeChild(row.domNode!); this.cache.release(row); diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 13daf5d8072..cd4ad77d3bb 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -798,6 +798,7 @@ export interface IListOptions extends IListStyles { readonly setRowLineHeight?: boolean; readonly supportDynamicHeights?: boolean; readonly mouseSupport?: boolean; + readonly horizontalScrolling?: boolean; } export interface IListStyles { @@ -1269,12 +1270,8 @@ export class List implements ISpliceable, IDisposable { this.view.domNode.focus(); } - layout(height?: number): void { - this.view.layout(height); - } - - layoutWidth(width: number): void { - this.view.layoutWidth(width); + layout(height?: number, width?: number): void { + this.view.layout(height, width); } setSelection(indexes: number[], browserEvent?: UIEvent): void { diff --git a/src/vs/base/browser/ui/splitview/panelview.ts b/src/vs/base/browser/ui/splitview/panelview.ts index a78bb77de5e..18fecbbaafa 100644 --- a/src/vs/base/browser/ui/splitview/panelview.ts +++ b/src/vs/base/browser/ui/splitview/panelview.ts @@ -109,6 +109,8 @@ export abstract class Panel implements IView { return headerSize + maximumBodySize; } + width: number; + constructor(options: IPanelOptions = {}) { this._expanded = typeof options.expanded === 'undefined' ? true : !!options.expanded; this.ariaHeaderLabel = options.ariaHeaderLabel || ''; @@ -188,12 +190,12 @@ export abstract class Panel implements IView { this.renderBody(body); } - layout(size: number): void { + layout(height: number): void { const headerSize = this.headerVisible ? Panel.HEADER_SIZE : 0; if (this.isExpanded()) { - this.layoutBody(size - headerSize); - this.expandedSize = size; + this.layoutBody(height - headerSize, this.width); + this.expandedSize = height; } } @@ -224,7 +226,7 @@ export abstract class Panel implements IView { protected abstract renderHeader(container: HTMLElement): void; protected abstract renderBody(container: HTMLElement): void; - protected abstract layoutBody(size: number): void; + protected abstract layoutBody(height: number, width: number): void; dispose(): void { this.disposables = dispose(this.disposables); @@ -369,6 +371,7 @@ export class PanelView extends Disposable { private dndContext: IDndContext = { draggable: null }; private el: HTMLElement; private panelItems: IPanelItem[] = []; + private width: number; private splitview: SplitView; private animationTimer: number | null = null; @@ -398,6 +401,7 @@ export class PanelView extends Disposable { const panelItem = { panel, disposable: combinedDisposable(disposables) }; this.panelItems.splice(index, 0, panelItem); + panel.width = this.width; this.splitview.addView(panel, size, index); if (this.dnd) { @@ -453,8 +457,14 @@ export class PanelView extends Disposable { return this.splitview.getViewSize(index); } - layout(size: number): void { - this.splitview.layout(size); + layout(height: number, width: number): void { + this.width = width; + + for (const panelItem of this.panelItems) { + panelItem.panel.width = width; + } + + this.splitview.layout(height); } private setupAnimation(): void { diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 2b5bc0486cc..1e2cf248eb2 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -766,12 +766,8 @@ export abstract class AbstractTree implements IDisposable return this.getHTMLElement() === document.activeElement; } - layout(height?: number): void { - this.view.layout(height); - } - - layoutWidth(width: number): void { - this.view.layoutWidth(width); + layout(height?: number, width?: number): void { + this.view.layout(height, width); } style(styles: IListStyles): void { diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index b982e323d45..4100092ef3d 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -348,8 +348,8 @@ export class AsyncDataTree implements IDisposable this.tree.domFocus(); } - layout(height?: number): void { - this.tree.layout(height); + layout(height?: number, width?: number): void { + this.tree.layout(height, width); } style(styles: IListStyles): void { diff --git a/src/vs/editor/contrib/referenceSearch/referencesWidget.ts b/src/vs/editor/contrib/referenceSearch/referencesWidget.ts index a5e63476dbb..1b4bfd87b19 100644 --- a/src/vs/editor/contrib/referenceSearch/referencesWidget.ts +++ b/src/vs/editor/contrib/referenceSearch/referencesWidget.ts @@ -427,7 +427,7 @@ export class ReferenceWidget extends PeekViewWidget { this._treeContainer.style.height = height; this._treeContainer.style.width = right; // forward - this._tree.layout(heightInPixel); + this._tree.layout(heightInPixel, widthInPixel); this._preview.layout(); // store layout data diff --git a/src/vs/platform/list/browser/listService.ts b/src/vs/platform/list/browser/listService.ts index 9a1548a49cd..f19f80ebeb2 100644 --- a/src/vs/platform/list/browser/listService.ts +++ b/src/vs/platform/list/browser/listService.ts @@ -217,6 +217,7 @@ function handleTreeController(configuration: ITreeConfiguration, instantiationSe export class WorkbenchList extends List { readonly contextKeyService: IContextKeyService; + private readonly configurationService: IConfigurationService; private listHasSelectionOrFocus: IContextKey; private listDoubleSelection: IContextKey; @@ -232,19 +233,23 @@ export class WorkbenchList extends List { @IContextKeyService contextKeyService: IContextKeyService, @IListService listService: IListService, @IThemeService themeService: IThemeService, - @IConfigurationService private readonly configurationService: IConfigurationService, + @IConfigurationService configurationService: IConfigurationService, @IKeybindingService keybindingService: IKeybindingService ) { + const horizontalScrolling = typeof options.horizontalScrolling !== 'undefined' ? options.horizontalScrolling : configurationService.getValue(horizontalScrollingKey); + super(container, delegate, renderers, { keyboardSupport: false, styleController: new DefaultStyleController(getSharedListStyleSheet()), ...computeStyles(themeService.getTheme(), defaultListStyles), - ...toWorkbenchListOptions(options, configurationService, keybindingService) + ...toWorkbenchListOptions(options, configurationService, keybindingService), + horizontalScrolling } as IListOptions ); this.contextKeyService = createScopedContextKeyService(contextKeyService, this); + this.configurationService = configurationService; const listSupportsMultiSelect = WorkbenchListSupportsMultiSelectContextKey.bindTo(this.contextKeyService); listSupportsMultiSelect.set(!(options.multipleSelectionSupport === false)); @@ -294,8 +299,9 @@ export class WorkbenchList extends List { export class WorkbenchPagedList extends PagedList { readonly contextKeyService: IContextKeyService; + private readonly configurationService: IConfigurationService; - private disposables: IDisposable[] = []; + private disposables: IDisposable[]; private _useAltAsMultipleSelectionModifier: boolean; @@ -307,19 +313,24 @@ export class WorkbenchPagedList extends PagedList { @IContextKeyService contextKeyService: IContextKeyService, @IListService listService: IListService, @IThemeService themeService: IThemeService, - @IConfigurationService private readonly configurationService: IConfigurationService, + @IConfigurationService configurationService: IConfigurationService, @IKeybindingService keybindingService: IKeybindingService ) { + const horizontalScrolling = typeof options.horizontalScrolling !== 'undefined' ? options.horizontalScrolling : configurationService.getValue(horizontalScrollingKey); super(container, delegate, renderers, { keyboardSupport: false, styleController: new DefaultStyleController(getSharedListStyleSheet()), ...computeStyles(themeService.getTheme(), defaultListStyles), - ...toWorkbenchListOptions(options, configurationService, keybindingService) + ...toWorkbenchListOptions(options, configurationService, keybindingService), + horizontalScrolling } as IListOptions ); + this.disposables = []; + this.contextKeyService = createScopedContextKeyService(contextKeyService, this); + this.configurationService = configurationService; const listSupportsMultiSelect = WorkbenchListSupportsMultiSelectContextKey.bindTo(this.contextKeyService); listSupportsMultiSelect.set(!(options.multipleSelectionSupport === false)); @@ -907,6 +918,7 @@ export class WorkbenchObjectTree, TFilterData = void> @IKeybindingService keybindingService: IKeybindingService ) { const keyboardNavigation = configurationService.getValue(keyboardNavigationSettingKey); + const horizontalScrolling = typeof options.horizontalScrolling !== 'undefined' ? options.horizontalScrolling : configurationService.getValue(horizontalScrollingKey); super(container, delegate, renderers, { keyboardSupport: false, @@ -915,7 +927,8 @@ export class WorkbenchObjectTree, TFilterData = void> ...toWorkbenchListOptions(options, configurationService, keybindingService), indent: configurationService.getValue(treeIndentKey), simpleKeyboardNavigation: keyboardNavigation === 'simple', - filterOnType: keyboardNavigation === 'filter' + filterOnType: keyboardNavigation === 'filter', + horizontalScrolling }); this.contextKeyService = createScopedContextKeyService(contextKeyService, this); @@ -999,6 +1012,7 @@ export class WorkbenchDataTree extends DataTree(keyboardNavigationSettingKey); + const horizontalScrolling = typeof options.horizontalScrolling !== 'undefined' ? options.horizontalScrolling : configurationService.getValue(horizontalScrollingKey); super(container, delegate, renderers, dataSource, { keyboardSupport: false, @@ -1007,7 +1021,8 @@ export class WorkbenchDataTree extends DataTree extends Async @IKeybindingService keybindingService: IKeybindingService ) { const keyboardNavigation = configurationService.getValue(keyboardNavigationSettingKey); + const horizontalScrolling = typeof options.horizontalScrolling !== 'undefined' ? options.horizontalScrolling : configurationService.getValue(horizontalScrollingKey); super(container, delegate, renderers, dataSource, { keyboardSupport: false, @@ -1094,7 +1110,8 @@ export class WorkbenchAsyncDataTree extends Async ...toWorkbenchListOptions(options, configurationService, keybindingService), indent: configurationService.getValue(treeIndentKey), simpleKeyboardNavigation: keyboardNavigation === 'simple', - filterOnType: keyboardNavigation === 'filter' + filterOnType: keyboardNavigation === 'filter', + horizontalScrolling }); this.contextKeyService = createScopedContextKeyService(contextKeyService, this); diff --git a/src/vs/workbench/browser/parts/notifications/notificationsList.ts b/src/vs/workbench/browser/parts/notifications/notificationsList.ts index 08669edfbf3..fd26a6fa1d6 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsList.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsList.ts @@ -77,7 +77,8 @@ export class NotificationsList extends Themable { [renderer], { ...this.options, - setRowLineHeight: false + setRowLineHeight: false, + horizontalScrolling: false } )); diff --git a/src/vs/workbench/browser/parts/quickinput/quickInputList.ts b/src/vs/workbench/browser/parts/quickinput/quickInputList.ts index 5d8f7aceeae..f24bb0bcc09 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInputList.ts +++ b/src/vs/workbench/browser/parts/quickinput/quickInputList.ts @@ -248,7 +248,8 @@ export class QuickInputList { identityProvider: { getId: element => element.saneLabel }, openController: { shouldOpen: () => false }, // Workaround #58124 setRowLineHeight: false, - multipleSelectionSupport: false + multipleSelectionSupport: false, + horizontalScrolling: false } as IListOptions) as WorkbenchList; this.list.getHTMLElement().id = id; this.disposables.push(this.list); diff --git a/src/vs/workbench/browser/parts/views/panelViewlet.ts b/src/vs/workbench/browser/parts/views/panelViewlet.ts index f69ee245d69..8622e66392e 100644 --- a/src/vs/workbench/browser/parts/views/panelViewlet.ts +++ b/src/vs/workbench/browser/parts/views/panelViewlet.ts @@ -305,7 +305,7 @@ export class PanelViewlet extends Viewlet { } layout(dimension: Dimension): void { - this.panelview.layout(dimension.height); + this.panelview.layout(dimension.height, dimension.width); } getOptimalWidth(): number { diff --git a/src/vs/workbench/parts/debug/browser/breakpointsView.ts b/src/vs/workbench/parts/debug/browser/breakpointsView.ts index 1c572502b27..7361a91dbb4 100644 --- a/src/vs/workbench/parts/debug/browser/breakpointsView.ts +++ b/src/vs/workbench/parts/debug/browser/breakpointsView.ts @@ -134,9 +134,9 @@ export class BreakpointsView extends ViewletPanel { } } - protected layoutBody(size: number): void { + protected layoutBody(height: number, width: number): void { if (this.list) { - this.list.layout(size); + this.list.layout(height, width); } } diff --git a/src/vs/workbench/parts/debug/browser/loadedScriptsView.ts b/src/vs/workbench/parts/debug/browser/loadedScriptsView.ts index c8584cb4ef3..fabd77cd8c0 100644 --- a/src/vs/workbench/parts/debug/browser/loadedScriptsView.ts +++ b/src/vs/workbench/parts/debug/browser/loadedScriptsView.ts @@ -500,8 +500,8 @@ export class LoadedScriptsView extends ViewletPanel { })); } - layoutBody(size: number): void { - this.tree.layout(size); + layoutBody(height: number, width: number): void { + this.tree.layout(height, width); } dispose(): void { diff --git a/src/vs/workbench/parts/debug/electron-browser/callStackView.ts b/src/vs/workbench/parts/debug/electron-browser/callStackView.ts index 06797efabb7..38c16c1817c 100644 --- a/src/vs/workbench/parts/debug/electron-browser/callStackView.ts +++ b/src/vs/workbench/parts/debug/electron-browser/callStackView.ts @@ -223,8 +223,8 @@ export class CallStackView extends ViewletPanel { })); } - layoutBody(size: number): void { - this.tree.layout(size); + layoutBody(height: number, width: number): void { + this.tree.layout(height, width); } private updateTreeSelection(): void { diff --git a/src/vs/workbench/parts/debug/electron-browser/debugHover.ts b/src/vs/workbench/parts/debug/electron-browser/debugHover.ts index 3cc5eabfdbe..b07b5f08469 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugHover.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugHover.ts @@ -85,7 +85,8 @@ export class DebugHoverWidget implements IContentWidget { this.dataSource, { ariaLabel: nls.localize('treeAriaLabel', "Debug Hover"), accessibilityProvider: new DebugHoverAccessibilityProvider(), - mouseSupport: false + mouseSupport: false, + horizontalScrolling: true }, this.contextKeyService, this.listService, this.themeService, this.configurationService, this.keybindingService); this.valueContainer = $('.value'); diff --git a/src/vs/workbench/parts/debug/electron-browser/repl.ts b/src/vs/workbench/parts/debug/electron-browser/repl.ts index 5183a792b49..e88a79861c7 100644 --- a/src/vs/workbench/parts/debug/electron-browser/repl.ts +++ b/src/vs/workbench/parts/debug/electron-browser/repl.ts @@ -269,7 +269,7 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati this.replDelegate.setWidth(dimension.width - 25, this.characterWidth); const treeHeight = dimension.height - this.replInputHeight; this.treeContainer.style.height = `${treeHeight}px`; - this.tree.layout(treeHeight); + this.tree.layout(treeHeight, dimension.width); } this.replInputContainer.style.height = `${this.replInputHeight}px`; diff --git a/src/vs/workbench/parts/debug/electron-browser/variablesView.ts b/src/vs/workbench/parts/debug/electron-browser/variablesView.ts index a1c4b9b5666..21c171270ca 100644 --- a/src/vs/workbench/parts/debug/electron-browser/variablesView.ts +++ b/src/vs/workbench/parts/debug/electron-browser/variablesView.ts @@ -117,8 +117,8 @@ export class VariablesView extends ViewletPanel { })); } - layoutBody(size: number): void { - this.tree.layout(size); + layoutBody(width: number, height: number): void { + this.tree.layout(width, height); } private onMouseDblClick(e: ITreeMouseEvent): void { diff --git a/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts b/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts index 409b49ad7ff..7b344d0abbf 100644 --- a/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts +++ b/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts @@ -113,8 +113,8 @@ export class WatchExpressionsView extends ViewletPanel { })); } - layoutBody(size: number): void { - this.tree.layout(size); + layoutBody(height: number, width: number): void { + this.tree.layout(height, width); } private onMouseDblClick(e: ITreeMouseEvent): void { diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts index d28726e3bb0..58e70c850e1 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts @@ -110,7 +110,8 @@ export class ExtensionsListView extends ViewletPanel { this.list = this.instantiationService.createInstance(WorkbenchPagedList, this.extensionsList, delegate, [renderer], { ariaLabel: localize('extensions', "Extensions"), multipleSelectionSupport: false, - setRowLineHeight: false + setRowLineHeight: false, + horizontalScrolling: false }) as WorkbenchPagedList; this.list.onContextMenu(e => this.onContextMenu(e), this, this.disposables); this.list.onFocusChange(e => extensionsViewState.onFocusChange(e.elements), this, this.disposables); @@ -128,9 +129,9 @@ export class ExtensionsListView extends ViewletPanel { .on(this.pin, this, this.disposables); } - layoutBody(size: number): void { - this.extensionsList.style.height = size + 'px'; - this.list.layout(size); + protected layoutBody(height: number, width: number): void { + this.extensionsList.style.height = height + 'px'; + this.list.layout(height, width); } async show(query: string): Promise> { diff --git a/src/vs/workbench/parts/extensions/electron-browser/runtimeExtensionsEditor.ts b/src/vs/workbench/parts/extensions/electron-browser/runtimeExtensionsEditor.ts index 4642d292181..d8b3b30fe8b 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/runtimeExtensionsEditor.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/runtimeExtensionsEditor.ts @@ -398,7 +398,8 @@ export class RuntimeExtensionsEditor extends BaseEditor { this._list = this._instantiationService.createInstance(WorkbenchList, parent, delegate, [renderer], { multipleSelectionSupport: false, - setRowLineHeight: false + setRowLineHeight: false, + horizontalScrolling: false }) as WorkbenchList; this._list.splice(0, this._list.length, this._elements); diff --git a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts index 46360bd1759..7bf3b826f07 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -147,8 +147,8 @@ export class ExplorerView extends ViewletPanel { setHeader(); } - protected layoutBody(size: number): void { - this.tree.layout(size); + protected layoutBody(height: number, width: number): void { + this.tree.layout(height, width); } renderBody(container: HTMLElement): void { diff --git a/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts b/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts index ab9376bce14..06474606ccd 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts @@ -307,9 +307,9 @@ export class OpenEditorsView extends ViewletPanel { return this.list; } - protected layoutBody(size: number): void { + protected layoutBody(height: number, width: number): void { if (this.list) { - this.list.layout(size); + this.list.layout(height, width); } } diff --git a/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts b/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts index 61e67c71b45..a87ac486e8c 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts @@ -151,7 +151,7 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { public layout(dimension: dom.Dimension): void { this.treeContainer.style.height = `${dimension.height}px`; - this.tree.layout(dimension.height); + this.tree.layout(dimension.height, dimension.width); if (this.filterInputActionItem) { this.filterInputActionItem.toggleLayout(dimension.width < 1200); } diff --git a/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts b/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts index 3605aad7566..72e4cd23c34 100644 --- a/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts +++ b/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts @@ -407,7 +407,12 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor private createList(parent: HTMLElement): void { this.keybindingsListContainer = DOM.append(parent, $('.keybindings-list-container')); this.keybindingsList = this._register(this.instantiationService.createInstance(WorkbenchList, this.keybindingsListContainer, new Delegate(), [new KeybindingItemRenderer(this, this.keybindingsService)], - { identityProvider: { getId: e => e.id }, ariaLabel: localize('keybindingsLabel', "Keybindings"), setRowLineHeight: false })) as WorkbenchList; + { + identityProvider: { getId: e => e.id }, + ariaLabel: localize('keybindingsLabel', "Keybindings"), + setRowLineHeight: false, + horizontalScrolling: false + })) as WorkbenchList; this._register(this.keybindingsList.onContextMenu(e => this.onContextMenu(e))); this._register(this.keybindingsList.onFocusChange(e => this.onFocusChange(e))); this._register(this.keybindingsList.onDidFocus(() => { diff --git a/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts b/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts index 5fd660b0669..1eafe1e69c0 100644 --- a/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts +++ b/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts @@ -1189,8 +1189,7 @@ export class SettingsEditor2 extends BaseEditor { const listHeight = dimension.height - (76 + 11 /* header height + padding*/); const settingsTreeHeight = listHeight - 14; this.settingsTreeContainer.style.height = `${settingsTreeHeight}px`; - this.settingsTree.layout(settingsTreeHeight); - this.settingsTree.layoutWidth(dimension.width); + this.settingsTree.layout(settingsTreeHeight, dimension.width); const tocTreeHeight = listHeight - 16; this.tocTreeContainer.style.height = `${tocTreeHeight}px`; diff --git a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts index 19d30856bec..2f7895892bb 100644 --- a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts +++ b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts @@ -724,6 +724,7 @@ function convertValidationType(type: InputValidationType): MessageType { export class RepositoryPanel extends ViewletPanel { private cachedHeight: number | undefined = undefined; + private cachedWidth: number | undefined = undefined; private inputBoxContainer: HTMLElement; private inputBox: InputBox; private listContainer: HTMLElement; @@ -910,7 +911,7 @@ export class RepositoryPanel extends ViewletPanel { } } - layoutBody(height: number = this.cachedHeight): void { + layoutBody(height: number = this.cachedHeight, width: number = this.cachedWidth): void { if (height === undefined) { return; } @@ -924,7 +925,7 @@ export class RepositoryPanel extends ViewletPanel { const editorHeight = this.inputBox.height; const listHeight = height - (editorHeight + 12 /* margin */); this.listContainer.style.height = `${listHeight}px`; - this.list.layout(listHeight); + this.list.layout(listHeight, width); toggleClass(this.inputBoxContainer, 'scroll', editorHeight >= 134); } else { @@ -932,7 +933,7 @@ export class RepositoryPanel extends ViewletPanel { removeClass(this.inputBoxContainer, 'scroll'); this.listContainer.style.height = `${height}px`; - this.list.layout(height); + this.list.layout(height, width); } } diff --git a/src/vs/workbench/parts/search/browser/searchView.ts b/src/vs/workbench/parts/search/browser/searchView.ts index f5077e3bb16..e333d5aea17 100644 --- a/src/vs/workbench/parts/search/browser/searchView.ts +++ b/src/vs/workbench/parts/search/browser/searchView.ts @@ -900,7 +900,7 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { this.resultsElement.style.height = searchResultContainerSize + 'px'; - this.tree.layout(searchResultContainerSize); + this.tree.layout(searchResultContainerSize, this.size.width); } layout(dimension: dom.Dimension): void { From 7efcf23bbd441292e0225c8115d4d0e7869ccd77 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 25 Jan 2019 16:55:02 +0100 Subject: [PATCH 145/274] fix awful formatting --- src/vs/code/electron-main/windows.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 5b0c18ecd87..9c2ff750b69 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -2100,8 +2100,7 @@ class WorkspacesManager { this.historyMainService.addRecentlyOpened([savedWorkspace], []); this.workspacesMainService.deleteUntitledWorkspaceSync(workspace); return false; - }, - () => false); + }, () => false); } return true; // keep veto if no target was provided From 48d48a0388fb0067dd8b9e5fb5cd4b7749a32a41 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Fri, 25 Jan 2019 17:04:51 +0100 Subject: [PATCH 146/274] Make task definition, problem pattern, and problem matcher dynamic --- .../parts/tasks/common/problemMatcher.ts | 30 +++++++++++++++---- .../tasks/common/taskDefinitionRegistry.ts | 15 ++++++++-- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/parts/tasks/common/problemMatcher.ts b/src/vs/workbench/parts/tasks/common/problemMatcher.ts index fcb912367fc..bab1c2e486d 100644 --- a/src/vs/workbench/parts/tasks/common/problemMatcher.ts +++ b/src/vs/workbench/parts/tasks/common/problemMatcher.ts @@ -1092,7 +1092,8 @@ const problemPatternExtPoint = ExtensionsRegistry.registerExtensionPoint((resolve, reject) => { - problemPatternExtPoint.setHandler((extensions) => { + problemPatternExtPoint.setHandler((extensions, delta) => { // We get all statically know extension during startup in one batch try { - extensions.forEach(extension => { + delta.removed.forEach(extension => { + let problemPatterns = extension.value as Config.NamedProblemPatterns; + for (let pattern of problemPatterns) { + if (this.patterns[pattern.name]) { + delete this.patterns[pattern.name]; + } + } + }); + delta.added.forEach(extension => { let problemPatterns = extension.value as Config.NamedProblemPatterns; let parser = new ProblemPatternParser(new ExtensionRegistryReporter(extension.collector)); for (let pattern of problemPatterns) { @@ -1664,7 +1673,8 @@ const problemMatchersExtPoint = ExtensionsRegistry.registerExtensionPoint((resolve, reject) => { - problemMatchersExtPoint.setHandler((extensions) => { + problemMatchersExtPoint.setHandler((extensions, delta) => { try { - extensions.forEach(extension => { + delta.removed.forEach(extension => { + let problemMatchers = extension.value; + for (let matcher of problemMatchers) { + if (this.matchers[matcher.name]) { + delete this.matchers[matcher.name]; + } + } + }); + delta.added.forEach(extension => { let problemMatchers = extension.value; let parser = new ProblemMatcherParser(new ExtensionRegistryReporter(extension.collector)); for (let matcher of problemMatchers) { diff --git a/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.ts b/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.ts index 77200450f4e..d83dd56628d 100644 --- a/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.ts +++ b/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.ts @@ -74,7 +74,8 @@ const taskDefinitionsExtPoint = ExtensionsRegistry.registerExtensionPoint((resolve, reject) => { - taskDefinitionsExtPoint.setHandler((extensions) => { + taskDefinitionsExtPoint.setHandler((extensions, delta) => { try { - for (let extension of extensions) { + for (let extension of delta.removed) { + let taskTypes = extension.value; + for (let taskType of taskTypes) { + if (this.taskTypes && this.taskTypes[taskType.type]) { + delete this.taskTypes[taskType.type]; + } + } + } + for (let extension of delta.added) { let taskTypes = extension.value; for (let taskType of taskTypes) { let type = Configuration.from(taskType, extension.description.identifier, extension.collector); From 04dd87588ee5d28313f0c734c6fa09b8fc1bdbbb Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 25 Jan 2019 17:05:03 +0100 Subject: [PATCH 147/274] debt - fix listener leak in file editor --- src/vs/workbench/browser/parts/editor/baseEditor.ts | 1 + .../parts/files/browser/editors/textFileEditor.ts | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/editor/baseEditor.ts b/src/vs/workbench/browser/parts/editor/baseEditor.ts index c0f00f4acf6..0e2df2423a3 100644 --- a/src/vs/workbench/browser/parts/editor/baseEditor.ts +++ b/src/vs/workbench/browser/parts/editor/baseEditor.ts @@ -118,6 +118,7 @@ export abstract class BaseEditor extends Panel implements IEditor { setVisible(visible: boolean, group?: IEditorGroup): void { super.setVisible(visible); + // Propagate to Editor this.setEditorVisible(visible, group); } diff --git a/src/vs/workbench/parts/files/browser/editors/textFileEditor.ts b/src/vs/workbench/parts/files/browser/editors/textFileEditor.ts index ac1fad843a4..09aaa5edc68 100644 --- a/src/vs/workbench/parts/files/browser/editors/textFileEditor.ts +++ b/src/vs/workbench/parts/files/browser/editors/textFileEditor.ts @@ -30,6 +30,7 @@ import { IEditorGroupsService, IEditorGroup } from 'vs/workbench/services/group/ import { CancellationToken } from 'vs/base/common/cancellation'; import { IEditorGroupView } from 'vs/workbench/browser/parts/editor/editor'; import { createErrorWithActions } from 'vs/base/common/errorsWithActions'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; /** * An implementation of editor for file system resources. @@ -39,6 +40,7 @@ export class TextFileEditor extends BaseTextEditor { static readonly ID = TEXT_FILE_EDITOR_ID; private restoreViewState: boolean; + private groupListener: IDisposable; constructor( @ITelemetryService telemetryService: ITelemetryService, @@ -96,7 +98,8 @@ export class TextFileEditor extends BaseTextEditor { // React to editors closing to preserve or clear view state. This needs to happen // in the onWillCloseEditor because at that time the editor has not yet // been disposed and we can safely persist the view state still as needed. - this._register((group as IEditorGroupView).onWillCloseEditor(e => { + this.groupListener = dispose(this.groupListener); + this.groupListener = ((group as IEditorGroupView).onWillCloseEditor(e => { if (e.editor === this.input) { this.doSaveOrClearTextEditorViewState(this.input); } @@ -276,4 +279,10 @@ export class TextFileEditor extends BaseTextEditor { this.saveTextEditorViewState(input.getResource()); } } + + dispose(): void { + this.groupListener = dispose(this.groupListener); + + super.dispose(); + } } From 95ba24b268123d7e5c05493821e077b58d17ca24 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Fri, 25 Jan 2019 17:12:41 +0100 Subject: [PATCH 148/274] Fix strict null issue in taskDefinitionRegistry.ts --- src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.ts b/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.ts index d83dd56628d..ab8a3a4231e 100644 --- a/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.ts +++ b/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.ts @@ -100,7 +100,7 @@ class TaskDefinitionRegistryImpl implements ITaskDefinitionRegistry { for (let extension of delta.removed) { let taskTypes = extension.value; for (let taskType of taskTypes) { - if (this.taskTypes && this.taskTypes[taskType.type]) { + if (this.taskTypes && taskType.type && this.taskTypes[taskType.type]) { delete this.taskTypes[taskType.type]; } } From b3e5d256a2f3ef66982990fe074239d058a706f0 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 25 Jan 2019 17:19:21 +0100 Subject: [PATCH 149/274] electron 3.0.x: remove window.smoothScrollingWorkaround setting --- src/main.js | 1 - src/vs/code/electron-main/window.ts | 28 ------------------- src/vs/platform/windows/common/windows.ts | 8 +----- .../electron-browser/shell.contribution.ts | 7 ----- .../relauncher.contribution.ts | 9 +----- 5 files changed, 2 insertions(+), 51 deletions(-) diff --git a/src/main.js b/src/main.js index b110c753ce5..5d6dcfca955 100644 --- a/src/main.js +++ b/src/main.js @@ -151,7 +151,6 @@ function onReady() { function configureCommandlineSwitches(cliArgs, nodeCachedDataDir) { // Force pre-Chrome-60 color profile handling (for https://github.com/Microsoft/vscode/issues/51791) - // TODO@Ben check if future versions of Electron still support this flag app.commandLine.appendSwitch('disable-features', 'ColorCorrectRendering'); // Support JS Flags diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index 2546f98ec5b..dbe19cc5743 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -424,34 +424,6 @@ export class CodeWindow extends Disposable implements ICodeWindow { // Handle Workspace events this._register(this.workspacesMainService.onUntitledWorkspaceDeleted(e => this.onUntitledWorkspaceDeleted(e))); - - // TODO@Ben workaround for https://github.com/Microsoft/vscode/issues/13612 - // It looks like smooth scrolling disappears as soon as the window is minimized - // and maximized again. Touching some window properties "fixes" it, like toggling - // the visibility of the menu. - if (isWindows) { - const windowConfig = this.configurationService.getValue('window'); - if (windowConfig && windowConfig.smoothScrollingWorkaround === true) { - let minimized = false; - - const restoreSmoothScrolling = () => { - if (minimized) { - const visibility = this.getMenuBarVisibility(); - const temporaryVisibility: MenuBarVisibility = (visibility === 'hidden' || visibility === 'toggle') ? 'default' : 'hidden'; - setTimeout(() => { - this.doSetMenuBarVisibility(temporaryVisibility); - this.doSetMenuBarVisibility(visibility); - }, 0); - } - - minimized = false; - }; - - this._win.on('minimize', () => minimized = true); - this._win.on('restore', () => restoreSmoothScrolling()); - this._win.on('maximize', () => restoreSmoothScrolling()); - } - } } private onUntitledWorkspaceDeleted(workspace: IWorkspaceIdentifier): void { diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index 649ef74160c..56eaeef6f49 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -6,7 +6,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { Event } from 'vs/base/common/event'; import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry'; -import { IProcessEnvironment, isMacintosh, isWindows } from 'vs/base/common/platform'; +import { IProcessEnvironment, isMacintosh } from 'vs/base/common/platform'; import { ParsedArgs, IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { IRecentlyOpened } from 'vs/platform/history/common/history'; @@ -247,7 +247,6 @@ export interface IWindowSettings { nativeFullScreen: boolean; enableMenuBarMnemonics: boolean; closeWhenEmpty: boolean; - smoothScrollingWorkaround: boolean; clickThroughInactive: boolean; } @@ -270,11 +269,6 @@ export function getTitleBarStyle(configurationService: IConfigurationService, en return 'native'; // simple fullscreen does not work well with custom title style (https://github.com/Microsoft/vscode/issues/63291) } - const smoothScrollingWorkaround = isWindows && configuration.smoothScrollingWorkaround === true; - if (smoothScrollingWorkaround) { - return 'native'; // smooth scrolling workaround does not work with custom title style - } - const style = configuration.titleBarStyle; if (style === 'native') { return 'native'; diff --git a/src/vs/workbench/electron-browser/shell.contribution.ts b/src/vs/workbench/electron-browser/shell.contribution.ts index 55877e94029..bc02c150a70 100644 --- a/src/vs/workbench/electron-browser/shell.contribution.ts +++ b/src/vs/workbench/electron-browser/shell.contribution.ts @@ -620,13 +620,6 @@ configurationRegistry.registerConfiguration({ 'description': nls.localize('window.nativeFullScreen', "Controls if native full-screen should be used on macOS. Disable this option to prevent macOS from creating a new space when going full-screen."), 'included': isMacintosh }, - 'window.smoothScrollingWorkaround': { // TODO@Ben remove once https://github.com/Microsoft/vscode/issues/61824 settles - 'type': 'boolean', - 'default': false, - 'scope': ConfigurationScope.APPLICATION, - 'markdownDescription': nls.localize('window.smoothScrollingWorkaround', "Enable this workaround if scrolling is no longer smooth after restoring a minimized VS Code window. This is a workaround for an issue (https://github.com/Microsoft/vscode/issues/13612) where scrolling starts to lag on devices with precision trackpads like the Surface devices from Microsoft. Enabling this workaround can result in a little bit of layout flickering after restoring the window from minimized state but is otherwise harmless."), - 'included': isWindows - }, 'window.clickThroughInactive': { 'type': 'boolean', 'default': true, diff --git a/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts b/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts index 0fee458eec6..64a3d5341d6 100644 --- a/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts +++ b/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts @@ -15,7 +15,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { RunOnceScheduler } from 'vs/base/common/async'; import { URI } from 'vs/base/common/uri'; import { isEqual } from 'vs/base/common/resources'; -import { isLinux, isMacintosh, isWindows } from 'vs/base/common/platform'; +import { isLinux, isMacintosh } from 'vs/base/common/platform'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { equals } from 'vs/base/common/objects'; @@ -38,7 +38,6 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo private enableCrashReporter: boolean; private touchbarEnabled: boolean; private treeHorizontalScrolling: boolean; - private windowsSmoothScrollingWorkaround: boolean; private experimentalFileWatcher: boolean; private fileWatcherExclude: object; private useGridLayout: boolean; @@ -139,12 +138,6 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo changed = true; } - // Windows: smooth scrolling workaround - if (isWindows && config.window && typeof config.window.smoothScrollingWorkaround === 'boolean' && config.window.smoothScrollingWorkaround !== this.windowsSmoothScrollingWorkaround) { - this.windowsSmoothScrollingWorkaround = config.window.smoothScrollingWorkaround; - changed = true; - } - // Workbench Grid Layout if (config.workbench && typeof config.workbench.useExperimentalGridLayout === 'boolean' && config.workbench.useExperimentalGridLayout !== this.useGridLayout) { this.useGridLayout = config.workbench.useExperimentalGridLayout; From 63fc2e0f7c4ccdcebac26f2ca775c28a1951dddc Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Fri, 25 Jan 2019 17:26:04 +0100 Subject: [PATCH 150/274] tree horizontal scrolling: wrap up debug hover and repl --- src/vs/workbench/parts/debug/browser/media/debugHover.css | 5 ++++- src/vs/workbench/parts/debug/electron-browser/debugHover.ts | 2 +- src/vs/workbench/parts/debug/electron-browser/repl.ts | 3 ++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/parts/debug/browser/media/debugHover.css b/src/vs/workbench/parts/debug/browser/media/debugHover.css index 2f1115e52df..2813a7377e0 100644 --- a/src/vs/workbench/parts/debug/browser/media/debugHover.css +++ b/src/vs/workbench/parts/debug/browser/media/debugHover.css @@ -36,7 +36,6 @@ .monaco-editor .debug-hover-widget .debug-hover-tree .monaco-list-row .monaco-tl-contents { user-select: text; - white-space: pre; } /* Disable tree highlight in debug hover tree. */ @@ -60,6 +59,10 @@ max-height: 500px; } +.monaco-editor .debug-hover-widget .monaco-tl-contents .value { + white-space: nowrap; +} + .monaco-editor .debug-hover-widget .error { color: #E51400; } diff --git a/src/vs/workbench/parts/debug/electron-browser/debugHover.ts b/src/vs/workbench/parts/debug/electron-browser/debugHover.ts index b07b5f08469..061d9a72a1b 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugHover.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugHover.ts @@ -255,7 +255,7 @@ export class DebugHoverWidget implements IContentWidget { private layoutTreeAndContainer(): void { const treeHeight = Math.min(MAX_TREE_HEIGHT, this.tree.visibleNodeCount * 18); this.treeContainer.style.height = `${treeHeight}px`; - this.tree.layout(treeHeight); + this.tree.layout(treeHeight, 324); } hide(): void { diff --git a/src/vs/workbench/parts/debug/electron-browser/repl.ts b/src/vs/workbench/parts/debug/electron-browser/repl.ts index e88a79861c7..45f77ae9975 100644 --- a/src/vs/workbench/parts/debug/electron-browser/repl.ts +++ b/src/vs/workbench/parts/debug/electron-browser/repl.ts @@ -358,7 +358,8 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati accessibilityProvider: new ReplAccessibilityProvider(), identityProvider: { getId: element => element.getId() }, mouseSupport: false, - keyboardNavigationLabelProvider: { getKeyboardNavigationLabel: e => e } + keyboardNavigationLabelProvider: { getKeyboardNavigationLabel: e => e }, + horizontalScrolling: false }, this.contextKeyService, this.listService, this.themeService, this.configurationService, this.keybindingService); this.toDispose.push(this.tree.onContextMenu(e => this.onContextMenu(e))); From caa6bc95bf4caac1a4a5a6050ca5943f5985d897 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Fri, 25 Jan 2019 17:27:28 +0100 Subject: [PATCH 151/274] setting should mention lists --- src/vs/platform/list/browser/listService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/list/browser/listService.ts b/src/vs/platform/list/browser/listService.ts index f19f80ebeb2..0957f20ddf8 100644 --- a/src/vs/platform/list/browser/listService.ts +++ b/src/vs/platform/list/browser/listService.ts @@ -1203,7 +1203,7 @@ configurationRegistry.registerConfiguration({ [horizontalScrollingKey]: { 'type': 'boolean', 'default': false, - 'description': localize('horizontalScrolling setting', "Controls whether trees support horizontal scrolling in the workbench.") + 'description': localize('horizontalScrolling setting', "Controls whether lists and trees support horizontal scrolling in the workbench.") }, [treeIndentKey]: { 'type': 'number', From 8e9151a446afdc84274aed2c424b901353345845 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 24 Jan 2019 23:59:43 -0800 Subject: [PATCH 152/274] ensure fuzzy score limit is what it is, #66923 --- src/vs/base/common/filters.ts | 19 ++++--------------- src/vs/base/test/common/filters.test.ts | 21 +++++++++++---------- 2 files changed, 15 insertions(+), 25 deletions(-) diff --git a/src/vs/base/common/filters.ts b/src/vs/base/common/filters.ts index bb69923e2e7..e7674b93648 100644 --- a/src/vs/base/common/filters.ts +++ b/src/vs/base/common/filters.ts @@ -378,16 +378,13 @@ export function createMatches(score: undefined | FuzzyScore): IMatch[] { return []; } - const [, matches, wordStart] = score; + const matches = score[1].toString(2); + const wordStart = score[2]; const res: IMatch[] = []; - for (let pos = wordStart; pos < _masks.length; pos++) { - const mask = _masks[pos]; - if (mask > matches) { - break; - } else if (matches & mask) { + for (let pos = wordStart; pos < _maxLen; pos++) { + if (matches[matches.length - (pos + 1)] === '1') { const last = res[res.length - 1]; - if (last && last.end === pos) { last.end = pos + 1; } else { @@ -400,14 +397,6 @@ export function createMatches(score: undefined | FuzzyScore): IMatch[] { const _maxLen = 53; -const _masks = (function () { - const result: number[] = []; - for (let pos = 0; pos < _maxLen; pos++) { - result.push(2 ** pos); - } - return result; -}()); - function initTable() { const table: number[][] = []; const row: number[] = [0]; diff --git a/src/vs/base/test/common/filters.test.ts b/src/vs/base/test/common/filters.test.ts index 4aeedc57f01..eec772c9263 100644 --- a/src/vs/base/test/common/filters.test.ts +++ b/src/vs/base/test/common/filters.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { IFilter, or, matchesPrefix, matchesStrictPrefix, matchesCamelCase, matchesSubString, matchesContiguousSubString, matchesWords, fuzzyScore, IMatch, fuzzyScoreGraceful, fuzzyScoreGracefulAggressive, FuzzyScorer } from 'vs/base/common/filters'; +import { IFilter, or, matchesPrefix, matchesStrictPrefix, matchesCamelCase, matchesSubString, matchesContiguousSubString, matchesWords, fuzzyScore, IMatch, fuzzyScoreGraceful, fuzzyScoreGracefulAggressive, FuzzyScorer, createMatches } from 'vs/base/common/filters'; function filterOk(filter: IFilter, word: string, wordToMatchAgainst: string, highlights?: { start: number; end: number; }[]) { let r = filter(word, wordToMatchAgainst); @@ -208,14 +208,15 @@ suite('Filters', () => { let r = filter(pattern, pattern.toLowerCase(), opts.patternPos || 0, word, word.toLowerCase(), opts.wordPos || 0, opts.firstMatchCanBeWeak || false); assert.ok(!decoratedWord === !r); if (r) { - let [, matches] = r; + let matches = createMatches(r); let actualWord = ''; - for (let pos = 0; pos < word.length; pos++) { - if (2 ** pos & matches) { - actualWord += '^'; - } - actualWord += word[pos]; + let pos = 0; + for (const match of matches) { + actualWord += word.substring(pos, match.start); + actualWord += '^' + word.substring(match.start, match.end).split('').join('^'); + pos = match.end; } + actualWord += word.substring(pos); assert.equal(actualWord, decoratedWord); } } @@ -450,7 +451,7 @@ suite('Filters', () => { assertMatches('cno', 'co_new', '^c^o_^new', fuzzyScoreGracefulAggressive); }); - // test('List highlight filter: Not all characters from match are highlighterd #66923', () => { - // assertMatches('foo', 'barbarbarbarbarbarbarbarbarbarbarbarbarbarbarbar_foo', 'barbarbarbarbarbarbarbarbarbarbarbarbarbarbarbar_^f^o^o', fuzzyScore); - // }); + test('List highlight filter: Not all characters from match are highlighterd #66923', () => { + assertMatches('foo', 'barbarbarbarbarbarbarbarbarbarbarbarbarbarbarbar_foo', 'barbarbarbarbarbarbarbarbarbarbarbarbarbarbarbar_^f^o^o', fuzzyScore); + }); }); From e77c9ab60084e103bfd84f4c30cad405478afd6d Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Fri, 25 Jan 2019 17:35:33 +0100 Subject: [PATCH 153/274] lists/trees with horizontal scrolling should have leave some space on the last row --- src/vs/base/browser/ui/list/listView.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 056bfa2b29f..1415769a307 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -352,7 +352,7 @@ export class ListView implements ISpliceable, IDisposable { if (!this.scrollableElementUpdateDisposable) { this.scrollableElementUpdateDisposable = DOM.scheduleAtNextAnimationFrame(() => { - this.scrollableElement.setScrollDimensions({ scrollHeight: this._scrollHeight }); + this.scrollableElement.setScrollDimensions({ scrollHeight: this.scrollHeight }); this.updateScrollWidth(); this.scrollableElementUpdateDisposable = null; }); @@ -429,7 +429,7 @@ export class ListView implements ISpliceable, IDisposable { if (this.scrollableElementUpdateDisposable) { this.scrollableElementUpdateDisposable.dispose(); this.scrollableElementUpdateDisposable = null; - scrollDimensions.scrollHeight = this._scrollHeight; + scrollDimensions.scrollHeight = this.scrollHeight; } this.scrollableElement.setScrollDimensions(scrollDimensions); @@ -605,7 +605,7 @@ export class ListView implements ISpliceable, IDisposable { if (this.scrollableElementUpdateDisposable) { this.scrollableElementUpdateDisposable.dispose(); this.scrollableElementUpdateDisposable = null; - this.scrollableElement.setScrollDimensions({ scrollHeight: this._scrollHeight }); + this.scrollableElement.setScrollDimensions({ scrollHeight: this.scrollHeight }); } this.scrollableElement.setScrollPosition({ scrollTop }); @@ -620,7 +620,7 @@ export class ListView implements ISpliceable, IDisposable { } get scrollHeight(): number { - return this._scrollHeight; + return this._scrollHeight + (this.horizontalScrolling ? 10 : 0); } // Events From d1d7b244e57e955727a17316d32b6ee4464ff681 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Fri, 25 Jan 2019 17:57:32 +0100 Subject: [PATCH 154/274] expose onDidScroll in list/trees --- src/vs/base/browser/ui/list/listView.ts | 3 +++ src/vs/base/browser/ui/list/listWidget.ts | 1 + src/vs/base/browser/ui/tree/abstractTree.ts | 2 ++ src/vs/base/browser/ui/tree/asyncDataTree.ts | 2 ++ 4 files changed, 8 insertions(+) diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 1415769a307..18f70e285a8 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -180,6 +180,8 @@ export class ListView implements ISpliceable, IDisposable { readonly onDidChangeContentHeight: Event = Event.latch(this._onDidChangeContentHeight.event); get contentHeight(): number { return this.rangeMap.size; } + readonly onDidScroll: Event; + // private _onDragStart = new Emitter<{ element: T, uri: string, event: DragEvent }>(); // readonly onDragStart = this._onDragStart.event; @@ -234,6 +236,7 @@ export class ListView implements ISpliceable, IDisposable { this.disposables = [this.rangeMap, this.gesture, this.scrollableElement, this.cache]; + this.onDidScroll = Event.signal(this.scrollableElement.onScroll); this.scrollableElement.onScroll(this.onScroll, this, this.disposables); domEvent(this.rowsContainer, TouchEventType.Change)(this.onTouchChange, this, this.disposables); diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index cd4ad77d3bb..717b6ab7907 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -1080,6 +1080,7 @@ export class List implements ISpliceable, IDisposable { return Event.map(this._onPin.event, indexes => this.toListEvent({ indexes })); } + get onDidScroll(): Event { return this.view.onDidScroll; } get onMouseClick(): Event> { return this.view.onMouseClick; } get onMouseDblClick(): Event> { return this.view.onMouseDblClick; } get onMouseMiddleClick(): Event> { return this.view.onMouseMiddleClick; } diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 1e2cf248eb2..e8916590c1d 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -636,6 +636,8 @@ export abstract class AbstractTree implements IDisposable private _onDidUpdateOptions = new Emitter>(); readonly onDidUpdateOptions = this._onDidUpdateOptions.event; + get onDidScroll(): Event { return this.view.onDidScroll; } + get onDidChangeFocus(): Event> { return Event.map(this.view.onFocusChange, asTreeEvent); } get onDidChangeSelection(): Event> { return Event.map(this.view.onSelectionChange, asTreeEvent); } get onDidOpen(): Event> { return Event.map(this.view.onDidOpen, asTreeEvent); } diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index 4100092ef3d..e6b0b855f9d 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -263,6 +263,8 @@ export class AsyncDataTree implements IDisposable protected readonly disposables: IDisposable[] = []; + get onDidScroll(): Event { return this.tree.onDidScroll; } + get onDidChangeFocus(): Event> { return Event.map(this.tree.onDidChangeFocus, asTreeEvent); } get onDidChangeSelection(): Event> { return Event.map(this.tree.onDidChangeSelection, asTreeEvent); } get onDidOpen(): Event> { return Event.map(this.tree.onDidOpen, asTreeEvent); } From 5ac435e50c5807ee4875d6aad587130385e72fd8 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 25 Jan 2019 18:17:09 +0100 Subject: [PATCH 155/274] files - always have "Revert File" enabled "Revert File" can be used to force load a file from disk and as such is useful in any context. --- .../parts/files/electron-browser/fileActions.contribution.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/workbench/parts/files/electron-browser/fileActions.contribution.ts b/src/vs/workbench/parts/files/electron-browser/fileActions.contribution.ts index a47d263259c..1002950c754 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileActions.contribution.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileActions.contribution.ts @@ -596,8 +596,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { group: '6_close', command: { id: REVERT_FILE_COMMAND_ID, - title: nls.localize({ key: 'miRevert', comment: ['&& denotes a mnemonic'] }, "Re&&vert File"), - precondition: DirtyEditorContext + title: nls.localize({ key: 'miRevert', comment: ['&& denotes a mnemonic'] }, "Re&&vert File") }, order: 1 }); From 6089c69984cd0bf9d04c22cbeb1d9b9c4e380a2c Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 25 Jan 2019 18:25:57 +0100 Subject: [PATCH 156/274] update to electron@3.1.2 --- .yarnrc | 2 +- cgmanifest.json | 6 ++--- src/typings/electron.d.ts | 57 ++++++++++++++++++++++++++++++++++----- test/smoke/package.json | 2 +- test/smoke/yarn.lock | 8 +++--- 5 files changed, 60 insertions(+), 15 deletions(-) diff --git a/.yarnrc b/.yarnrc index 9e184a6f5ac..190252d15da 100644 --- a/.yarnrc +++ b/.yarnrc @@ -1,3 +1,3 @@ disturl "https://atom.io/download/electron" -target "3.1.0" +target "3.1.2" runtime "electron" diff --git a/cgmanifest.json b/cgmanifest.json index 89693a02d32..1cf8ac1f1fe 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -48,7 +48,7 @@ "git": { "name": "libchromiumcontent", "repositoryUrl": "https://github.com/electron/libchromiumcontent", - "commitHash": "29e02cd4c37777734f97d00b5a538d7c7acfa67a" + "commitHash": "7ea271f92018b1eeb8e70ec6de8c29f9758a0c05" } }, "isOnlyProductionDependency": true, @@ -73,12 +73,12 @@ "git": { "name": "electron", "repositoryUrl": "https://github.com/electron/electron", - "commitHash": "4913fc81d1dab21f4b15c4cb682a218072447fed" + "commitHash": "bb28fa8e8e797db249a66405146ad0501eaf411a" } }, "isOnlyProductionDependency": true, "license": "MIT", - "version": "3.1.0" + "version": "3.1.2" }, { "component": { diff --git a/src/typings/electron.d.ts b/src/typings/electron.d.ts index 85ff31b320f..8f72a1bc565 100644 --- a/src/typings/electron.d.ts +++ b/src/typings/electron.d.ts @@ -1,4 +1,4 @@ -// Type definitions for Electron 3.1.0 +// Type definitions for Electron 3.1.2 // Project: http://electronjs.org/ // Definitions by: The Electron Team // Definitions: https://github.com/electron/electron-typescript-definitions @@ -2794,7 +2794,9 @@ declare namespace Electron { * registered shortcut is pressed by the user. When the accelerator is already * taken by other applications, this call will silently fail. This behavior is * intended by operating systems, since they don't want applications to fight for - * global shortcuts. + * global shortcuts. The following accelerators will not be registered successfully + * on macOS 10.14 Mojave unless the app has been authorized as a trusted + * accessibility client: */ register(accelerator: Accelerator, callback: Function): void; /** @@ -6307,14 +6309,14 @@ declare namespace Electron { * connection is made to the server, but before any http data is sent. The callback * has to be called with an response object. */ - onBeforeSendHeaders(filter: OnBeforeSendHeadersFilter, listener: Function): void; + onBeforeSendHeaders(filter: OnBeforeSendHeadersFilter, listener: (details: OnBeforeSendHeadersDetails, callback: (response: OnBeforeSendHeadersResponse) => void) => void): void; /** * The listener will be called with listener(details, callback) before sending an * HTTP request, once the request headers are available. This may occur after a TCP * connection is made to the server, but before any http data is sent. The callback * has to be called with an response object. */ - onBeforeSendHeaders(listener: Function): void; + onBeforeSendHeaders(listener: (details: OnBeforeSendHeadersDetails, callback: (response: OnBeforeSendHeadersResponse) => void) => void): void; /** * The listener will be called with listener(details) when a request is completed. */ @@ -6336,13 +6338,13 @@ declare namespace Electron { * headers of a request have been received. The callback has to be called with an * response object. */ - onHeadersReceived(filter: OnHeadersReceivedFilter, listener: Function): void; + onHeadersReceived(filter: OnHeadersReceivedFilter, listener: (details: OnHeadersReceivedDetails, callback: (response: OnHeadersReceivedResponse) => void) => void): void; /** * The listener will be called with listener(details, callback) when HTTP response * headers of a request have been received. The callback has to be called with an * response object. */ - onHeadersReceived(listener: Function): void; + onHeadersReceived(listener: (details: OnHeadersReceivedDetails, callback: (response: OnHeadersReceivedResponse) => void) => void): void; /** * The listener will be called with listener(details) when first byte of the * response body is received. For HTTP requests, this means that the status line @@ -8054,6 +8056,16 @@ declare namespace Electron { urls: string[]; } + interface OnBeforeSendHeadersDetails { + id: number; + url: string; + method: string; + webContentsId?: number; + resourceType: string; + timestamp: number; + requestHeaders: RequestHeaders; + } + interface OnBeforeSendHeadersFilter { /** * Array of URL patterns that will be used to filter out the requests that do not @@ -8062,6 +8074,14 @@ declare namespace Electron { urls: string[]; } + interface OnBeforeSendHeadersResponse { + cancel?: boolean; + /** + * When provided, request will be made with these headers. + */ + requestHeaders?: RequestHeaders; + } + interface OnCompletedDetails { id: number; url: string; @@ -8105,6 +8125,18 @@ declare namespace Electron { urls: string[]; } + interface OnHeadersReceivedDetails { + id: number; + url: string; + method: string; + webContentsId?: number; + resourceType: string; + timestamp: number; + statusLine: string; + statusCode: number; + responseHeaders: ResponseHeaders; + } + interface OnHeadersReceivedFilter { /** * Array of URL patterns that will be used to filter out the requests that do not @@ -8113,6 +8145,19 @@ declare namespace Electron { urls: string[]; } + interface OnHeadersReceivedResponse { + cancel: boolean; + /** + * When provided, the server is assumed to have responded with these headers. + */ + responseHeaders?: ResponseHeaders; + /** + * Should be provided when overriding responseHeaders to change header status + * otherwise original response header's status will be used. + */ + statusLine?: string; + } + interface OnResponseStartedDetails { id: number; url: string; diff --git a/test/smoke/package.json b/test/smoke/package.json index f3c439c99a8..fe5adc19357 100644 --- a/test/smoke/package.json +++ b/test/smoke/package.json @@ -22,7 +22,7 @@ "@types/webdriverio": "4.6.1", "concurrently": "^3.5.1", "cpx": "^1.5.0", - "electron": "3.1.0", + "electron": "3.1.2", "htmlparser2": "^3.9.2", "mkdirp": "^0.5.1", "mocha": "^5.2.0", diff --git a/test/smoke/yarn.lock b/test/smoke/yarn.lock index a44a612cf72..6ca02b80baf 100644 --- a/test/smoke/yarn.lock +++ b/test/smoke/yarn.lock @@ -596,10 +596,10 @@ electron-download@^4.1.0: semver "^5.4.1" sumchecker "^2.0.2" -electron@3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/electron/-/electron-3.1.0.tgz#5e36ba4c24926c7cf80eccf6f8361b3cad409f17" - integrity sha512-FnHH3T7aQGAjw5h8//9BNLZBByP/gnEGP3sQH5if7HVe6Znz5KcsRbIdxLYVH9DXJFoJ2SArP+UiAAYQIdVQJQ== +electron@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/electron/-/electron-3.1.2.tgz#e410b976c56fc2f783c3b0fb6d757e02eaeab902" + integrity sha512-B/mXRCN8jGBBx8dvtIgLyW+nE8i9y7K9G6wijU+cLoneqF5al9BgZA1l5xgZEiUrwTtt0cgXIWNwhStt7EDoQQ== dependencies: "@types/node" "^8.0.24" electron-download "^4.1.0" From 002cd6253bfce5b3d2e66c1750f9ae555b6cd1ad Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 25 Jan 2019 18:28:34 +0100 Subject: [PATCH 157/274] move exthost into extensions service where its used from (#67131) --- .../api/node/extHostExtensionService.ts | 2 +- src/vs/workbench/buildfile.js | 3 ++- .../extensions/electron-browser/extensionHost.ts | 2 +- .../extensions}/node/extensionHostMain.ts | 0 .../extensions}/node/extensionHostProcess.ts | 2 +- .../extensions}/node/proxyResolver.ts | 0 tslint.json | 16 +--------------- 7 files changed, 6 insertions(+), 19 deletions(-) rename src/vs/workbench/{ => services/extensions}/node/extensionHostMain.ts (100%) rename src/vs/workbench/{ => services/extensions}/node/extensionHostProcess.ts (98%) rename src/vs/workbench/{ => services/extensions}/node/proxyResolver.ts (100%) diff --git a/src/vs/workbench/api/node/extHostExtensionService.ts b/src/vs/workbench/api/node/extHostExtensionService.ts index 41dd9ad1fc9..8edbff284ea 100644 --- a/src/vs/workbench/api/node/extHostExtensionService.ts +++ b/src/vs/workbench/api/node/extHostExtensionService.ts @@ -21,7 +21,7 @@ import { ExtHostStorage } from 'vs/workbench/api/node/extHostStorage'; import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace'; import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry'; -import { connectProxyResolver } from 'vs/workbench/node/proxyResolver'; +import { connectProxyResolver } from 'vs/workbench/services/extensions/node/proxyResolver'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import * as errors from 'vs/base/common/errors'; import { ResolvedAuthority } from 'vs/platform/remote/common/remoteAuthorityResolver'; diff --git a/src/vs/workbench/buildfile.js b/src/vs/workbench/buildfile.js index caa36aa632b..8946ef5adb8 100644 --- a/src/vs/workbench/buildfile.js +++ b/src/vs/workbench/buildfile.js @@ -22,10 +22,11 @@ exports.collectModules = function () { createModuleDescription('vs/workbench/parts/debug/node/telemetryApp', []), createModuleDescription('vs/workbench/services/search/node/searchApp', []), + createModuleDescription('vs/workbench/services/files/node/watcher/unix/watcherApp', []), createModuleDescription('vs/workbench/services/files/node/watcher/nsfw/watcherApp', []), - createModuleDescription('vs/workbench/node/extensionHostProcess', []), + createModuleDescription('vs/workbench/services/extensions/node/extensionHostProcess', []), ]; return modules; diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts index 5a6e51c6d3a..b54507fb37e 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts @@ -167,7 +167,7 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { const opts = { env: objects.mixin(objects.deepClone(process.env), { - AMD_ENTRYPOINT: 'vs/workbench/node/extensionHostProcess', + AMD_ENTRYPOINT: 'vs/workbench/services/extensions/node/extensionHostProcess', PIPE_LOGGING: 'true', VERBOSE_LOGGING: true, VSCODE_IPC_HOOK_EXTHOST: pipeName, diff --git a/src/vs/workbench/node/extensionHostMain.ts b/src/vs/workbench/services/extensions/node/extensionHostMain.ts similarity index 100% rename from src/vs/workbench/node/extensionHostMain.ts rename to src/vs/workbench/services/extensions/node/extensionHostMain.ts diff --git a/src/vs/workbench/node/extensionHostProcess.ts b/src/vs/workbench/services/extensions/node/extensionHostProcess.ts similarity index 98% rename from src/vs/workbench/node/extensionHostProcess.ts rename to src/vs/workbench/services/extensions/node/extensionHostProcess.ts index ea1b550b065..5f2935618c6 100644 --- a/src/vs/workbench/node/extensionHostProcess.ts +++ b/src/vs/workbench/services/extensions/node/extensionHostProcess.ts @@ -12,7 +12,7 @@ import { Protocol } from 'vs/base/parts/ipc/node/ipc.net'; import product from 'vs/platform/node/product'; import { IInitData } from 'vs/workbench/api/node/extHost.protocol'; import { MessageType, createMessageOfType, isMessageOfType } from 'vs/workbench/common/extensionHostProtocol'; -import { ExtensionHostMain, exit } from 'vs/workbench/node/extensionHostMain'; +import { exit, ExtensionHostMain } from 'vs/workbench/services/extensions/node/extensionHostMain'; // With Electron 2.x and node.js 8.x the "natives" module // can cause a native crash (see https://github.com/nodejs/node/issues/19891 and diff --git a/src/vs/workbench/node/proxyResolver.ts b/src/vs/workbench/services/extensions/node/proxyResolver.ts similarity index 100% rename from src/vs/workbench/node/proxyResolver.ts rename to src/vs/workbench/services/extensions/node/proxyResolver.ts diff --git a/tslint.json b/tslint.json index 919f55305f5..67a640687c9 100644 --- a/tslint.json +++ b/tslint.json @@ -393,21 +393,6 @@ "*" // node modules ] }, - { - "target": "**/vs/workbench/node/**", - "restrictions": [ - "vs/nls", - "**/vs/base/{common,node}/**", - "**/vs/base/parts/*/{common,node}/**", - "**/vs/platform/node/**", - "**/vs/platform/*/{common,node}/**", - "**/vs/editor/{common,node}/**", - "**/vs/editor/contrib/*/{common,node}/**", - "**/vs/workbench/{common,node,api}/**", - "**/vs/workbench/services/*/{common,node}/**", - "*" // node modules - ] - }, { "target": "**/vs/workbench/services/**/test/**", "restrictions": [ @@ -454,6 +439,7 @@ "**/vs/platform/**/{common,node}/**", "**/vs/editor/{common,node}/**", "**/vs/workbench/{common,node}/**", + "**/vs/workbench/api/{common,node}/**", "**/vs/workbench/services/**/{common,node}/**", "*" // node modules ] From 4d20887762be6a060ffaa2363b448a35e3ea92d4 Mon Sep 17 00:00:00 2001 From: Scott Craig Date: Fri, 25 Jan 2019 13:48:56 -0500 Subject: [PATCH 158/274] Fix settings centering issue --- .../parts/preferences/electron-browser/media/settingsEditor2.css | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/parts/preferences/electron-browser/media/settingsEditor2.css b/src/vs/workbench/parts/preferences/electron-browser/media/settingsEditor2.css index 5acd2d73baf..a29751e89bd 100644 --- a/src/vs/workbench/parts/preferences/electron-browser/media/settingsEditor2.css +++ b/src/vs/workbench/parts/preferences/electron-browser/media/settingsEditor2.css @@ -113,7 +113,6 @@ text-decoration: underline; } -.settings-editor.no-toc-search > .settings-body .settings-tree-container .monaco-list-rows, .settings-editor.narrow-width > .settings-body .settings-tree-container .monaco-list-rows { margin-left: 0px; } From 057b48ef8c636d7195f0874a5fad0e1defc0713f Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Fri, 25 Jan 2019 11:13:29 -0800 Subject: [PATCH 159/274] reactions --- src/vs/vscode.proposed.d.ts | 11 +++++++++++ .../parts/comments/electron-browser/commentNode.ts | 13 +++++++++++++ .../comments/electron-browser/media/review.css | 9 +++++++++ 3 files changed, 33 insertions(+) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index e2c6fb84fdc..77b915859c9 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -793,6 +793,7 @@ declare module 'vscode' { command?: Command; isDraft?: boolean; + commentReactions?: CommentReaction[]; } export interface CommentThreadChangedEvent { @@ -817,6 +818,11 @@ declare module 'vscode' { readonly inDraftMode: boolean; } + interface CommentReaction { + readonly label?: string; + readonly iconPath?: string | Uri; + } + interface DocumentCommentProvider { /** * Provide the commenting ranges and comment threads for the given document. The comments are displayed within the editor. @@ -851,6 +857,11 @@ declare module 'vscode' { deleteDraftLabel?: string; finishDraftLabel?: string; + commentReactions?: CommentReaction[]; + + addReaction(comment: Comment, reaction: CommentReaction): Promise; + deleteReaction(comment: Comment, reaction: CommentReaction): Promise; + /** * Notify of updates to comment threads. */ diff --git a/src/vs/workbench/parts/comments/electron-browser/commentNode.ts b/src/vs/workbench/parts/comments/electron-browser/commentNode.ts index 21077bd08aa..f1338bd4507 100644 --- a/src/vs/workbench/parts/comments/electron-browser/commentNode.ts +++ b/src/vs/workbench/parts/comments/electron-browser/commentNode.ts @@ -86,6 +86,8 @@ export class CommentNode extends Disposable { this._md = this.markdownRenderer.render(comment.body).element; this._body.appendChild(this._md); + this.createReactions(commentDetailsContainer); + this._domNode.setAttribute('aria-label', `${comment.userName}, ${comment.body.value}`); this._domNode.setAttribute('role', 'treeitem'); this._clearTimeout = null; @@ -127,6 +129,17 @@ export class CommentNode extends Disposable { } } + private createReactions(commentDetailsContainer: HTMLElement): void { + let reactions = ['❤️', '🎉', '😄']; + + const reactionsBar = dom.append(commentDetailsContainer, dom.$('div.comment-reactions')); + + reactions.forEach(reaction => { + let btn = new Button(reactionsBar); + btn.label = reaction; + }); + } + private createCommentEditor(): void { const container = dom.append(this._commentEditContainer, dom.$('.edit-textarea')); this._commentEditor = this.instantiationService.createInstance(SimpleCommentEditor, container, SimpleCommentEditor.getEditorOptions()); diff --git a/src/vs/workbench/parts/comments/electron-browser/media/review.css b/src/vs/workbench/parts/comments/electron-browser/media/review.css index eaecf20ed26..11015f5d7e9 100644 --- a/src/vs/workbench/parts/comments/electron-browser/media/review.css +++ b/src/vs/workbench/parts/comments/electron-browser/media/review.css @@ -78,6 +78,15 @@ border-style: none; } + +.monaco-editor .review-widget .body .monaco-text-button { + margin: 0 7px 0 0; + width: 30px; + background-color: transparent; + border: 1px solid grey; + border-radius: 3px; +} + .monaco-editor .review-widget .body .review-comment .review-comment-contents { padding-left: 20px; user-select: text; From b36fddfc1fab811d2353cb208bc27a11d22c7ca5 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 24 Jan 2019 15:48:39 -0800 Subject: [PATCH 160/274] Experimentally marking a few more quick fixes as preferred --- .../typescript-language-features/src/features/quickFix.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/extensions/typescript-language-features/src/features/quickFix.ts b/extensions/typescript-language-features/src/features/quickFix.ts index dbce53a14cc..24866ac9403 100644 --- a/extensions/typescript-language-features/src/features/quickFix.ts +++ b/extensions/typescript-language-features/src/features/quickFix.ts @@ -304,9 +304,12 @@ class TypeScriptQuickFixProvider implements vscode.CodeActionProvider { } const preferredFixes = new Set([ + 'annotateWithTypeFromJSDoc', 'constructorForDerivedNeedSuperCall', + 'extendsInterfaceBecomesImplements', 'fixClassIncorrectlyImplementsInterface', 'fixUnreachableCode', + 'forgottenThisPropertyAccess', 'spelling', 'unusedIdentifier', ]); From 7f1745027bf372539e2d3e1995d54e973aa0ee97 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 25 Jan 2019 11:48:10 -0800 Subject: [PATCH 161/274] Re-use options from markdown engine instead of creating new options object Fixes https://github.com/mjbvz/vscode-markdown-mermaid/issues/26 --- extensions/markdown-language-features/src/markdownEngine.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/markdown-language-features/src/markdownEngine.ts b/extensions/markdown-language-features/src/markdownEngine.ts index ac2743a487d..90e7284cf2b 100644 --- a/extensions/markdown-language-features/src/markdownEngine.ts +++ b/extensions/markdown-language-features/src/markdownEngine.ts @@ -131,7 +131,7 @@ export class MarkdownEngine { const config = this.getConfig(document.uri); const engine = await this.getEngine(config); return engine.renderer.render(this.tokenize(document, config, engine), { - ...(await getMarkdownOptions(() => engine)), + ...(engine as any).options, ...config }, {}); } From ea218fab26b6aaf2f836ebfdc3a3d8dc0779c1d6 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 25 Jan 2019 11:49:29 -0800 Subject: [PATCH 162/274] vscode-xterm@3.11.0-beta1 - addCsiHandler/addOscHandler custom sequence hooks - Reflow final - OSC_STRING optimization - Draw to middle instead of top - NPE/sanity checks in DOM renderer underline - Remove jsarray buffer - Doc typos - Don't print keys that print more than a single character --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 58456599d0d..6d516a6819a 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "vscode-ripgrep": "^1.2.5", "vscode-sqlite3": "4.0.7", "vscode-textmate": "^4.0.1", - "vscode-xterm": "3.10.0-beta11-reflow", + "vscode-xterm": "3.11.0-beta1", "winreg": "^1.2.4", "yauzl": "^2.9.1", "yazl": "^2.4.3" diff --git a/yarn.lock b/yarn.lock index 9dbb3c2151d..da55da48abd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9433,10 +9433,10 @@ vscode-uri@^1.0.6: resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-1.0.6.tgz#6b8f141b0bbc44ad7b07e94f82f168ac7608ad4d" integrity sha512-sLI2L0uGov3wKVb9EB+vIQBl9tVP90nqRvxSoJ35vI3NjxE8jfsE5DSOhWgSunHSZmKS4OCi2jrtfxK7uyp2ww== -vscode-xterm@3.10.0-beta11-reflow: - version "3.10.0-beta11-reflow" - resolved "https://registry.yarnpkg.com/vscode-xterm/-/vscode-xterm-3.10.0-beta11-reflow.tgz#e92eb61163489957ba2d4ec978ddebd0bb7b6631" - integrity sha512-BrOMsRAgsZ/lDTtgsRdlHPAFy4ZDr6MxVGYbQzysO4RTP010kno5qmOvctQP/6tRDl9gymUHZIy1FmjfsmdxcQ== +vscode-xterm@3.11.0-beta1: + version "3.11.0-beta1" + resolved "https://registry.yarnpkg.com/vscode-xterm/-/vscode-xterm-3.11.0-beta1.tgz#1c4dac56e8e87839e8cbc4a6481916f578e3ad8e" + integrity sha512-9T1uswvZ4fGGa+rXCT7AFmTbwQsuARoyG75FzTr0MYDXxg0hufnofWZ73LBn92yY/GP9bv2oJZ5Wc4LDlX6OJQ== vso-node-api@6.1.2-preview: version "6.1.2-preview" From 80173554753db48f121aab2c1a7c63032ff24246 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Fri, 25 Jan 2019 12:37:58 -0800 Subject: [PATCH 163/274] fix sidebar on launch with grid layout fixes #67062 --- src/vs/workbench/electron-browser/workbench.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts index aee037e7b7c..d4a0ac5172e 100644 --- a/src/vs/workbench/electron-browser/workbench.ts +++ b/src/vs/workbench/electron-browser/workbench.ts @@ -235,7 +235,11 @@ export class Workbench extends Disposable implements IPartService { private fontAliasing: FontAliasingOption; private hasInitialFilesToOpen: boolean; private shouldCenterLayout = false; - private uiState: IWorkbenchUIState = {}; + private uiState: IWorkbenchUIState = { + lastPanelHeight: 350, + lastPanelWidth: 350, + lastSidebarDimension: 300, + }; private inZenMode: IContextKey; private sideBarVisibleContext: IContextKey; @@ -1026,11 +1030,10 @@ export class Workbench extends Disposable implements IPartService { }); } else { this.workbenchGrid = new SerializableGrid(this.editorPart, { proportionalLayout: false }); - } - this.updateGrid(); this.workbench.prepend(this.workbenchGrid.element); + this.layout(); } else { this.workbenchGrid = this.instantiationService.createInstance( WorkbenchLayout, @@ -1522,8 +1525,8 @@ export class Workbench extends Disposable implements IPartService { DOM.position(this.workbench, 0, 0, 0, 0, 'relative'); DOM.size(this.workbench, dimensions.width, dimensions.height); - this.updateGrid(); this.workbenchGrid.layout(dimensions.width, dimensions.height); + this.updateGrid(); } else { this.workbenchGrid.layout(options); } From 89480a0d954a8cf57a0da482b3ff7d7e5468f0d9 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Fri, 25 Jan 2019 13:31:12 -0800 Subject: [PATCH 164/274] fix broken picker in grid layout fixes #67075 --- src/vs/workbench/electron-browser/workbench.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts index d4a0ac5172e..929dda515de 100644 --- a/src/vs/workbench/electron-browser/workbench.ts +++ b/src/vs/workbench/electron-browser/workbench.ts @@ -1525,7 +1525,16 @@ export class Workbench extends Disposable implements IPartService { DOM.position(this.workbench, 0, 0, 0, 0, 'relative'); DOM.size(this.workbench, dimensions.width, dimensions.height); + // Layout the grid this.workbenchGrid.layout(dimensions.width, dimensions.height); + + // Layout non-view ui components + this.quickInput.layout(dimensions); + this.quickOpen.layout(dimensions); + this.notificationsCenter.layout(dimensions); + this.notificationsToasts.layout(dimensions); + + // Update grid view membership this.updateGrid(); } else { this.workbenchGrid.layout(options); From 7d56a1d93787bfa21817d9b962791bb886008563 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Fri, 25 Jan 2019 13:37:40 -0800 Subject: [PATCH 165/274] fix titlebar height on macOS fixes #67063 --- src/vs/workbench/browser/parts/titlebar/titlebarPart.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index 29714f58a92..c6bd885de3b 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -66,8 +66,8 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi minimumWidth: number = 0; maximumWidth: number = Number.POSITIVE_INFINITY; - minimumHeight: number = 30; - maximumHeight: number = 30; + minimumHeight: number = isMacintosh ? 22 : 30; + maximumHeight: number = isMacintosh ? 22 : 30; private _onDidChange = new Emitter<{ width: number; height: number; }>(); readonly onDidChange = this._onDidChange.event; From 88de59d49750817c52c17438fedfae680e4b48c3 Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Fri, 25 Jan 2019 13:47:46 -0800 Subject: [PATCH 166/274] Improve Process explorer's hover, fixes #66279 --- .../processExplorer/processExplorerMain.ts | 61 +++++++++++-------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts b/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts index a3a47fd39a9..96b66228f0d 100644 --- a/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts +++ b/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts @@ -113,42 +113,55 @@ function getProcessIdWithHighestProperty(processList, propertyName: string) { } function updateProcessInfo(processList): void { - const target = document.getElementById('process-list'); - if (!target) { + const container = document.getElementById('process-list'); + if (!container) { return; } + container.innerHTML = ''; const highestCPUProcess = getProcessIdWithHighestProperty(processList, 'cpu'); const highestMemoryProcess = getProcessIdWithHighestProperty(processList, 'memory'); - let tableHtml = ` - - - ${localize('cpu', "CPU %")} - ${localize('memory', "Memory (MB)")} - ${localize('pid', "pid")} - ${localize('name', "Name")} - - `; + const tableHead = document.createElement('thead'); + tableHead.innerHTML = ` + ${localize('cpu', "CPU %")} + ${localize('memory', "Memory (MB)")} + ${localize('pid', "pid")} + ${localize('name', "Name")} + `; - tableHtml += ``; + const tableBody = document.createElement('tbody'); processList.forEach(p => { - const cpuClass = p.pid === highestCPUProcess ? 'highest' : ''; - const memoryClass = p.pid === highestMemoryProcess ? 'highest' : ''; + const row = document.createElement('tr'); + row.id = p.pid; - tableHtml += ` - - ${p.cpu} - ${p.memory} - ${p.pid} - ${p.formattedName} - `; + const cpu = document.createElement('td'); + p.pid === highestCPUProcess + ? cpu.classList.add('centered', 'highest') + : cpu.classList.add('centered'); + cpu.textContent = p.cpu; + + const memory = document.createElement('td'); + p.pid === highestMemoryProcess + ? memory.classList.add('centered', 'highest') + : memory.classList.add('centered'); + memory.textContent = p.memory; + + const pid = document.createElement('td'); + pid.classList.add('centered'); + pid.textContent = p.pid; + + const name = document.createElement('td'); + name.classList.add('data'); + name.title = p.cmd; + name.textContent = p.formattedName; + + row.append(cpu, memory, pid, name); + tableBody.appendChild(row); }); - tableHtml += ``; - - target.innerHTML = tableHtml; + container.append(tableHead, tableBody); } function applyStyles(styles: ProcessExplorerStyles): void { From 6eefb6d8d14543e1e992c418a910d1760aac3b4c Mon Sep 17 00:00:00 2001 From: Christopher Leidigh Date: Fri, 25 Jan 2019 17:11:13 -0500 Subject: [PATCH 167/274] ObjectTree/List: Add ariaRootRole option. Addresses: #65939 (#67092) * ObjectTree/List: Add ariaRootRole option. Addresses: #65939 * Address @joaomoreno tweaks --- src/vs/base/browser/ui/list/list.ts | 6 ++++++ src/vs/base/browser/ui/list/listWidget.ts | 15 ++++++++++++--- .../parts/preferences/browser/settingsTree.ts | 10 ++++------ .../electron-browser/settingsEditor2.ts | 4 ---- 4 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/vs/base/browser/ui/list/list.ts b/src/vs/base/browser/ui/list/list.ts index 89af3a81a5f..a2afd3aaf80 100644 --- a/src/vs/base/browser/ui/list/list.ts +++ b/src/vs/base/browser/ui/list/list.ts @@ -62,6 +62,12 @@ export interface IIdentityProvider { getId(element: T): { toString(): string; }; } +// Default tree/list root role 'tree' does not support interactive element labeling, 'form' does +export enum ListAriaRootRole { + TREE = 'tree', // default tree structure role + FORM = 'form' // unstructured - allows interactive elements within tree/list structure +} + export interface IKeyboardNavigationLabelProvider { getKeyboardNavigationLabel(element: T): { toString(): string; }; mightProducePrintableCharacter?(event: IKeyboardEvent): boolean; diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 717b6ab7907..ae7f801485d 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -16,7 +16,7 @@ import { KeyCode } from 'vs/base/common/keyCodes'; import { StandardKeyboardEvent, IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { Event, Emitter, EventBufferer } from 'vs/base/common/event'; import { domEvent } from 'vs/base/browser/event'; -import { IListVirtualDelegate, IListRenderer, IListEvent, IListContextMenuEvent, IListMouseEvent, IListTouchEvent, IListGestureEvent, IIdentityProvider, IKeyboardNavigationLabelProvider, IListDragAndDrop, IListDragOverReaction } from './list'; +import { IListVirtualDelegate, IListRenderer, IListEvent, IListContextMenuEvent, IListMouseEvent, IListTouchEvent, IListGestureEvent, IIdentityProvider, IKeyboardNavigationLabelProvider, IListDragAndDrop, IListDragOverReaction, ListAriaRootRole } from './list'; import { ListView, IListViewOptions, IListViewDragAndDrop } from './listView'; import { Color } from 'vs/base/common/color'; import { mixin } from 'vs/base/common/objects'; @@ -779,11 +779,13 @@ export class DefaultStyleController implements IStyleController { } } + export interface IListOptions extends IListStyles { readonly identityProvider?: IIdentityProvider; readonly dnd?: IListDragAndDrop; readonly enableKeyboardNavigation?: boolean; readonly keyboardNavigationLabelProvider?: IKeyboardNavigationLabelProvider; + readonly ariaRole?: ListAriaRootRole; readonly ariaLabel?: string; readonly keyboardSupport?: boolean; readonly multipleSelectionSupport?: boolean; @@ -844,7 +846,8 @@ const DefaultOptions = { onDragStart(): void { }, onDragOver() { return false; }, drop() { } - } + }, + ariaRootRole: ListAriaRootRole.TREE }; // TODO@Joao: move these utils into a SortedArray class @@ -1165,7 +1168,13 @@ export class List implements ISpliceable, IDisposable { }; this.view = new ListView(container, virtualDelegate, renderers, viewOptions); - this.view.domNode.setAttribute('role', 'tree'); + + if (typeof _options.ariaRole !== 'string') { + this.view.domNode.setAttribute('role', ListAriaRootRole.TREE); + } else { + this.view.domNode.setAttribute('role', _options.ariaRole); + } + DOM.addClass(this.view.domNode, this.idPrefix); this.view.domNode.tabIndex = 0; diff --git a/src/vs/workbench/parts/preferences/browser/settingsTree.ts b/src/vs/workbench/parts/preferences/browser/settingsTree.ts index 37f37082112..f4776033828 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsTree.ts @@ -12,7 +12,7 @@ import { alert as ariaAlert } from 'vs/base/browser/ui/aria/aria'; import { Button } from 'vs/base/browser/ui/button/button'; import { Checkbox } from 'vs/base/browser/ui/checkbox/checkbox'; import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox'; -import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; +import { IListVirtualDelegate, ListAriaRootRole } from 'vs/base/browser/ui/list/list'; import { ISelectOptionItem, SelectBox } from 'vs/base/browser/ui/selectBox/selectBox'; import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; import { IObjectTreeOptions, ObjectTree } from 'vs/base/browser/ui/tree/objectTree'; @@ -437,11 +437,6 @@ export abstract class AbstractSettingRenderer implements ITreeRenderertemplate, onChange); - // Remove tree attributes - sometimes overridden by tree - should be managed there - template.containerElement.parentElement.parentElement.removeAttribute('role'); - template.containerElement.parentElement.parentElement.removeAttribute('aria-level'); - template.containerElement.parentElement.parentElement.removeAttribute('aria-posinset'); - template.containerElement.parentElement.parentElement.removeAttribute('aria-setsize'); } private renderDescriptionMarkdown(element: SettingsTreeSettingElement, text: string, disposeables: IDisposable[]): HTMLElement { @@ -1292,11 +1287,14 @@ export class SettingsTree extends ObjectTree { ) { const treeClass = 'settings-editor-tree'; + // Must use role = EListAriaRootRole.FORM for interactive elements in settingsTree + super(container, new SettingsTreeDelegate(), renderers, { supportDynamicHeights: true, + ariaRole: ListAriaRootRole.FORM, ariaLabel: localize('treeAriaLabel', "Settings"), identityProvider: { getId(e) { diff --git a/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts b/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts index 1eafe1e69c0..5e8b41d0e3e 100644 --- a/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts +++ b/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts @@ -605,10 +605,6 @@ export class SettingsEditor2 extends BaseEditor { this.settingRenderers.allRenderers)); this.settingsTree.getHTMLElement().attributes.removeNamedItem('tabindex'); - // Have to redefine role of the tree widget to form for input elements - // TODO:CDL make this an option for tree - this.settingsTree.getHTMLElement().setAttribute('role', 'form'); - // this._register(this.settingsTree.onDidScroll(() => { // this.updateTreeScrollSync(); // })); From db771d085fd91f391993250f107b44646f14a004 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 25 Jan 2019 22:15:21 +0000 Subject: [PATCH 168/274] Tweaks from #67092 --- src/vs/base/browser/ui/list/list.ts | 8 +++++--- src/vs/base/browser/ui/list/listWidget.ts | 1 - .../workbench/parts/preferences/browser/settingsTree.ts | 2 -- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/vs/base/browser/ui/list/list.ts b/src/vs/base/browser/ui/list/list.ts index a2afd3aaf80..49c953f0124 100644 --- a/src/vs/base/browser/ui/list/list.ts +++ b/src/vs/base/browser/ui/list/list.ts @@ -62,10 +62,12 @@ export interface IIdentityProvider { getId(element: T): { toString(): string; }; } -// Default tree/list root role 'tree' does not support interactive element labeling, 'form' does export enum ListAriaRootRole { - TREE = 'tree', // default tree structure role - FORM = 'form' // unstructured - allows interactive elements within tree/list structure + /** default tree structure role */ + TREE = 'tree', + + /** role='tree' can interfere with screenreaders reading nested elements inside the tree row. Use FORM in that case. */ + FORM = 'form' } export interface IKeyboardNavigationLabelProvider { diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index ae7f801485d..81dedadaa7e 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -779,7 +779,6 @@ export class DefaultStyleController implements IStyleController { } } - export interface IListOptions extends IListStyles { readonly identityProvider?: IIdentityProvider; readonly dnd?: IListDragAndDrop; diff --git a/src/vs/workbench/parts/preferences/browser/settingsTree.ts b/src/vs/workbench/parts/preferences/browser/settingsTree.ts index f4776033828..0b9ccb098bc 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsTree.ts @@ -1287,8 +1287,6 @@ export class SettingsTree extends ObjectTree { ) { const treeClass = 'settings-editor-tree'; - // Must use role = EListAriaRootRole.FORM for interactive elements in settingsTree - super(container, new SettingsTreeDelegate(), renderers, From d48e978ebdffc512bc1e1840e63bd6160a8207b9 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Fri, 25 Jan 2019 14:25:08 -0800 Subject: [PATCH 169/274] propogate proportional layout flag fixes #67018 fyi @joaomoreno --- src/vs/base/browser/ui/grid/gridview.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/browser/ui/grid/gridview.ts b/src/vs/base/browser/ui/grid/gridview.ts index 73c042929c1..edde60f0eb3 100644 --- a/src/vs/base/browser/ui/grid/gridview.ts +++ b/src/vs/base/browser/ui/grid/gridview.ts @@ -147,7 +147,7 @@ class BranchNode implements ISplitView, IDisposable { this._orthogonalSize = orthogonalSize; this.element = $('.monaco-grid-branch-node'); - this.splitview = new SplitView(this.element, { orientation, styles }); + this.splitview = new SplitView(this.element, { orientation, styles, proportionalLayout }); this.splitview.layout(size); const onDidSashReset = Event.map(this.splitview.onDidSashReset, i => [i]); From aa497deb2d47dad97229ff19069a93d97ac7df32 Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Fri, 25 Jan 2019 14:34:24 -0800 Subject: [PATCH 170/274] Fixes #67140, Comments panel code font/color should match other places --- .../parts/comments/electron-browser/commentsPanel.ts | 7 ++++++- .../parts/comments/electron-browser/media/panel.css | 4 ++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/comments/electron-browser/commentsPanel.ts b/src/vs/workbench/parts/comments/electron-browser/commentsPanel.ts index 43a7d4b5d85..307216ce03d 100644 --- a/src/vs/workbench/parts/comments/electron-browser/commentsPanel.ts +++ b/src/vs/workbench/parts/comments/electron-browser/commentsPanel.ts @@ -20,7 +20,7 @@ import { CommentsDataFilter, CommentsDataSource, CommentsModelRenderer } from 'v import { ICommentService, IWorkspaceCommentThreadsEvent } from 'vs/workbench/parts/comments/electron-browser/commentService'; import { IEditorService, ACTIVE_GROUP, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { ICommandService } from 'vs/platform/commands/common/commands'; -import { textLinkForeground, textLinkActiveForeground, focusBorder } from 'vs/platform/theme/common/colorRegistry'; +import { textLinkForeground, textLinkActiveForeground, focusBorder, textPreformatForeground } from 'vs/platform/theme/common/colorRegistry'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { ResourceLabels } from 'vs/workbench/browser/labels'; @@ -97,6 +97,11 @@ export class CommentsPanel extends Panel { content.push(`.comments-panel .commenst-panel-container a:focus { outline-color: ${focusColor}; }`); } + const codeTextForegroundColor = theme.getColor(textPreformatForeground); + if (codeTextForegroundColor) { + content.push(`.comments-panel .comments-panel-container .text code { color: ${codeTextForegroundColor}; }`); + } + styleElement.innerHTML = content.join('\n'); } diff --git a/src/vs/workbench/parts/comments/electron-browser/media/panel.css b/src/vs/workbench/parts/comments/electron-browser/media/panel.css index 3c2b37bcf3f..43f2d98ff6b 100644 --- a/src/vs/workbench/parts/comments/electron-browser/media/panel.css +++ b/src/vs/workbench/parts/comments/electron-browser/media/panel.css @@ -41,6 +41,10 @@ overflow: hidden; } +.comments-panel .comments-panel-container .tree-container .comment-container .text code { + font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Courier New", monospace, "Droid Sans Fallback"; +} + .comments-panel .comments-panel-container .message-box-container { line-height: 22px; padding-left: 20px; From 1bbb5ad40d033aa24e8fa9d3b16f69b075a08801 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 25 Jan 2019 15:30:37 -0800 Subject: [PATCH 171/274] Spelling --- src/vs/vscode.proposed.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index f68d3533d7f..f79ed750f2d 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1088,7 +1088,7 @@ declare module 'vscode' { } //#endregion - //#region SignatureHelpContext active paramters - mjbvz + //#region SignatureHelpContext active parameters - mjbvz export interface SignatureHelpContext { /** * The currently active [`SignatureHelp`](#SignatureHelp). From ff58f5083f57b50e6a100ab60454dfb72fe21e80 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 25 Jan 2019 15:34:58 -0800 Subject: [PATCH 172/274] Pull in updated coffeescript grammar Fixes #67138 --- extensions/coffeescript/cgmanifest.json | 4 +- .../syntaxes/coffeescript.tmLanguage.json | 100 +++++++++++++++++- 2 files changed, 101 insertions(+), 3 deletions(-) diff --git a/extensions/coffeescript/cgmanifest.json b/extensions/coffeescript/cgmanifest.json index 3731fb9e3ff..3c792173e6e 100644 --- a/extensions/coffeescript/cgmanifest.json +++ b/extensions/coffeescript/cgmanifest.json @@ -6,12 +6,12 @@ "git": { "name": "atom/language-coffee-script", "repositoryUrl": "https://github.com/atom/language-coffee-script", - "commitHash": "a0da2a73ad817e2fc13c2ef8fcd2624017c39610" + "commitHash": "0f6db9143663e18b1ad00667820f46747dba495e" } }, "license": "MIT", "description": "The file syntaxes/coffeescript.tmLanguage.json was derived from the Atom package https://github.com/atom/language-coffee-script which was originally converted from the TextMate bundle https://github.com/jashkenas/coffee-script-tmbundle.", - "version": "0.49.2" + "version": "0.49.3" } ], "version": 1 diff --git a/extensions/coffeescript/syntaxes/coffeescript.tmLanguage.json b/extensions/coffeescript/syntaxes/coffeescript.tmLanguage.json index 5fcff295c61..708856898bc 100644 --- a/extensions/coffeescript/syntaxes/coffeescript.tmLanguage.json +++ b/extensions/coffeescript/syntaxes/coffeescript.tmLanguage.json @@ -4,10 +4,13 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/atom/language-coffee-script/commit/a0da2a73ad817e2fc13c2ef8fcd2624017c39610", + "version": "https://github.com/atom/language-coffee-script/commit/0f6db9143663e18b1ad00667820f46747dba495e", "name": "CoffeeScript", "scopeName": "source.coffee", "patterns": [ + { + "include": "#jsx" + }, { "match": "(new)\\s+(?:(?:(class)\\s+(\\w+(?:\\.\\w*)*)?)|(\\w+(?:\\.\\w*)*))", "name": "meta.class.instance.constructor.coffee", @@ -1213,6 +1216,101 @@ "include": "#embedded_comment" } ] + }, + "jsx": { + "patterns": [ + { + "include": "#jsx-tag" + }, + { + "include": "#jsx-end-tag" + } + ] + }, + "jsx-expression": { + "begin": "{", + "beginCaptures": { + "0": { + "name": "meta.brace.curly.coffee" + } + }, + "end": "}", + "endCaptures": { + "0": { + "name": "meta.brace.curly.coffee" + } + }, + "patterns": [ + { + "include": "#double_quoted_string" + }, + { + "include": "$self" + } + ] + }, + "jsx-attribute": { + "patterns": [ + { + "captures": { + "1": { + "name": "entity.other.attribute-name.coffee" + }, + "2": { + "name": "keyword.operator.assignment.coffee" + } + }, + "match": "(?:^|\\s+)([-\\w.]+)\\s*(=)" + }, + { + "include": "#double_quoted_string" + }, + { + "include": "#single_quoted_string" + }, + { + "include": "#jsx-expression" + } + ] + }, + "jsx-tag": { + "patterns": [ + { + "begin": "(<)([-\\w\\.]+)", + "beginCaptures": { + "1": { + "name": "punctuation.definition.tag.coffee" + }, + "2": { + "name": "entity.name.tag.coffee" + } + }, + "end": "(/?>)", + "name": "meta.tag.coffee", + "patterns": [ + { + "include": "#jsx-attribute" + } + ] + } + ] + }, + "jsx-end-tag": { + "patterns": [ + { + "begin": "()", + "name": "meta.tag.coffee" + } + ] } } } \ No newline at end of file From 770beca1187e4b5592a2cdd38fbdc69940852044 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 25 Jan 2019 15:35:46 -0800 Subject: [PATCH 173/274] Update js/ts grammars --- .../syntaxes/JavaScript.tmLanguage.json | 77 +++++++++++++++- .../syntaxes/JavaScriptReact.tmLanguage.json | 77 +++++++++++++++- extensions/typescript-basics/cgmanifest.json | 2 +- .../syntaxes/TypeScript.tmLanguage.json | 92 ++++++++++++++++++- .../syntaxes/TypeScriptReact.tmLanguage.json | 77 +++++++++++++++- 5 files changed, 308 insertions(+), 17 deletions(-) diff --git a/extensions/javascript/syntaxes/JavaScript.tmLanguage.json b/extensions/javascript/syntaxes/JavaScript.tmLanguage.json index 869f8dafa44..5d6df3a855e 100644 --- a/extensions/javascript/syntaxes/JavaScript.tmLanguage.json +++ b/extensions/javascript/syntaxes/JavaScript.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/76cfeabda164d2d132222af72e0a5f991e26c51a", + "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/444971648e9e1e41c54c62f41d1310f2816965c4", "name": "JavaScript (with React support)", "scopeName": "source.js", "patterns": [ @@ -690,6 +690,12 @@ { "include": "#string" }, + { + "include": "#numeric-literal" + }, + { + "include": "#regex" + }, { "include": "#object-binding-pattern" }, @@ -712,6 +718,12 @@ { "include": "#string" }, + { + "include": "#numeric-literal" + }, + { + "include": "#regex" + }, { "include": "#object-binding-pattern-const" }, @@ -986,6 +998,12 @@ { "include": "#string" }, + { + "include": "#numeric-literal" + }, + { + "include": "#regex" + }, { "include": "#parameter-object-binding-pattern" }, @@ -2462,8 +2480,8 @@ }, { "name": "meta.object.member.js meta.object-literal.key.js", - "begin": "(?=[\\'\\\"])", - "end": "(?=:)|((?<=[\\'\\\"])(?=\\s*[\\(\\<]))", + "begin": "(?=[\\'\\\"\\`])", + "end": "(?=:)|((?<=[\\'\\\"\\`])(?=((\\s*[\\(\\<,}])|(\\s+(as)\\s+))))", "patterns": [ { "include": "#comment" @@ -2473,9 +2491,22 @@ } ] }, + { + "name": "meta.object.member.js meta.object-literal.key.js", + "begin": "(?x)(?=(\\b(?)", + "captures": { + "1": { + "name": "meta.brace.angle.ts" + }, + "2": { + "name": "storage.modifier.ts" + }, + "3": { + "name": "meta.brace.angle.ts" + } + } + }, { "name": "cast.expr.ts", "begin": "(?:(?*?\\&\\|\\^]|[^_$[:alnum:]](?:\\+\\+|\\-\\-)|[^\\+]\\+|[^\\-]\\-))\\s*(<)(?! Date: Fri, 25 Jan 2019 15:41:35 -0800 Subject: [PATCH 174/274] Fix #52562, Process Explorer: NVDA reads the whole table dimension every time before reading each cell value --- .../processExplorer/media/processExplorer.css | 5 ++++- .../electron-browser/processExplorer/processExplorer.html | 2 +- .../electron-browser/processExplorer/processExplorerMain.ts | 3 ++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/vs/code/electron-browser/processExplorer/media/processExplorer.css b/src/vs/code/electron-browser/processExplorer/media/processExplorer.css index 312929a2b46..2691208f2a2 100644 --- a/src/vs/code/electron-browser/processExplorer/media/processExplorer.css +++ b/src/vs/code/electron-browser/processExplorer/media/processExplorer.css @@ -56,7 +56,7 @@ table { width: 100%; table-layout: fixed; } -th { +th[scope='col'] { vertical-align: bottom; border-bottom: 1px solid #cccccc; padding: .5rem; @@ -80,6 +80,9 @@ td { .data { white-space: pre; padding-left: .5rem; + font-weight: normal; + text-align: left; + height: 22px; } tbody > tr:hover { diff --git a/src/vs/code/electron-browser/processExplorer/processExplorer.html b/src/vs/code/electron-browser/processExplorer/processExplorer.html index 366bdda9d4e..3ef3be23f8f 100644 --- a/src/vs/code/electron-browser/processExplorer/processExplorer.html +++ b/src/vs/code/electron-browser/processExplorer/processExplorer.html @@ -8,7 +8,7 @@ -
+
diff --git a/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts b/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts index 96b66228f0d..a9e8f38c810 100644 --- a/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts +++ b/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts @@ -152,7 +152,8 @@ function updateProcessInfo(processList): void { pid.classList.add('centered'); pid.textContent = p.pid; - const name = document.createElement('td'); + const name = document.createElement('th'); + name.scope = 'row'; name.classList.add('data'); name.title = p.cmd; name.textContent = p.formattedName; From 9070abedaf002ff4bc4998985bed89e57ef415f2 Mon Sep 17 00:00:00 2001 From: Phil Marshall Date: Fri, 25 Jan 2019 18:45:06 -0600 Subject: [PATCH 175/274] image link should be pushed to results before other link --- .../src/features/documentLinkProvider.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/extensions/markdown-language-features/src/features/documentLinkProvider.ts b/extensions/markdown-language-features/src/features/documentLinkProvider.ts index ec07f8339b5..fe4eeaa6ebc 100644 --- a/extensions/markdown-language-features/src/features/documentLinkProvider.ts +++ b/extensions/markdown-language-features/src/features/documentLinkProvider.ts @@ -92,13 +92,13 @@ export default class LinkProvider implements vscode.DocumentLinkProvider { ): vscode.DocumentLink[] { const results: vscode.DocumentLink[] = []; for (const match of matchAll(this.linkPattern, text)) { - const urlLink = extractDocumentLink(document, base, match[1].length, match[6], match.index); - if (urlLink) { - results.push(urlLink); + const matchImage = match[5] && extractDocumentLink(document, base, match[3].length + 1, match[5], match.index); + if (matchImage) { + results.push(matchImage); } - const imgLink = match[5] && extractDocumentLink(document, base, match[3].length + 1, match[5], match.index); - if (imgLink) { - results.push(imgLink); + const matchLink = extractDocumentLink(document, base, match[1].length, match[6], match.index); + if (matchLink) { + results.push(matchLink); } } return results; From a7c88d79a2ac768930d40f9a8f65ae91a6a485c7 Mon Sep 17 00:00:00 2001 From: Phil Marshall Date: Fri, 25 Jan 2019 18:46:28 -0600 Subject: [PATCH 176/274] updated test for switched order and new tests for image link --- .../src/test/documentLinkProvider.test.ts | 29 +++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts b/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts index 1d94141d76e..f6309a027d3 100644 --- a/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts +++ b/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts @@ -104,12 +104,31 @@ suite('markdown.DocumentLinkProvider', () => { assertRangeEqual(link2.range, new vscode.Range(0, 23, 0, 28)); }); + // #49238 test('should handle hyperlinked images', () => { - const links = getLinksForFile('[![alt text](image.jpg)](https://example.com)'); - assert.strictEqual(links.length, 2); - const [link1, link2] = links; - assertRangeEqual(link1.range, new vscode.Range(0,25,0,44)); - assertRangeEqual(link2.range, new vscode.Range(0,13,0,22)); + { + const links = getLinksForFile('[![alt text](image.jpg)](https://example.com)'); + assert.strictEqual(links.length, 2); + const [link1, link2] = links; + assertRangeEqual(link1.range, new vscode.Range(0,13,0,22)); + assertRangeEqual(link2.range, new vscode.Range(0,25,0,44)); + } + { + const links = getLinksForFile('[![a]( whitespace.jpg )]( https://whitespace.com )'); + assert.strictEqual(links.length, 2); + const [link1, link2] = links; + assertRangeEqual(link1.range, new vscode.Range(0,7,0,21)); + assertRangeEqual(link2.range, new vscode.Range(0,26,0,48)); + } + { + const links = getLinksForFile('[![a](img1.jpg)](file1.txt) text [![a](img2.jpg)](file2.txt)'); + assert.strictEqual(links.length, 4); + const [link1, link2, link3, link4] = links; + assertRangeEqual(link1.range, new vscode.Range(0,6,0,14)); + assertRangeEqual(link2.range, new vscode.Range(0,17,0,26)); + assertRangeEqual(link3.range, new vscode.Range(0,39,0,47)); + assertRangeEqual(link4.range, new vscode.Range(0,50,0,59)); + } }); }); From ef66e5fab1398f7c4387bcc8897ff5faf7268d80 Mon Sep 17 00:00:00 2001 From: Phil Marshall Date: Fri, 25 Jan 2019 18:55:42 -0600 Subject: [PATCH 177/274] tweaked linkPattern regex to pass new tests --- .../src/features/documentLinkProvider.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/markdown-language-features/src/features/documentLinkProvider.ts b/extensions/markdown-language-features/src/features/documentLinkProvider.ts index fe4eeaa6ebc..5d6fdbd926c 100644 --- a/extensions/markdown-language-features/src/features/documentLinkProvider.ts +++ b/extensions/markdown-language-features/src/features/documentLinkProvider.ts @@ -70,7 +70,7 @@ function extractDocumentLink( } export default class LinkProvider implements vscode.DocumentLinkProvider { - private readonly linkPattern = /(\[((!\[(.+)\]\()(.+)\)\]|[^\]]*\])\(\s*)((([^\s\(\)]|\(\S*?\))+))\s*(".*?")?\)/g; + private readonly linkPattern = /(\[((!\[[^\]]*\]\(\s*)([^\s\(\)]+)\s*\)\]|[^\]]*\])\(\s*)(([^\s\(\)]|\(\S*?\))+)\s*(".*?")?\)/g; private readonly referenceLinkPattern = /(\[([^\]]+)\]\[\s*?)([^\s\]]*?)\]/g; private readonly definitionPattern = /^([\t ]*\[([^\]]+)\]:\s*)(\S+)/gm; @@ -92,11 +92,11 @@ export default class LinkProvider implements vscode.DocumentLinkProvider { ): vscode.DocumentLink[] { const results: vscode.DocumentLink[] = []; for (const match of matchAll(this.linkPattern, text)) { - const matchImage = match[5] && extractDocumentLink(document, base, match[3].length + 1, match[5], match.index); + const matchImage = match[4] && extractDocumentLink(document, base, match[3].length + 1, match[4], match.index); if (matchImage) { results.push(matchImage); } - const matchLink = extractDocumentLink(document, base, match[1].length, match[6], match.index); + const matchLink = extractDocumentLink(document, base, match[1].length, match[5], match.index); if (matchLink) { results.push(matchLink); } From 4c2d33559f3b64248b2564aea21449398e8fe3ed Mon Sep 17 00:00:00 2001 From: Phil Marshall Date: Fri, 25 Jan 2019 19:07:45 -0600 Subject: [PATCH 178/274] lazy quantifiers for linkPattern regex --- .../src/features/documentLinkProvider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/markdown-language-features/src/features/documentLinkProvider.ts b/extensions/markdown-language-features/src/features/documentLinkProvider.ts index 5d6fdbd926c..b35f3f0d787 100644 --- a/extensions/markdown-language-features/src/features/documentLinkProvider.ts +++ b/extensions/markdown-language-features/src/features/documentLinkProvider.ts @@ -70,7 +70,7 @@ function extractDocumentLink( } export default class LinkProvider implements vscode.DocumentLinkProvider { - private readonly linkPattern = /(\[((!\[[^\]]*\]\(\s*)([^\s\(\)]+)\s*\)\]|[^\]]*\])\(\s*)(([^\s\(\)]|\(\S*?\))+)\s*(".*?")?\)/g; + private readonly linkPattern = /(\[((!\[[^\]]*?\]\(\s*)([^\s\(\)]+?)\s*\)\]|[^\]]*\])\(\s*)(([^\s\(\)]|\(\S*?\))+)\s*(".*?")?\)/g; private readonly referenceLinkPattern = /(\[([^\]]+)\]\[\s*?)([^\s\]]*?)\]/g; private readonly definitionPattern = /^([\t ]*\[([^\]]+)\]:\s*)(\S+)/gm; From a6369781746ae12dd76bcbf77e9796a924246d9a Mon Sep 17 00:00:00 2001 From: al Date: Sun, 27 Jan 2019 02:09:44 +0300 Subject: [PATCH 179/274] Move cursor inside braces --- src/vs/workbench/api/common/configurationExtensionPoint.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/api/common/configurationExtensionPoint.ts b/src/vs/workbench/api/common/configurationExtensionPoint.ts index 151bc66d10a..596500d9c4c 100644 --- a/src/vs/workbench/api/common/configurationExtensionPoint.ts +++ b/src/vs/workbench/api/common/configurationExtensionPoint.ts @@ -84,7 +84,6 @@ const defaultConfigurationExtPoint = ExtensionsRegistry.registerExtensionPoint Date: Tue, 22 Jan 2019 10:55:50 +0100 Subject: [PATCH 180/274] node-debug@1.31.2 --- build/builtInExtensions.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/builtInExtensions.json b/build/builtInExtensions.json index 5bc85092c84..9480537578b 100644 --- a/build/builtInExtensions.json +++ b/build/builtInExtensions.json @@ -1,7 +1,7 @@ [ { "name": "ms-vscode.node-debug", - "version": "1.31.1", + "version": "1.31.2", "repo": "https://github.com/Microsoft/vscode-node-debug", "metadata": { "id": "b6ded8fb-a0a0-4c1c-acbd-ab2a3bc995a6", From 24261c00682529bfdc378849470b592f5672cf3a Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Tue, 22 Jan 2019 10:56:42 +0100 Subject: [PATCH 181/274] cleaning up path DAP handling --- .../mainThreadDebugService.ts | 8 +- .../workbench/api/node/extHostDebugService.ts | 6 +- .../parts/debug/common/debugUtils.ts | 85 +++++++++---------- 3 files changed, 46 insertions(+), 53 deletions(-) diff --git a/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts b/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts index 81ca13c1694..2eac8972c17 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts @@ -14,7 +14,7 @@ import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostC import severity from 'vs/base/common/severity'; import { AbstractDebugAdapter } from 'vs/workbench/parts/debug/node/debugAdapter'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; -import { convertToVSCPaths, convertToDAPaths, stringToUri, uriToString } from 'vs/workbench/parts/debug/common/debugUtils'; +import { convertToVSCPaths, convertToDAPaths } from 'vs/workbench/parts/debug/common/debugUtils'; @extHostNamedCustomer(MainContext.MainThreadDebugService) export class MainThreadDebugService implements MainThreadDebugServiceShape, IDebugAdapterFactory { @@ -259,8 +259,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb } public $acceptDAMessage(handle: number, message: DebugProtocol.ProtocolMessage) { - - this._debugAdapters.get(handle).acceptMessage(convertToVSCPaths(message, source => uriToString(source))); + this._debugAdapters.get(handle).acceptMessage(convertToVSCPaths(message, false)); } public $acceptDAError(handle: number, name: string, message: string, stack: string) { @@ -345,8 +344,7 @@ class ExtensionHostDebugAdapter extends AbstractDebugAdapter { } public sendMessage(message: DebugProtocol.ProtocolMessage): void { - - this._proxy.$sendDAMessage(this._handle, convertToDAPaths(message, source => stringToUri(source))); + this._proxy.$sendDAMessage(this._handle, convertToDAPaths(message, true)); } public stopSession(): Promise { diff --git a/src/vs/workbench/api/node/extHostDebugService.ts b/src/vs/workbench/api/node/extHostDebugService.ts index fcc931e8082..a4b106f656b 100644 --- a/src/vs/workbench/api/node/extHostDebugService.ts +++ b/src/vs/workbench/api/node/extHostDebugService.ts @@ -24,7 +24,7 @@ import { getTerminalLauncher, hasChildProcesses, prepareCommand } from 'vs/workb import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { AbstractVariableResolverService } from 'vs/workbench/services/configurationResolver/node/variableResolver'; import { ExtHostConfiguration, ExtHostConfigProvider } from './extHostConfiguration'; -import { convertToVSCPaths, convertToDAPaths, stringToUri, uriToString } from 'vs/workbench/parts/debug/common/debugUtils'; +import { convertToVSCPaths, convertToDAPaths } from 'vs/workbench/parts/debug/common/debugUtils'; import { ExtHostTerminalService } from 'vs/workbench/api/node/extHostTerminalService'; import { IDisposable } from 'vs/base/common/lifecycle'; import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; @@ -422,7 +422,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { } // DA -> VS Code - message = convertToVSCPaths(message, source => stringToUri(source)); + message = convertToVSCPaths(message, true); mythis._debugServiceProxy.$acceptDAMessage(debugAdapterHandle, message); }); @@ -454,7 +454,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { public $sendDAMessage(debugAdapterHandle: number, message: DebugProtocol.ProtocolMessage): Promise { // VS Code -> DA - message = convertToDAPaths(message, source => uriToString(source)); + message = convertToDAPaths(message, false); const tracker = this._debugAdaptersTrackers.get(debugAdapterHandle); // TODO@AW: same handle? if (tracker) { diff --git a/src/vs/workbench/parts/debug/common/debugUtils.ts b/src/vs/workbench/parts/debug/common/debugUtils.ts index 88b3c6e2251..0d00748fd61 100644 --- a/src/vs/workbench/parts/debug/common/debugUtils.ts +++ b/src/vs/workbench/parts/debug/common/debugUtils.ts @@ -79,78 +79,84 @@ export function isUri(s: string) { return s && s.match(_schemePattern); } -export function stringToUri(source: DebugProtocol.Source): void { - if (typeof source.path === 'string') { - if (isUri(source.path)) { - (source).path = uri.parse(source.path); +function stringToUri(path: string): string { + if (typeof path === 'string') { + if (isUri(path)) { + return uri.parse(path); } else { // assume path - if (isAbsolute_posix(source.path) || isAbsolute_win32(source.path)) { - (source).path = uri.file(source.path); + if (isAbsolute_posix(path) || isAbsolute_win32(path)) { + return uri.file(path); } else { // leave relative path as is } } } + return path; } -export function uriToString(source: DebugProtocol.Source): void { - if (typeof source.path === 'object') { - const u = uri.revive(source.path); +function uriToString(path: string): string { + if (typeof path === 'object') { + const u = uri.revive(path); if (u.scheme === 'file') { - source.path = u.fsPath; + return u.fsPath; } else { - source.path = u.toString(); + return u.toString(); } } + return path; } // path hooks helpers -export function convertToDAPaths(message: DebugProtocol.ProtocolMessage, fixSourcePaths: (source: DebugProtocol.Source) => void): DebugProtocol.ProtocolMessage { +interface PathContainer { + path?: string; +} + +export function convertToDAPaths(message: DebugProtocol.ProtocolMessage, toUri: boolean): DebugProtocol.ProtocolMessage { + + const fixPath = toUri ? stringToUri : uriToString; // since we modify Source.paths in the message in place, we need to make a copy of it (see #61129) const msg = deepClone(message); - convertPaths(msg, (toDA: boolean, source: DebugProtocol.Source | undefined) => { + convertPaths(msg, (toDA: boolean, source: PathContainer | undefined) => { if (toDA && source) { - fixSourcePaths(source); + source.path = fixPath(source.path); } }); return msg; } -export function convertToVSCPaths(message: DebugProtocol.ProtocolMessage, fixSourcePaths: (source: DebugProtocol.Source) => void): DebugProtocol.ProtocolMessage { +export function convertToVSCPaths(message: DebugProtocol.ProtocolMessage, toUri: boolean): DebugProtocol.ProtocolMessage { + + const fixPath = toUri ? stringToUri : uriToString; // since we modify Source.paths in the message in place, we need to make a copy of it (see #61129) const msg = deepClone(message); - convertPaths(msg, (toDA: boolean, source: DebugProtocol.Source | undefined) => { + convertPaths(msg, (toDA: boolean, source: PathContainer | undefined) => { if (!toDA && source) { - fixSourcePaths(source); + source.path = fixPath(source.path); } }); return msg; } -function convertPaths(msg: DebugProtocol.ProtocolMessage, fixSourcePaths: (toDA: boolean, source: DebugProtocol.Source | undefined) => void): void { - - const tmpSource: DebugProtocol.Source = { - path: '' - }; +function convertPaths(msg: DebugProtocol.ProtocolMessage, fixSourcePath: (toDA: boolean, source: PathContainer | undefined) => void): void { switch (msg.type) { case 'event': const event = msg; switch (event.event) { case 'output': - fixSourcePaths(false, (event).body.source); + fixSourcePath(false, (event).body.source); break; case 'loadedSource': - fixSourcePaths(false, (event).body.source); + fixSourcePath(false, (event).body.source); break; case 'breakpoint': - fixSourcePaths(false, (event).body.breakpoint.source); + fixSourcePath(false, (event).body.breakpoint.source); break; default: break; @@ -160,22 +166,16 @@ function convertPaths(msg: DebugProtocol.ProtocolMessage, fixSourcePaths: (toDA: const request = msg; switch (request.command) { case 'setBreakpoints': - fixSourcePaths(true, (request.arguments).source); + fixSourcePath(true, (request.arguments).source); break; case 'source': - fixSourcePaths(true, (request.arguments).source); + fixSourcePath(true, (request.arguments).source); break; case 'gotoTargets': - fixSourcePaths(true, (request.arguments).source); + fixSourcePath(true, (request.arguments).source); break; case 'launchVSCode': - request.arguments.args.forEach(a => { - if (a.path) { - tmpSource.path = a.path; - fixSourcePaths(false, tmpSource); - a.path = tmpSource.path; - } - }); + request.arguments.args.forEach(arg => fixSourcePath(false, arg)); break; default: break; @@ -186,24 +186,19 @@ function convertPaths(msg: DebugProtocol.ProtocolMessage, fixSourcePaths: (toDA: if (response.success) { switch (response.command) { case 'stackTrace': - const r1 = response; - r1.body.stackFrames.forEach(frame => fixSourcePaths(false, frame.source)); + (response).body.stackFrames.forEach(frame => fixSourcePath(false, frame.source)); break; case 'loadedSources': - const r2 = response; - r2.body.sources.forEach(source => fixSourcePaths(false, source)); + (response).body.sources.forEach(source => fixSourcePath(false, source)); break; case 'scopes': - const r3 = response; - r3.body.scopes.forEach(scope => fixSourcePaths(false, scope.source)); + (response).body.scopes.forEach(scope => fixSourcePath(false, scope.source)); break; case 'setFunctionBreakpoints': - const r4 = response; - r4.body.breakpoints.forEach(bp => fixSourcePaths(false, bp.source)); + (response).body.breakpoints.forEach(bp => fixSourcePath(false, bp.source)); break; case 'setBreakpoints': - const r5 = response; - r5.body.breakpoints.forEach(bp => fixSourcePaths(false, bp.source)); + (response).body.breakpoints.forEach(bp => fixSourcePath(false, bp.source)); break; default: break; From 9301aefe0f5b8c2505421ee57721e5bcfde2e117 Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Sun, 27 Jan 2019 09:04:25 +0100 Subject: [PATCH 182/274] node-debug2@1.31.4 --- build/builtInExtensions.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/builtInExtensions.json b/build/builtInExtensions.json index 9480537578b..99e7570459d 100644 --- a/build/builtInExtensions.json +++ b/build/builtInExtensions.json @@ -16,7 +16,7 @@ }, { "name": "ms-vscode.node-debug2", - "version": "1.31.3", + "version": "1.31.4", "repo": "https://github.com/Microsoft/vscode-node-debug2", "metadata": { "id": "36d19e17-7569-4841-a001-947eb18602b2", From 13b73269d92fde692268a8237e4eca3bae6fa70d Mon Sep 17 00:00:00 2001 From: Thien Do Date: Mon, 28 Jan 2019 00:57:32 +0700 Subject: [PATCH 183/274] Update nvmrc As noted in [the wiki](https://github.com/Microsoft/vscode/wiki/How-to-Contribute#prerequisites) node's version should be larger than 8.12 so 8.9.2 will not work. In fact, I think we only need to define the target version as `8` so we can have the latest version that satisfies the requirement (`>= 8.12.0, < 9.0.0`). At the time of writing it is `8.15` which works perfectly fine (install + watch + run). --- .nvmrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.nvmrc b/.nvmrc index 32c861f970d..45a4fb75db8 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -8.9.2 +8 From 186f21da29a13e89424fa4508a763eb73426860d Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Sun, 27 Jan 2019 18:55:07 +0000 Subject: [PATCH 184/274] Fix #67113 - missing context in search copy results --- .../parts/search/browser/searchActions.ts | 2 +- .../parts/search/common/searchModel.ts | 4 ++ .../search/test/common/searchResult.test.ts | 47 ++----------------- 3 files changed, 8 insertions(+), 45 deletions(-) diff --git a/src/vs/workbench/parts/search/browser/searchActions.ts b/src/vs/workbench/parts/search/browser/searchActions.ts index 965e0149c13..3dde8d15063 100644 --- a/src/vs/workbench/parts/search/browser/searchActions.ts +++ b/src/vs/workbench/parts/search/browser/searchActions.ts @@ -653,7 +653,7 @@ function matchToString(match: Match, indent = 0): string { const getFirstLinePrefix = () => `${match.range().startLineNumber},${match.range().startColumn}`; const getOtherLinePrefix = (i: number) => match.range().startLineNumber + i + ''; - const fullMatchLines = match.fullMatchText().split(/\r?\n/g); + const fullMatchLines = match.fullPreviewLines(); const largestPrefixSize = fullMatchLines.reduce((largest, _, i) => { const thisSize = i === 0 ? getFirstLinePrefix().length : diff --git a/src/vs/workbench/parts/search/common/searchModel.ts b/src/vs/workbench/parts/search/common/searchModel.ts index edd87128a3f..b700671faa0 100644 --- a/src/vs/workbench/parts/search/common/searchModel.ts +++ b/src/vs/workbench/parts/search/common/searchModel.ts @@ -135,6 +135,10 @@ export class Match { return thisMatchPreviewLines.join('\n'); } + fullPreviewLines(): string[] { + return this._fullPreviewLines.slice(this._fullPreviewRange.startLineNumber, this._fullPreviewRange.endLineNumber + 1); + } + getMatchString(): string { return this._oneLinePreviewText.substring(this._rangeInPreviewText.startColumn - 1, this._rangeInPreviewText.endColumn - 1); } diff --git a/src/vs/workbench/parts/search/test/common/searchResult.test.ts b/src/vs/workbench/parts/search/test/common/searchResult.test.ts index f374bea8d53..61421c17c10 100644 --- a/src/vs/workbench/parts/search/test/common/searchResult.test.ts +++ b/src/vs/workbench/parts/search/test/common/searchResult.test.ts @@ -40,6 +40,9 @@ suite('SearchResult', () => { assert.equal(lineMatch.range().startColumn, 1); assert.equal(lineMatch.range().endColumn, 4); assert.equal('file:///folder/file.txt>[2,1 -> 2,4]foo', lineMatch.id()); + + assert.equal(lineMatch.fullMatchText(), 'foo'); + assert.equal(lineMatch.fullMatchText(true), 'foo bar'); }); test('Line Match - Remove', function () { @@ -312,50 +315,6 @@ suite('SearchResult', () => { return voidPromise.then(() => assert.ok(testObject.isEmpty())); }); - //// ----- utils - //function lineHasDecorations(model: editor.IModel, lineNumber: number, decorations: { start: number; end: number; }[]): void { - // let lineDecorations:typeof decorations = []; - // let decs = model.getLineDecorations(lineNumber); - // for (let i = 0, len = decs.length; i < len; i++) { - // lineDecorations.push({ - // start: decs[i].range.startColumn, - // end: decs[i].range.endColumn - // }); - // } - // assert.deepEqual(lineDecorations, decorations); - //} - // - //function lineHasNoDecoration(model: editor.IModel, lineNumber: number): void { - // lineHasDecorations(model, lineNumber, []); - //} - // - //function lineHasDecoration(model: editor.IModel, lineNumber: number, start: number, end: number): void { - // lineHasDecorations(model, lineNumber, [{ - // start: start, - // end: end - // }]); - //} - //// ----- end utils - // - //test('Model Highlights', function () { - // - // let fileMatch = instantiation.createInstance(FileMatch, null, toUri('folder\\file.txt')); - // fileMatch.add(new Match(fileMatch, 'line2', 1, 0, 2)); - // fileMatch.connect(); - // lineHasDecoration(oneModel, 2, 1, 3); - //}); - // - //test('Dispose', () => { - // - // let fileMatch = instantiation.createInstance(FileMatch, null, toUri('folder\\file.txt')); - // fileMatch.add(new Match(fileMatch, 'line2', 1, 0, 2)); - // fileMatch.connect(); - // lineHasDecoration(oneModel, 2, 1, 3); - // - // fileMatch.dispose(); - // lineHasNoDecoration(oneModel, 2); - //}); - function aFileMatch(path: string, searchResult?: SearchResult, ...lineMatches: ITextSearchMatch[]): FileMatch { const rawMatch: IFileMatch = { resource: URI.file('/' + path), From c1c64f48f8b8bbff40dfb17c1bbf7793463dffcd Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Sun, 27 Jan 2019 19:06:36 +0000 Subject: [PATCH 185/274] Bump node-debug2 --- build/builtInExtensions.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/builtInExtensions.json b/build/builtInExtensions.json index 99e7570459d..368480b1118 100644 --- a/build/builtInExtensions.json +++ b/build/builtInExtensions.json @@ -16,7 +16,7 @@ }, { "name": "ms-vscode.node-debug2", - "version": "1.31.4", + "version": "1.31.5", "repo": "https://github.com/Microsoft/vscode-node-debug2", "metadata": { "id": "36d19e17-7569-4841-a001-947eb18602b2", From 130fdbee2b8a1c0d02282c6c4d4821a61c4b4b52 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Sun, 27 Jan 2019 15:06:33 -0800 Subject: [PATCH 186/274] reaction api --- src/vs/editor/common/modes.ts | 14 +++ src/vs/vscode.proposed.d.ts | 9 +- .../electron-browser/mainThreadComments.ts | 8 ++ src/vs/workbench/api/node/extHost.protocol.ts | 3 + src/vs/workbench/api/node/extHostComments.ts | 37 +++++++- .../comments/electron-browser/commentNode.ts | 90 ++++++++++++++++--- .../electron-browser/commentService.ts | 35 +++++++- .../electron-browser/commentThreadWidget.ts | 5 +- .../commentsEditorContribution.ts | 23 ++++- .../electron-browser/media/review.css | 33 +++++++ 10 files changed, 230 insertions(+), 27 deletions(-) diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index ae8a3b77861..1468afc9581 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -1229,6 +1229,14 @@ export interface NewCommentAction { actions: Command[]; } +/** + * @internal + */ +export interface CommentReaction { + readonly label?: string; + readonly hasReacted?: boolean; +} + /** * @internal */ @@ -1241,6 +1249,7 @@ export interface Comment { readonly canDelete?: boolean; readonly command?: Command; readonly isDraft?: boolean; + readonly commentReactions?: CommentReaction[]; } /** @@ -1284,6 +1293,11 @@ export interface DocumentCommentProvider { startDraftLabel?: string; deleteDraftLabel?: string; finishDraftLabel?: string; + + addReaction?(resource: URI, comment: Comment, reaction: CommentReaction, token: CancellationToken): Promise; + deleteReaction?(resource: URI, comment: Comment, reaction: CommentReaction, token: CancellationToken): Promise; + reactionGroup?: CommentReaction[]; + onDidChangeCommentThreads(): Event; } diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 77b915859c9..40399883818 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -820,7 +820,7 @@ declare module 'vscode' { interface CommentReaction { readonly label?: string; - readonly iconPath?: string | Uri; + readonly hasReacted?: boolean; } interface DocumentCommentProvider { @@ -857,10 +857,9 @@ declare module 'vscode' { deleteDraftLabel?: string; finishDraftLabel?: string; - commentReactions?: CommentReaction[]; - - addReaction(comment: Comment, reaction: CommentReaction): Promise; - deleteReaction(comment: Comment, reaction: CommentReaction): Promise; + addReaction?(document: TextDocument, comment: Comment, reaction: CommentReaction): Promise; + deleteReaction?(document: TextDocument, comment: Comment, reaction: CommentReaction): Promise; + reactionGroup?: CommentReaction[]; /** * Notify of updates to comment threads. diff --git a/src/vs/workbench/api/electron-browser/mainThreadComments.ts b/src/vs/workbench/api/electron-browser/mainThreadComments.ts index 1c6ebef3589..2eec4b8c85f 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadComments.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadComments.ts @@ -30,6 +30,7 @@ export class MainThreadDocumentCommentProvider implements modes.DocumentCommentP get startDraftLabel(): string { return this._features.startDraftLabel; } get deleteDraftLabel(): string { return this._features.deleteDraftLabel; } get finishDraftLabel(): string { return this._features.finishDraftLabel; } + get reactionGroup(): modes.CommentReaction[] { return this._features.reactionGroup; } constructor(proxy: ExtHostCommentsShape, handle: number, features: CommentProviderFeatures) { this._proxy = proxy; @@ -66,6 +67,13 @@ export class MainThreadDocumentCommentProvider implements modes.DocumentCommentP async finishDraft(uri, token): Promise { return this._proxy.$finishDraft(this._handle, uri); } + async addReaction(uri, comment: modes.Comment, reaction: modes.CommentReaction, token): Promise { + return this._proxy.$addReaction(this._handle, uri, comment, reaction); + } + async deleteReaction(uri, comment: modes.Comment, reaction: modes.CommentReaction, token): Promise { + return this._proxy.$deleteReaction(this._handle, uri, comment, reaction); + } + onDidChangeCommentThreads = null; } diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index d9f02bbeb03..72e15943a45 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -110,6 +110,7 @@ export interface CommentProviderFeatures { startDraftLabel?: string; deleteDraftLabel?: string; finishDraftLabel?: string; + reactionGroup?: vscode.CommentReaction[]; } export interface MainThreadCommentsShape extends IDisposable { @@ -1063,6 +1064,8 @@ export interface ExtHostCommentsShape { $startDraft(handle: number, document: UriComponents): Promise; $deleteDraft(handle: number, document: UriComponents): Promise; $finishDraft(handle: number, document: UriComponents): Promise; + $addReaction(handle: number, document: UriComponents, comment: modes.Comment, reaction: modes.CommentReaction): Promise; + $deleteReaction(handle: number, document: UriComponents, comment: modes.Comment, reaction: modes.CommentReaction): Promise; $provideWorkspaceComments(handle: number): Promise; } diff --git a/src/vs/workbench/api/node/extHostComments.ts b/src/vs/workbench/api/node/extHostComments.ts index e814ea68a15..3e76d66a03e 100644 --- a/src/vs/workbench/api/node/extHostComments.ts +++ b/src/vs/workbench/api/node/extHostComments.ts @@ -69,7 +69,8 @@ export class ExtHostComments implements ExtHostCommentsShape { this._proxy.$registerDocumentCommentProvider(handle, { startDraftLabel: provider.startDraftLabel, deleteDraftLabel: provider.deleteDraftLabel, - finishDraftLabel: provider.finishDraftLabel + finishDraftLabel: provider.finishDraftLabel, + reactionGroup: provider.reactionGroup }); this.registerListeners(handle, extensionId, provider); @@ -174,6 +175,34 @@ export class ExtHostComments implements ExtHostCommentsShape { }); } + $addReaction(handle: number, uri: UriComponents, comment: modes.Comment, reaction: modes.CommentReaction): Promise { + const data = this._documents.getDocumentData(URI.revive(uri)); + + if (!data || !data.document) { + throw new Error('Unable to retrieve document from URI'); + } + + const handlerData = this._documentProviders.get(handle); + + return asPromise(() => { + return handlerData.provider.addReaction(data.document, convertFromComment(comment), reaction); + }); + } + + $deleteReaction(handle: number, uri: UriComponents, comment: modes.Comment, reaction: modes.CommentReaction): Promise { + const data = this._documents.getDocumentData(URI.revive(uri)); + + if (!data || !data.document) { + throw new Error('Unable to retrieve document from URI'); + } + + const handlerData = this._documentProviders.get(handle); + + return asPromise(() => { + return handlerData.provider.deleteReaction(data.document, convertFromComment(comment), reaction); + }); + } + $provideDocumentComments(handle: number, uri: UriComponents): Promise { const data = this._documents.getDocumentData(URI.revive(uri)); if (!data || !data.document) { @@ -259,7 +288,8 @@ function convertFromComment(comment: modes.Comment): vscode.Comment { userIconPath: userIconPath, canEdit: comment.canEdit, canDelete: comment.canDelete, - isDraft: comment.isDraft + isDraft: comment.isDraft, + commentReactions: comment.commentReactions }; } @@ -275,6 +305,7 @@ function convertToComment(provider: vscode.DocumentCommentProvider | vscode.Work canEdit: canEdit, canDelete: canDelete, command: vscodeComment.command ? commandsConverter.toInternal(vscodeComment.command) : null, - isDraft: vscodeComment.isDraft + isDraft: vscodeComment.isDraft, + commentReactions: vscodeComment.commentReactions }; } diff --git a/src/vs/workbench/parts/comments/electron-browser/commentNode.ts b/src/vs/workbench/parts/comments/electron-browser/commentNode.ts index f1338bd4507..d81030836ec 100644 --- a/src/vs/workbench/parts/comments/electron-browser/commentNode.ts +++ b/src/vs/workbench/parts/comments/electron-browser/commentNode.ts @@ -7,9 +7,9 @@ import * as nls from 'vs/nls'; import * as dom from 'vs/base/browser/dom'; import * as modes from 'vs/editor/common/modes'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { ActionsOrientation, ActionItem, ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { Button } from 'vs/base/browser/ui/button/button'; -import { Action } from 'vs/base/common/actions'; +import { Action, IActionRunner } from 'vs/base/common/actions'; import { Disposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { ITextModel } from 'vs/editor/common/model'; @@ -30,6 +30,8 @@ import { Emitter, Event } from 'vs/base/common/event'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { assign } from 'vs/base/common/objects'; import { MarkdownString } from 'vs/base/common/htmlContent'; +import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; const UPDATE_COMMENT_LABEL = nls.localize('label.updateComment', "Update comment"); const UPDATE_IN_PROGRESS_LABEL = nls.localize('label.updatingComment', "Updating comment..."); @@ -49,6 +51,9 @@ export class CommentNode extends Disposable { private _isPendingLabel: HTMLElement; private _deleteAction: Action; + protected actionRunner?: IActionRunner; + protected toolbar: ToolBar; + private _onDidDelete = new Emitter(); public get domNode(): HTMLElement { @@ -66,7 +71,8 @@ export class CommentNode extends Disposable { private modelService: IModelService, private modeService: IModeService, private dialogService: IDialogService, - private notificationService: INotificationService + private notificationService: INotificationService, + private contextMenuService: IContextMenuService ) { super(); @@ -86,7 +92,9 @@ export class CommentNode extends Disposable { this._md = this.markdownRenderer.render(comment.body).element; this._body.appendChild(this._md); - this.createReactions(commentDetailsContainer); + if (this.comment.commentReactions) { + this.createReactions(commentDetailsContainer); + } this._domNode.setAttribute('aria-label', `${comment.userName}, ${comment.body.value}`); this._domNode.setAttribute('role', 'treeitem'); @@ -121,23 +129,79 @@ export class CommentNode extends Disposable { if (actions.length) { const actionsContainer = dom.append(header, dom.$('.comment-actions.hidden')); - const actionBar = new ActionBar(actionsContainer, {}); - this._toDispose.push(actionBar); + + this.toolbar = new ToolBar(actionsContainer, this.contextMenuService, { + actionItemProvider: action => this.actionItemProvider(action as Action), + orientation: ActionsOrientation.HORIZONTAL + }); + this.registerActionBarListeners(actionsContainer); - actions.forEach(action => actionBar.push(action, { label: false, icon: true })); + let reactionActions = []; + let reactionGroup = this.commentService.getReactionGroup(this.owner); + if (reactionGroup) { + reactionActions = reactionGroup.map((reaction) => { + return new Action(`reaction.command.${reaction.label}`, `${reaction.label}`, '', true, async () => { + try { + await this.commentService.addReaction(this.owner, this.resource, this.comment, reaction); + } catch (e) { + const error = e.message + ? nls.localize('commentAddReactionError', "Deleting the comment reaction failed: {0}.", e.message) + : nls.localize('commentAddReactionDefaultError', "Deleting the comment reaction failed"); + this.notificationService.error(error); + } + }); + }); + } + + this.toolbar.setActions(actions, reactionActions)(); + this._toDispose.push(this.toolbar); } } + actionItemProvider(action: Action) { + let options = {}; + if (action.id === 'comment.delete' || action.id === 'comment.edit') { + options = { label: false, icon: true }; + } else { + options = { label: true, icon: true }; + } + + let item = new ActionItem({}, action, options); + return item; + } + private createReactions(commentDetailsContainer: HTMLElement): void { - let reactions = ['❤️', '🎉', '😄']; + const actionsContainer = dom.append(commentDetailsContainer, dom.$('div.comment-reactions')); + const actionBar = new ActionBar(actionsContainer, {}); + this._toDispose.push(actionBar); - const reactionsBar = dom.append(commentDetailsContainer, dom.$('div.comment-reactions')); + let reactionActions = this.comment.commentReactions.map(reaction => { + return new Action(`reaction.${reaction.label}`, `${reaction.label}`, reaction.hasReacted ? 'active' : '', true, async () => { + try { + if (reaction.hasReacted) { + await this.commentService.deleteReaction(this.owner, this.resource, this.comment, reaction); + } else { + await this.commentService.addReaction(this.owner, this.resource, this.comment, reaction); + } + } catch (e) { + let error: string; - reactions.forEach(reaction => { - let btn = new Button(reactionsBar); - btn.label = reaction; + if (reaction.hasReacted) { + error = e.message + ? nls.localize('commentDeleteReactionError', "Deleting the comment reaction failed: {0}.", e.message) + : nls.localize('commentDeleteReactionDefaultError', "Deleting the comment reaction failed"); + } else { + error = e.message + ? nls.localize('commentAddReactionError', "Deleting the comment reaction failed: {0}.", e.message) + : nls.localize('commentAddReactionDefaultError', "Deleting the comment reaction failed"); + } + this.notificationService.error(error); + } + }); }); + + reactionActions.forEach(action => actionBar.push(action, { label: true, icon: true })); } private createCommentEditor(): void { @@ -272,7 +336,7 @@ export class CommentNode extends Disposable { actionsContainer.classList.remove('hidden'); })); - this._toDispose.push(dom.addDisposableListener(this._domNode, 'mouseleave', (e: MouseEvent) => { + this._toDispose.push(dom.addDisposableListener(this._domNode, 'mouseleave', () => { if (!this._domNode.contains(document.activeElement)) { actionsContainer.classList.add('hidden'); } diff --git a/src/vs/workbench/parts/comments/electron-browser/commentService.ts b/src/vs/workbench/parts/comments/electron-browser/commentService.ts index 0a806cc0332..7173a1b02db 100644 --- a/src/vs/workbench/parts/comments/electron-browser/commentService.ts +++ b/src/vs/workbench/parts/comments/electron-browser/commentService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CommentThread, DocumentCommentProvider, CommentThreadChangedEvent, CommentInfo, Comment } from 'vs/editor/common/modes'; +import { CommentThread, DocumentCommentProvider, CommentThreadChangedEvent, CommentInfo, Comment, CommentReaction } from 'vs/editor/common/modes'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { Event, Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; @@ -55,6 +55,9 @@ export interface ICommentService { getStartDraftLabel(owner: string): string; getDeleteDraftLabel(owner: string): string; getFinishDraftLabel(owner: string): string; + addReaction(owner: string, resource: URI, comment: Comment, reaction: CommentReaction): Promise; + deleteReaction(owner: string, resource: URI, comment: Comment, reaction: CommentReaction): Promise; + getReactionGroup(owner: string): CommentReaction[]; } export class CommentService extends Disposable implements ICommentService { @@ -178,6 +181,36 @@ export class CommentService extends Disposable implements ICommentService { } } + async addReaction(owner: string, resource: URI, comment: Comment, reaction: CommentReaction): Promise { + const commentProvider = this._commentProviders.get(owner); + + if (commentProvider && commentProvider.addReaction) { + return commentProvider.addReaction(resource, comment, reaction, CancellationToken.None); + } else { + throw new Error('Not supported'); + } + } + + async deleteReaction(owner: string, resource: URI, comment: Comment, reaction: CommentReaction): Promise { + const commentProvider = this._commentProviders.get(owner); + + if (commentProvider && commentProvider.deleteReaction) { + return commentProvider.deleteReaction(resource, comment, reaction, CancellationToken.None); + } else { + throw new Error('Not supported'); + } + } + + getReactionGroup(owner: string): CommentReaction[] { + const commentProvider = this._commentProviders.get(owner); + + if (commentProvider) { + return commentProvider.reactionGroup; + } + + return null; + } + getStartDraftLabel(owner: string): string | null { const commentProvider = this._commentProviders.get(owner); diff --git a/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts b/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts index b009344ee58..980e3d0d8b6 100644 --- a/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts +++ b/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts @@ -39,6 +39,7 @@ import { CommentNode } from 'vs/workbench/parts/comments/electron-browser/commen import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { ITextModel } from 'vs/editor/common/model'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; export const COMMENTEDITOR_DECORATION_KEY = 'commenteditordecoration'; const COLLAPSE_ACTION_CLASS = 'expand-review-action octicon octicon-x'; @@ -97,6 +98,7 @@ export class ReviewZoneWidget extends ZoneWidget { private openerService: IOpenerService, private dialogService: IDialogService, private notificationService: INotificationService, + private contextMenuService: IContextMenuService, editor: ICodeEditor, owner: string, commentThread: modes.CommentThread, @@ -491,7 +493,8 @@ export class ReviewZoneWidget extends ZoneWidget { this.modelService, this.modeService, this.dialogService, - this.notificationService); + this.notificationService, + this.contextMenuService); this._disposables.push(newCommentNode); this._disposables.push(newCommentNode.onDidDelete(deletedNode => { diff --git a/src/vs/workbench/parts/comments/electron-browser/commentsEditorContribution.ts b/src/vs/workbench/parts/comments/electron-browser/commentsEditorContribution.ts index c6f1b95096c..dd4b304492c 100644 --- a/src/vs/workbench/parts/comments/electron-browser/commentsEditorContribution.ts +++ b/src/vs/workbench/parts/comments/electron-browser/commentsEditorContribution.ts @@ -35,6 +35,8 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; import { overviewRulerCommentingRangeForeground } from 'vs/workbench/parts/comments/electron-browser/commentGlyphWidget'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { STATUS_BAR_ITEM_HOVER_BACKGROUND, STATUS_BAR_ITEM_ACTIVE_BACKGROUND } from 'vs/workbench/common/theme'; export const ctxReviewPanelVisible = new RawContextKey('reviewPanelVisible', false); @@ -175,7 +177,8 @@ export class ReviewController implements IEditorContribution { @IModelService private readonly modelService: IModelService, @ICodeEditorService private readonly codeEditorService: ICodeEditorService, @IOpenerService private readonly openerService: IOpenerService, - @IDialogService private readonly dialogService: IDialogService + @IDialogService private readonly dialogService: IDialogService, + @IContextMenuService private readonly contextMenuService: IContextMenuService, ) { this.editor = editor; this.globalToDispose = []; @@ -393,7 +396,7 @@ export class ReviewController implements IEditorContribution { } }); added.forEach(thread => { - let zoneWidget = new ReviewZoneWidget(this.instantiationService, this.modeService, this.modelService, this.themeService, this.commentService, this.openerService, this.dialogService, this.notificationService, this.editor, e.owner, thread, null, draftMode, {}); + let zoneWidget = new ReviewZoneWidget(this.instantiationService, this.modeService, this.modelService, this.themeService, this.commentService, this.openerService, this.dialogService, this.notificationService, this.contextMenuService, this.editor, e.owner, thread, null, draftMode, {}); zoneWidget.display(thread.range.startLineNumber); this._commentWidgets.push(zoneWidget); this._commentInfos.filter(info => info.owner === e.owner)[0].threads.push(thread); @@ -412,7 +415,7 @@ export class ReviewController implements IEditorContribution { // add new comment this._reviewPanelVisible.set(true); - this._newCommentWidget = new ReviewZoneWidget(this.instantiationService, this.modeService, this.modelService, this.themeService, this.commentService, this.openerService, this.dialogService, this.notificationService, this.editor, ownerId, { + this._newCommentWidget = new ReviewZoneWidget(this.instantiationService, this.modeService, this.modelService, this.themeService, this.commentService, this.openerService, this.dialogService, this.notificationService, this.contextMenuService, this.editor, ownerId, { extensionId: extensionId, threadId: null, resource: null, @@ -570,7 +573,7 @@ export class ReviewController implements IEditorContribution { thread.collapsibleState = modes.CommentThreadCollapsibleState.Expanded; } - let zoneWidget = new ReviewZoneWidget(this.instantiationService, this.modeService, this.modelService, this.themeService, this.commentService, this.openerService, this.dialogService, this.notificationService, this.editor, info.owner, thread, pendingComment, info.draftMode, {}); + let zoneWidget = new ReviewZoneWidget(this.instantiationService, this.modeService, this.modelService, this.themeService, this.commentService, this.openerService, this.dialogService, this.notificationService, this.contextMenuService, this.editor, info.owner, thread, pendingComment, info.draftMode, {}); zoneWidget.display(thread.range.startLineNumber); this._commentWidgets.push(zoneWidget); }); @@ -741,4 +744,16 @@ registerThemingParticipant((theme, collector) => { } `); } + + const statusBarItemHoverBackground = theme.getColor(STATUS_BAR_ITEM_HOVER_BACKGROUND); + if (statusBarItemHoverBackground) { + collector.addRule(`.monaco-editor .review-widget .body .review-comment .review-comment-contents .comment-reactions .action-item a.action-label:hover { background-color: ${statusBarItemHoverBackground}; border: 1px solid grey; + border-radius: 3px; }`); + } + + const statusBarItemActiveBackground = theme.getColor(STATUS_BAR_ITEM_ACTIVE_BACKGROUND); + if (statusBarItemActiveBackground) { + collector.addRule(`.monaco-editor .review-widget .body .review-comment .review-comment-contents .comment-reactions .action-item a.action-label:active { background-color: ${statusBarItemActiveBackground}; border: 1px solid grey; + border-radius: 3px;}`); + } }); diff --git a/src/vs/workbench/parts/comments/electron-browser/media/review.css b/src/vs/workbench/parts/comments/electron-browser/media/review.css index 11015f5d7e9..57347578cb3 100644 --- a/src/vs/workbench/parts/comments/electron-browser/media/review.css +++ b/src/vs/workbench/parts/comments/electron-browser/media/review.css @@ -56,6 +56,13 @@ line-height: 18px; } +.monaco-editor .review-widget .body .review-comment .comment-title .monaco-dropdown .toolbar-toggle-more { + width: 16px; + height: 18px; + line-height: 18px; + vertical-align: middle; +} + .monaco-editor .review-widget .body .comment-body blockquote { margin: 0 7px 0 5px; padding: 0 16px 0 10px; @@ -120,6 +127,32 @@ padding-top: 4px; } +.monaco-editor .review-widget .body .review-comment .review-comment-contents .comment-reactions { + margin-top: 8px; + min-height: 25px; +} + +.monaco-editor .review-widget .body .review-comment .review-comment-contents .comment-reactions .monaco-action-bar .actions-container { + justify-content: flex-start; +} + +.monaco-editor .review-widget .body .review-comment .review-comment-contents .comment-reactions .action-item .action-label { + padding: 2px 5px 0px 5px; + white-space: pre; + text-align: center; + font-size: 14px; + margin: 4px; +} + +.monaco-editor .review-widget .body .review-comment .review-comment-contents .comment-reactions .action-item a.action-label.active { + border: 1px solid grey; + border-radius: 3px; +} + +.monaco-editor .review-widget .body .review-comment .review-comment-contents .comment-reactions .action-item a.action-label.disabled { + opacity: 0.6; +} + .monaco-editor.vs-dark .review-widget .body span.created_at { color: #e0e0e0; } From fe70dbfdbedaa1483fb99156af502249085e38b1 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Sun, 27 Jan 2019 19:42:57 +0000 Subject: [PATCH 187/274] Fix #66323 - add border to search inputs for light theme in panel --- src/vs/workbench/parts/search/browser/media/searchview.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/workbench/parts/search/browser/media/searchview.css b/src/vs/workbench/parts/search/browser/media/searchview.css index 030d85f1cdf..fd591c75b55 100644 --- a/src/vs/workbench/parts/search/browser/media/searchview.css +++ b/src/vs/workbench/parts/search/browser/media/searchview.css @@ -390,3 +390,7 @@ .hc-black .monaco-workbench .search-view .linematch { line-height: 20px; } + +.vs .panel .search-view .monaco-inputbox { + border: 1px solid #ddd; +} From 0928c96524f4554afd1a67eed1032448eb12237b Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Sun, 27 Jan 2019 20:41:38 +0000 Subject: [PATCH 188/274] Fix #66184 - nicer display for very long terminal setting --- .../parts/preferences/browser/settingsTreeModels.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/preferences/browser/settingsTreeModels.ts b/src/vs/workbench/parts/preferences/browser/settingsTreeModels.ts index ff8557ac6c0..7deca62b6af 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsTreeModels.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsTreeModels.ts @@ -71,6 +71,8 @@ export class SettingsTreeNewExtensionsElement extends SettingsTreeElement { } export class SettingsTreeSettingElement extends SettingsTreeElement { + private static MAX_DESC_LINES = 20; + setting: ISetting; private _displayCategory: string; @@ -164,7 +166,13 @@ export class SettingsTreeSettingElement extends SettingsTreeElement { } this.overriddenScopeList = overriddenScopeList; - this.description = this.setting.description.join('\n'); + if (this.setting.description.length > SettingsTreeSettingElement.MAX_DESC_LINES) { + const truncatedDescLines = this.setting.description.slice(0, SettingsTreeSettingElement.MAX_DESC_LINES); + truncatedDescLines.push('[...]'); + this.description = truncatedDescLines.join('\n'); + } else { + this.description = this.setting.description.join('\n'); + } if (this.setting.enum && (!this.setting.type || settingTypeEnumRenderable(this.setting.type))) { this.valueType = SettingValueType.Enum; From 8ee9d312a9b8e75bedf7c44946c5ec4f95943f3f Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 28 Jan 2019 00:03:31 +0000 Subject: [PATCH 189/274] Fix #67114 - don't open search items when context menu is invoked --- src/vs/platform/list/browser/listService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/list/browser/listService.ts b/src/vs/platform/list/browser/listService.ts index 0957f20ddf8..515329e5de1 100644 --- a/src/vs/platform/list/browser/listService.ts +++ b/src/vs/platform/list/browser/listService.ts @@ -637,7 +637,7 @@ export class TreeResourceNavigator2 extends Disposable { } private onSelection(e: ITreeEvent): void { - if (!e.browserEvent) { + if (!e.browserEvent || e.browserEvent.type === 'contextmenu') { return; } From b353e5385fb414d8aa03888f7a6919ff3b66740a Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Sun, 27 Jan 2019 16:29:06 -0800 Subject: [PATCH 190/274] make reaction api --- src/vs/vscode.proposed.d.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index deed9a9a28a..8a322618092 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -859,8 +859,8 @@ declare module 'vscode' { commentReactions?: CommentReaction[]; - addReaction(comment: Comment, reaction: CommentReaction): Promise; - deleteReaction(comment: Comment, reaction: CommentReaction): Promise; + addReaction?(comment: Comment, reaction: CommentReaction): Promise; + deleteReaction?(comment: Comment, reaction: CommentReaction): Promise; /** * Notify of updates to comment threads. From aad260bc7d3901752650fca61a5629841b0ec9a3 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Sun, 27 Jan 2019 19:03:09 -0800 Subject: [PATCH 191/274] Adopt new css provider interface --- .../.vscode/settings.json | 5 ++ .../css-language-features/server/package.json | 2 +- .../server/src/cssServerMain.ts | 23 +++------ .../server/src/customData.ts | 47 +++++++++++++++++++ .../server/src/utils/languageFacts.ts | 23 --------- .../css-language-features/server/yarn.lock | 8 ++-- 6 files changed, 63 insertions(+), 45 deletions(-) create mode 100644 extensions/css-language-features/.vscode/settings.json create mode 100644 extensions/css-language-features/server/src/customData.ts delete mode 100644 extensions/css-language-features/server/src/utils/languageFacts.ts diff --git a/extensions/css-language-features/.vscode/settings.json b/extensions/css-language-features/.vscode/settings.json new file mode 100644 index 00000000000..5b30e2983c2 --- /dev/null +++ b/extensions/css-language-features/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "prettier.semi": true, + "prettier.singleQuote": true, + "prettier.printWidth": 120, +} \ No newline at end of file diff --git a/extensions/css-language-features/server/package.json b/extensions/css-language-features/server/package.json index b48257bb0c5..678ddaec04c 100644 --- a/extensions/css-language-features/server/package.json +++ b/extensions/css-language-features/server/package.json @@ -9,7 +9,7 @@ }, "main": "./out/cssServerMain", "dependencies": { - "vscode-css-languageservice": "^3.0.13-next.7", + "vscode-css-languageservice": "^3.0.13-next.8", "vscode-languageserver": "^5.1.0" }, "devDependencies": { diff --git a/extensions/css-language-features/server/src/cssServerMain.ts b/extensions/css-language-features/server/src/cssServerMain.ts index c5fd3bdb65d..3ef166f1b80 100644 --- a/extensions/css-language-features/server/src/cssServerMain.ts +++ b/extensions/css-language-features/server/src/cssServerMain.ts @@ -7,15 +7,14 @@ import { createConnection, IConnection, TextDocuments, InitializeParams, InitializeResult, ServerCapabilities, ConfigurationRequest, WorkspaceFolder } from 'vscode-languageserver'; import URI from 'vscode-uri'; -import * as fs from 'fs'; import { TextDocument, CompletionList, Position } from 'vscode-languageserver-types'; -import { getCSSLanguageService, getSCSSLanguageService, getLESSLanguageService, LanguageSettings, LanguageService, Stylesheet, CSSData } from 'vscode-css-languageservice'; +import { getCSSLanguageService, getSCSSLanguageService, getLESSLanguageService, LanguageSettings, LanguageService, Stylesheet } from 'vscode-css-languageservice'; import { getLanguageModelCache } from './languageModelCache'; import { getPathCompletionParticipant } from './pathCompletion'; import { formatError, runSafe } from './utils/runner'; import { getDocumentContext } from './utils/documentContext'; -import { parseCSSData } from './utils/languageFacts'; +import { getDataProviders } from './customData'; export interface Settings { css: LanguageSettings; @@ -66,17 +65,7 @@ connection.onInitialize((params: InitializeParams): InitializeResult => { } const dataPaths: string[] = params.initializationOptions.dataPaths; - - const customDataCollections: CSSData[] = []; - - dataPaths.forEach(p => { - if (fs.existsSync(p)) { - const data = parseCSSData(fs.readFileSync(p, 'utf-8')); - customDataCollections.push(data); - } else { - return; - } - }); + const customDataProviders = getDataProviders(dataPaths); function getClientCapability(name: string, def: T) { const keys = name.split('.'); @@ -93,9 +82,9 @@ connection.onInitialize((params: InitializeParams): InitializeResult => { scopedSettingsSupport = !!getClientCapability('workspace.configuration', false); foldingRangeLimit = getClientCapability('textDocument.foldingRange.rangeLimit', Number.MAX_VALUE); - languageServices.css = getCSSLanguageService({ customDataCollections }); - languageServices.scss = getSCSSLanguageService({ customDataCollections }); - languageServices.less = getLESSLanguageService({ customDataCollections }); + languageServices.css = getCSSLanguageService({ customDataProviders }); + languageServices.scss = getSCSSLanguageService({ customDataProviders }); + languageServices.less = getLESSLanguageService({ customDataProviders }); const capabilities: ServerCapabilities = { // Tell the client that the server works in FULL text document sync mode diff --git a/extensions/css-language-features/server/src/customData.ts b/extensions/css-language-features/server/src/customData.ts new file mode 100644 index 00000000000..3b253648d42 --- /dev/null +++ b/extensions/css-language-features/server/src/customData.ts @@ -0,0 +1,47 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CSSData, ICSSDataProvider } from 'vscode-css-languageservice'; +import * as fs from 'fs'; + +export function getDataProviders(dataPaths: string[]): ICSSDataProvider[] { + const providers = dataPaths.map(p => { + if (fs.existsSync(p)) { + const data = parseCSSData(fs.readFileSync(p, 'utf-8')); + return { + provideProperties: () => data.properties || [], + provideAtDirectives: () => data.atDirectives || [], + providePseudoClasses: () => data.pseudoClasses || [], + providePseudoElements: () => data.pseudoElements || [] + }; + } else { + return { + provideProperties: () => [], + provideAtDirectives: () => [], + providePseudoClasses: () => [], + providePseudoElements: () => [] + }; + } + }); + + return providers; +} + +function parseCSSData(source: string): CSSData { + let rawData: any; + + try { + rawData = JSON.parse(source); + } catch (err) { + return {}; + } + + return { + properties: rawData.properties || [], + atDirectives: rawData.atdirectives || [], + pseudoClasses: rawData.pseudoclasses || [], + pseudoElements: rawData.pseudoelements || [] + }; +} diff --git a/extensions/css-language-features/server/src/utils/languageFacts.ts b/extensions/css-language-features/server/src/utils/languageFacts.ts deleted file mode 100644 index c6d9b2ee641..00000000000 --- a/extensions/css-language-features/server/src/utils/languageFacts.ts +++ /dev/null @@ -1,23 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { CSSData } from 'vscode-css-languageservice'; - -export function parseCSSData(source: string): CSSData { - let rawData: any; - - try { - rawData = JSON.parse(source); - } catch (err) { - return {}; - } - - return { - properties: rawData.properties || [], - atDirectives: rawData.atdirectives || [], - pseudoClasses: rawData.pseudoclasses || [], - pseudoElements: rawData.pseudoelements || [] - }; -} diff --git a/extensions/css-language-features/server/yarn.lock b/extensions/css-language-features/server/yarn.lock index 53a82e931a8..af0f5782494 100644 --- a/extensions/css-language-features/server/yarn.lock +++ b/extensions/css-language-features/server/yarn.lock @@ -229,10 +229,10 @@ supports-color@5.4.0: dependencies: has-flag "^3.0.0" -vscode-css-languageservice@^3.0.13-next.7: - version "3.0.13-next.7" - resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-3.0.13-next.7.tgz#48392edbf35ce60c54aeaf8d3e83c994d6c4c84e" - integrity sha512-2Z28n0CVwwNpdDmoIbjUMJPnchm+eRJE/PhhjXss/p0CnSJfXzbll3Ay1V+nDLW2NIGap0CJFbbfT+cilyivIA== +vscode-css-languageservice@^3.0.13-next.8: + version "3.0.13-next.8" + resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-3.0.13-next.8.tgz#3e019c9166914fcbd8b71530ff32b438698206fa" + integrity sha512-YjoIMCvZSegs7UOogehQjrfNybBL3MDVN2jx/uxFFw0VKFGAiyZkjfQiipEg36jbKL4LKHiBUoutoHYMF2PGfw== dependencies: vscode-languageserver-types "^3.13.0" vscode-nls "^4.0.0" From d9e6741a655f8f0ee4fb3bfdf218f0304d1b573b Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 28 Jan 2019 00:10:40 +0000 Subject: [PATCH 192/274] Fix #67073 - hide fancy selectbox when scrolling settings --- .../electron-browser/settingsEditor2.ts | 41 ++++++++----------- 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts b/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts index 5e8b41d0e3e..83d58a7c502 100644 --- a/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts +++ b/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts @@ -605,9 +605,9 @@ export class SettingsEditor2 extends BaseEditor { this.settingRenderers.allRenderers)); this.settingsTree.getHTMLElement().attributes.removeNamedItem('tabindex'); - // this._register(this.settingsTree.onDidScroll(() => { - // this.updateTreeScrollSync(); - // })); + this._register(this.settingsTree.onDidScroll(() => { + this.updateTreeScrollSync(); + })); } private notifyNoSaveNeeded() { @@ -644,36 +644,29 @@ export class SettingsEditor2 extends BaseEditor { // this.updateTreePagingByScroll(); - const element = this.tocTreeModel.children[0]; + // const element = this.tocTreeModel.children[0]; // const elementToSync = this.settingsTree.getFirstVisibleElement(); // const element = elementToSync instanceof SettingsTreeSettingElement ? elementToSync.parent : // elementToSync instanceof SettingsTreeGroupElement ? elementToSync : // null; - if (element && this.tocTree.getSelection()[0] !== element) { - // this.tocTree.reveal(element); - // const elementTop = this.tocTree.getRelativeTop(element); - // collapseAll(this.tocTree, element); - // if (elementTop < 0 || elementTop > 1) { - // this.tocTree.reveal(element); - // } else { - // this.tocTree.reveal(element, elementTop); - // } + // if (element && this.tocTree.getSelection()[0] !== element) { + // this.tocTree.reveal(element); + // const elementTop = this.tocTree.getRelativeTop(element); + // collapseAll(this.tocTree, element); + // if (elementTop < 0 || elementTop > 1) { + // this.tocTree.reveal(element); + // } else { + // this.tocTree.reveal(element, elementTop); + // } - // this.tocTree.expand(element); + // this.tocTree.expand(element); - // this.tocTree.setSelection([element]); - // this.tocTree.setFocus(element, { fromScroll: true }); - } + // this.tocTree.setSelection([element]); + // this.tocTree.setFocus(element, { fromScroll: true }); + // } } - // private updateTreePagingByScroll(): void { - // const lastVisibleElement = this.settingsTree.getLastVisibleElement(); - // if (lastVisibleElement && this.settingsTreeDataSource.pageTo(lastVisibleElement.index)) { - // this.renderTree(); - // } - // } - private updateChangedSetting(key: string, value: any): Promise { // ConfigurationService displays the error if this fails. // Force a render afterwards because onDidConfigurationUpdate doesn't fire if the update doesn't result in an effective setting value change From 1b575168183b8e1d3a988f4cc8a5c7d15bce3c7c Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 28 Jan 2019 01:23:37 +0000 Subject: [PATCH 193/274] Fix #65451 - keep search tree element collapse state when refreshing --- .../parts/search/browser/searchView.ts | 96 ++++++++++--------- .../search/test/browser/searchViewlet.test.ts | 11 +-- 2 files changed, 51 insertions(+), 56 deletions(-) diff --git a/src/vs/workbench/parts/search/browser/searchView.ts b/src/vs/workbench/parts/search/browser/searchView.ts index e333d5aea17..31f506bd990 100644 --- a/src/vs/workbench/parts/search/browser/searchView.ts +++ b/src/vs/workbench/parts/search/browser/searchView.ts @@ -65,49 +65,6 @@ import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/un const $ = dom.$; -function createResultIterator(searchResult: SearchResult, collapseResults: ISearchConfigurationProperties['collapseResults']): Iterator> { - const folderMatches = searchResult.folderMatches() - .filter(fm => !fm.isEmpty()) - .sort(searchMatchComparer); - - if (folderMatches.length === 1) { - return createFolderIterator(folderMatches[0], collapseResults); - } - - const foldersIt = Iterator.fromArray(folderMatches); - return Iterator.map(foldersIt, folderMatch => { - const children = createFolderIterator(folderMatch, collapseResults); - return >{ element: folderMatch, children }; - }); -} - -function createFolderIterator(folderMatch: FolderMatch, collapseResults: ISearchConfigurationProperties['collapseResults']): Iterator> { - const filesIt = Iterator.fromArray( - folderMatch.matches() - .sort(searchMatchComparer)); - - return Iterator.map(filesIt, fileMatch => { - const children = createFileIterator(fileMatch); - - const collapsed = collapseResults === 'alwaysCollapse' || (fileMatch.matches().length > 10 && collapseResults !== 'alwaysExpand'); - - return >{ element: fileMatch, children, collapsed }; - }); -} - -function createFileIterator(fileMatch: FileMatch): Iterator> { - const matchesIt = Iterator.from( - fileMatch.matches() - .sort(searchMatchComparer)); - return Iterator.map(matchesIt, r => (>{ element: r })); -} - -export function createIterator(match: FolderMatch | FileMatch | SearchResult, collapseResults: ISearchConfigurationProperties['collapseResults']): Iterator> { - return match instanceof SearchResult ? createResultIterator(match, collapseResults) : - match instanceof FolderMatch ? createFolderIterator(match, collapseResults) : - createFileIterator(match); -} - export class SearchView extends Viewlet implements IViewlet, IPanel { private static readonly MAX_TEXT_RESULTS = 10000; @@ -483,21 +440,68 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { refreshTree(event?: IChangeEvent): void { const collapseResults = this.configurationService.getValue('search').collapseResults; if (!event || event.added || event.removed) { - this.tree.setChildren(null, createResultIterator(this.viewModel.searchResult, collapseResults)); + this.tree.setChildren(null, this.createResultIterator(collapseResults)); } else { event.elements.forEach(element => { if (element instanceof FolderMatch) { // The folder may or may not be in the tree. Refresh the whole thing. - this.tree.setChildren(null, createResultIterator(this.viewModel.searchResult, collapseResults)); + this.tree.setChildren(null, this.createResultIterator(collapseResults)); return; } const root = element instanceof SearchResult ? null : element; - this.tree.setChildren(root, createIterator(element, collapseResults)); + this.tree.setChildren(root, this.createIterator(element, collapseResults)); }); } } + private createResultIterator(collapseResults: ISearchConfigurationProperties['collapseResults']): Iterator> { + const folderMatches = this.searchResult.folderMatches() + .filter(fm => !fm.isEmpty()) + .sort(searchMatchComparer); + + if (folderMatches.length === 1) { + return this.createFolderIterator(folderMatches[0], collapseResults); + } + + const foldersIt = Iterator.fromArray(folderMatches); + return Iterator.map(foldersIt, folderMatch => { + const children = this.createFolderIterator(folderMatch, collapseResults); + return >{ element: folderMatch, children }; + }); + } + + private createFolderIterator(folderMatch: FolderMatch, collapseResults: ISearchConfigurationProperties['collapseResults']): Iterator> { + const filesIt = Iterator.fromArray( + folderMatch.matches() + .sort(searchMatchComparer)); + + return Iterator.map(filesIt, fileMatch => { + const children = this.createFileIterator(fileMatch); + + let nodeExists = true; + try { this.tree.getNode(fileMatch); } catch (e) { nodeExists = false; } + + const collapsed = nodeExists ? undefined : + (collapseResults === 'alwaysCollapse' || (fileMatch.matches().length > 10 && collapseResults !== 'alwaysExpand')); + + return >{ element: fileMatch, children, collapsed }; + }); + } + + private createFileIterator(fileMatch: FileMatch): Iterator> { + const matchesIt = Iterator.from( + fileMatch.matches() + .sort(searchMatchComparer)); + return Iterator.map(matchesIt, r => (>{ element: r })); + } + + private createIterator(match: FolderMatch | FileMatch | SearchResult, collapseResults: ISearchConfigurationProperties['collapseResults']): Iterator> { + return match instanceof SearchResult ? this.createResultIterator(collapseResults) : + match instanceof FolderMatch ? this.createFolderIterator(match, collapseResults) : + this.createFileIterator(match); + } + private replaceAll(): void { if (this.viewModel.searchResult.count() === 0) { return; diff --git a/src/vs/workbench/parts/search/test/browser/searchViewlet.test.ts b/src/vs/workbench/parts/search/test/browser/searchViewlet.test.ts index 8cfe81dba7f..24925478596 100644 --- a/src/vs/workbench/parts/search/test/browser/searchViewlet.test.ts +++ b/src/vs/workbench/parts/search/test/browser/searchViewlet.test.ts @@ -12,11 +12,8 @@ import { TestInstantiationService } from 'vs/platform/instantiation/test/common/ import { IFileMatch, ITextSearchMatch, OneLineRange, QueryType } from 'vs/platform/search/common/search'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { TestWorkspace } from 'vs/platform/workspace/test/common/testWorkspace'; -import { FileMatch, Match, SearchResult, RenderableMatch, searchMatchComparer } from 'vs/workbench/parts/search/common/searchModel'; +import { FileMatch, Match, searchMatchComparer, SearchResult } from 'vs/workbench/parts/search/common/searchModel'; import { TestContextService } from 'vs/workbench/test/workbenchTestServices'; -import { createIterator } from 'vs/workbench/parts/search/browser/searchView'; -import { ITreeElement } from 'vs/base/browser/ui/tree/tree'; -import { Iterator } from 'vs/base/common/iterator'; suite('Search - Viewlet', () => { let instantiation: TestInstantiationService; @@ -63,12 +60,6 @@ suite('Search - Viewlet', () => { assert.equal(fileMatch.id(), 'file:///c%3A/foo'); assert.equal(lineMatch.id(), 'file:///c%3A/foo>[2,1 -> 2,2]b'); - - const resultIterator = createIterator(result, 'auto'); - const first = resultIterator.next(); - - assert(!!first.value!.children); - assert.equal((>>first.value!.children).next().value!.element.id(), 'file:///c%3A/foo>[2,1 -> 2,2]b'); }); test('Comparer', () => { From 3815bb15fdce7fa08c8a49f215e4aec00279774b Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Sun, 27 Jan 2019 22:22:50 -0800 Subject: [PATCH 194/274] Update service and adopt HTML provider interface --- .../.vscode/settings.json | 5 ++- .../server/package.json | 4 +-- .../server/src/customData.ts | 29 +++++++++++++++++ .../server/src/htmlServerMain.ts | 31 ++----------------- .../server/src/modes/languageModes.ts | 7 ++--- .../server/src/utils/tagDefinitions.ts | 10 ------ .../html-language-features/server/yarn.lock | 16 +++++----- 7 files changed, 49 insertions(+), 53 deletions(-) create mode 100644 extensions/html-language-features/server/src/customData.ts delete mode 100644 extensions/html-language-features/server/src/utils/tagDefinitions.ts diff --git a/extensions/html-language-features/.vscode/settings.json b/extensions/html-language-features/.vscode/settings.json index e46111f13be..569ac10cf8f 100644 --- a/extensions/html-language-features/.vscode/settings.json +++ b/extensions/html-language-features/.vscode/settings.json @@ -1,3 +1,6 @@ { - "editor.insertSpaces": false + "editor.insertSpaces": false, + "prettier.semi": true, + "prettier.singleQuote": true, + "prettier.printWidth": 120, } \ No newline at end of file diff --git a/extensions/html-language-features/server/package.json b/extensions/html-language-features/server/package.json index a1c09e1aa2d..337521cc817 100644 --- a/extensions/html-language-features/server/package.json +++ b/extensions/html-language-features/server/package.json @@ -9,8 +9,8 @@ }, "main": "./out/htmlServerMain", "dependencies": { - "vscode-css-languageservice": "^3.0.13-next.6", - "vscode-html-languageservice": "^2.1.11-next.4", + "vscode-css-languageservice": "^3.0.13-next.8", + "vscode-html-languageservice": "^2.1.11-next.6", "vscode-languageserver": "^5.1.0", "vscode-languageserver-types": "^3.13.0", "vscode-nls": "^4.0.0", diff --git a/extensions/html-language-features/server/src/customData.ts b/extensions/html-language-features/server/src/customData.ts new file mode 100644 index 00000000000..8aa57038288 --- /dev/null +++ b/extensions/html-language-features/server/src/customData.ts @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IHTMLDataProvider, HTMLDataProvider } from 'vscode-html-languageservice'; +import * as fs from 'fs'; + +export function getDataProviders(dataPaths?: string[]): IHTMLDataProvider[] { + if (!dataPaths) { + return []; + } + + const providers: IHTMLDataProvider[] = []; + + dataPaths.forEach((path, i) => { + try { + if (fs.existsSync(path)) { + const htmlData = JSON.parse(fs.readFileSync(path, 'utf-8')); + + providers.push(new HTMLDataProvider(`customProvider${i}`, htmlData)); + } + } catch (err) { + console.log(`Failed to laod tag from ${path}`); + } + }); + + return providers; +} \ No newline at end of file diff --git a/extensions/html-language-features/server/src/htmlServerMain.ts b/extensions/html-language-features/server/src/htmlServerMain.ts index bcc3feedf12..c904afe354c 100644 --- a/extensions/html-language-features/server/src/htmlServerMain.ts +++ b/extensions/html-language-features/server/src/htmlServerMain.ts @@ -11,7 +11,6 @@ import { } from 'vscode-languageserver'; import { TextDocument, Diagnostic, DocumentLink, SymbolInformation } from 'vscode-languageserver-types'; import { getLanguageModes, LanguageModes, Settings } from './modes/languageModes'; -import * as fs from 'fs'; import { format } from './modes/formatting'; import { pushAll } from './utils/arrays'; @@ -20,8 +19,7 @@ import uri from 'vscode-uri'; import { formatError, runSafe, runSafeAsync } from './utils/runner'; import { getFoldingRanges } from './modes/htmlFolding'; -import { parseHTMLData } from './utils/tagDefinitions'; -import { HTMLData } from 'vscode-html-languageservice'; +import { getDataProviders } from './customData'; namespace TagCloseRequest { export const type: RequestType = new RequestType('html/tag'); @@ -92,37 +90,14 @@ connection.onInitialize((params: InitializeParams): InitializeResult => { } const dataPaths: string[] = params.initializationOptions.dataPaths; - - let allHtmlData: HTMLData = { - tags: [], - globalAttributes: [], - valueSetMap: {} - }; - - if (dataPaths) { - dataPaths.forEach(path => { - try { - if (fs.existsSync(path)) { - const htmlData = parseHTMLData(fs.readFileSync(path, 'utf-8')); - if (htmlData.tags) { - allHtmlData.tags = allHtmlData.tags!.concat(htmlData.tags); - } - if (htmlData.globalAttributes) { - allHtmlData.globalAttributes = allHtmlData.globalAttributes!.concat(htmlData.globalAttributes); - } - } - } catch (err) { - console.log(`Failed to laod tag from ${path}`); - } - }); - } + const providers = getDataProviders(dataPaths); const workspace = { get settings() { return globalSettings; }, get folders() { return workspaceFolders; } }; - languageModes = getLanguageModes(initializationOptions ? initializationOptions.embeddedLanguages : { css: true, javascript: true }, workspace, allHtmlData); + languageModes = getLanguageModes(initializationOptions ? initializationOptions.embeddedLanguages : { css: true, javascript: true }, workspace, providers); documents.onDidClose(e => { languageModes.onDocumentRemoved(e.document); diff --git a/extensions/html-language-features/server/src/modes/languageModes.ts b/extensions/html-language-features/server/src/modes/languageModes.ts index 63d295c014b..048076ed70d 100644 --- a/extensions/html-language-features/server/src/modes/languageModes.ts +++ b/extensions/html-language-features/server/src/modes/languageModes.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { getLanguageService as getHTMLLanguageService, DocumentContext, HTMLData } from 'vscode-html-languageservice'; +import { getLanguageService as getHTMLLanguageService, DocumentContext, IHTMLDataProvider } from 'vscode-html-languageservice'; import { CompletionItem, Location, SignatureHelp, Definition, TextEdit, TextDocument, Diagnostic, DocumentLink, Range, Hover, DocumentHighlight, CompletionList, Position, FormattingOptions, SymbolInformation, FoldingRange @@ -66,9 +66,8 @@ export interface LanguageModeRange extends Range { attributeValue?: boolean; } -export function getLanguageModes(supportedLanguages: { [languageId: string]: boolean; }, workspace: Workspace, customData?: HTMLData): LanguageModes { - const customDataCollections = customData ? [customData] : []; - const htmlLanguageService = getHTMLLanguageService({ customDataCollections }); +export function getLanguageModes(supportedLanguages: { [languageId: string]: boolean; }, workspace: Workspace, customDataProviders?: IHTMLDataProvider[]): LanguageModes { + const htmlLanguageService = getHTMLLanguageService({ customDataProviders }); let documentRegions = getLanguageModelCache(10, 60, document => getDocumentRegions(htmlLanguageService, document)); diff --git a/extensions/html-language-features/server/src/utils/tagDefinitions.ts b/extensions/html-language-features/server/src/utils/tagDefinitions.ts deleted file mode 100644 index 110a2acc5d7..00000000000 --- a/extensions/html-language-features/server/src/utils/tagDefinitions.ts +++ /dev/null @@ -1,10 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { HTMLData } from 'vscode-html-languageservice'; - -export function parseHTMLData(source: string): HTMLData { - return JSON.parse(source); -} \ No newline at end of file diff --git a/extensions/html-language-features/server/yarn.lock b/extensions/html-language-features/server/yarn.lock index a1ed0ca3f33..0c5fc9913e6 100644 --- a/extensions/html-language-features/server/yarn.lock +++ b/extensions/html-language-features/server/yarn.lock @@ -229,18 +229,18 @@ supports-color@5.4.0: dependencies: has-flag "^3.0.0" -vscode-css-languageservice@^3.0.13-next.6: - version "3.0.13-next.6" - resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-3.0.13-next.6.tgz#0329ea1da29ca84d1821cd32ee10bca0ee4c50cd" - integrity sha512-wC8zaFWHNnqIaOT4LXByy3NyTl916uHxGy3U3cpV7Gw7F8ENylSGM2RAO+l7NohIbP0WZlet441HEwSbb+bZbQ== +vscode-css-languageservice@^3.0.13-next.8: + version "3.0.13-next.8" + resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-3.0.13-next.8.tgz#3e019c9166914fcbd8b71530ff32b438698206fa" + integrity sha512-YjoIMCvZSegs7UOogehQjrfNybBL3MDVN2jx/uxFFw0VKFGAiyZkjfQiipEg36jbKL4LKHiBUoutoHYMF2PGfw== dependencies: vscode-languageserver-types "^3.13.0" vscode-nls "^4.0.0" -vscode-html-languageservice@^2.1.11-next.4: - version "2.1.11-next.4" - resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-2.1.11-next.4.tgz#bcfe68ca17300d7f845889be3cb947af95dab409" - integrity sha512-dQOcdqLhMZuAWhSoe/AqqvVo2klkWpCHwp14dWvgrAfUyKM7l0e0kk9lbf8kt1Kn2J9ukDTySIcOkbHaVSwcHA== +vscode-html-languageservice@^2.1.11-next.6: + version "2.1.11-next.6" + resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-2.1.11-next.6.tgz#66eb7a10d84ac91d5985b45cfa704ce05fe3e638" + integrity sha512-BCShNFqflm1XpjMJrxAz4tWSAdA1cqJi2XrI3/eHzqcHzw4kO/l3ApaZPdci77CAcOdWKyDmmzSau0UwR+CkYQ== dependencies: vscode-languageserver-types "^3.13.0" vscode-nls "^4.0.0" From 0ba2b1d078dc427e191c74f55f6e7085db8d20d8 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Sun, 27 Jan 2019 23:05:13 -0800 Subject: [PATCH 195/274] Update css-service to fix NPE exceptions --- extensions/css-language-features/server/package.json | 2 +- extensions/css-language-features/server/yarn.lock | 8 ++++---- extensions/html-language-features/server/package.json | 2 +- extensions/html-language-features/server/yarn.lock | 8 ++++---- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/extensions/css-language-features/server/package.json b/extensions/css-language-features/server/package.json index 678ddaec04c..5fe60307113 100644 --- a/extensions/css-language-features/server/package.json +++ b/extensions/css-language-features/server/package.json @@ -9,7 +9,7 @@ }, "main": "./out/cssServerMain", "dependencies": { - "vscode-css-languageservice": "^3.0.13-next.8", + "vscode-css-languageservice": "^3.0.13-next.9", "vscode-languageserver": "^5.1.0" }, "devDependencies": { diff --git a/extensions/css-language-features/server/yarn.lock b/extensions/css-language-features/server/yarn.lock index af0f5782494..0c35b246af8 100644 --- a/extensions/css-language-features/server/yarn.lock +++ b/extensions/css-language-features/server/yarn.lock @@ -229,10 +229,10 @@ supports-color@5.4.0: dependencies: has-flag "^3.0.0" -vscode-css-languageservice@^3.0.13-next.8: - version "3.0.13-next.8" - resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-3.0.13-next.8.tgz#3e019c9166914fcbd8b71530ff32b438698206fa" - integrity sha512-YjoIMCvZSegs7UOogehQjrfNybBL3MDVN2jx/uxFFw0VKFGAiyZkjfQiipEg36jbKL4LKHiBUoutoHYMF2PGfw== +vscode-css-languageservice@^3.0.13-next.9: + version "3.0.13-next.9" + resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-3.0.13-next.9.tgz#1f3ba55fb7444d8eab2c20df60ea5a95ae450cce" + integrity sha512-ooq2KOge+fF7K3mk43C+3/a7roVBDgHUSWfPiDrhOcT7iHAFSmyG/I7yZpH29mXincIHo22hGCoWzEerJF1otw== dependencies: vscode-languageserver-types "^3.13.0" vscode-nls "^4.0.0" diff --git a/extensions/html-language-features/server/package.json b/extensions/html-language-features/server/package.json index 337521cc817..eb1eae43671 100644 --- a/extensions/html-language-features/server/package.json +++ b/extensions/html-language-features/server/package.json @@ -9,7 +9,7 @@ }, "main": "./out/htmlServerMain", "dependencies": { - "vscode-css-languageservice": "^3.0.13-next.8", + "vscode-css-languageservice": "^3.0.13-next.9", "vscode-html-languageservice": "^2.1.11-next.6", "vscode-languageserver": "^5.1.0", "vscode-languageserver-types": "^3.13.0", diff --git a/extensions/html-language-features/server/yarn.lock b/extensions/html-language-features/server/yarn.lock index 0c5fc9913e6..3455b53ad43 100644 --- a/extensions/html-language-features/server/yarn.lock +++ b/extensions/html-language-features/server/yarn.lock @@ -229,10 +229,10 @@ supports-color@5.4.0: dependencies: has-flag "^3.0.0" -vscode-css-languageservice@^3.0.13-next.8: - version "3.0.13-next.8" - resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-3.0.13-next.8.tgz#3e019c9166914fcbd8b71530ff32b438698206fa" - integrity sha512-YjoIMCvZSegs7UOogehQjrfNybBL3MDVN2jx/uxFFw0VKFGAiyZkjfQiipEg36jbKL4LKHiBUoutoHYMF2PGfw== +vscode-css-languageservice@^3.0.13-next.9: + version "3.0.13-next.9" + resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-3.0.13-next.9.tgz#1f3ba55fb7444d8eab2c20df60ea5a95ae450cce" + integrity sha512-ooq2KOge+fF7K3mk43C+3/a7roVBDgHUSWfPiDrhOcT7iHAFSmyG/I7yZpH29mXincIHo22hGCoWzEerJF1otw== dependencies: vscode-languageserver-types "^3.13.0" vscode-nls "^4.0.0" From 33783baaa19ba89e30cf72469ba2086243997f51 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Sun, 27 Jan 2019 23:16:41 -0800 Subject: [PATCH 196/274] Fix #64022 --- .../client/src/customData.ts | 30 +++++++++++++------ .../client/src/htmlMain.ts | 7 +++-- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/extensions/html-language-features/client/src/customData.ts b/extensions/html-language-features/client/src/customData.ts index fb89f811769..467940bc736 100644 --- a/extensions/html-language-features/client/src/customData.ts +++ b/extensions/html-language-features/client/src/customData.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as path from 'path'; -import { workspace, WorkspaceFolder } from 'vscode'; +import { workspace, WorkspaceFolder, extensions } from 'vscode'; interface ExperimentalConfig { experimental?: { @@ -12,14 +12,11 @@ interface ExperimentalConfig { }; } -export function getCustomDataPathsInAllWorkspaces(workspaceFolders: WorkspaceFolder[] | undefined) { +export function getCustomDataPathsInAllWorkspaces(workspaceFolders: WorkspaceFolder[] | undefined): string[] { const dataPaths: string[] = []; - if (!workspaceFolders) { - return { - dataPaths - }; + return dataPaths; } workspaceFolders.forEach(wf => { @@ -39,7 +36,22 @@ export function getCustomDataPathsInAllWorkspaces(workspaceFolders: WorkspaceFol } }); - return { - dataPaths - }; + return dataPaths; +} + +export function getCustomDataPathsFromAllExtensions(): string[] { + const dataPaths: string[] = []; + + for (const extension of extensions.all) { + const contributes = extension.packageJSON && extension.packageJSON.contributes; + + if (contributes && contributes.html && contributes.html.customData && Array.isArray(contributes.html.customData)) { + const relativePaths: string[] = contributes.html.customData; + relativePaths.forEach(rp => { + dataPaths.push(path.resolve(extension.extensionPath, rp)); + }); + } + } + + return dataPaths; } diff --git a/extensions/html-language-features/client/src/htmlMain.ts b/extensions/html-language-features/client/src/htmlMain.ts index b9d3978b304..571c1d3ff47 100644 --- a/extensions/html-language-features/client/src/htmlMain.ts +++ b/extensions/html-language-features/client/src/htmlMain.ts @@ -13,7 +13,7 @@ import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind, Re import { EMPTY_ELEMENTS } from './htmlEmptyTagsShared'; import { activateTagClosing } from './tagClosing'; import TelemetryReporter from 'vscode-extension-telemetry'; -import { getCustomDataPathsInAllWorkspaces } from './customData'; +import { getCustomDataPathsInAllWorkspaces, getCustomDataPathsFromAllExtensions } from './customData'; namespace TagCloseRequest { export const type: RequestType = new RequestType('html/tag'); @@ -50,7 +50,10 @@ export function activate(context: ExtensionContext) { let documentSelector = ['html', 'handlebars', 'razor']; let embeddedLanguages = { css: true, javascript: true }; - let { dataPaths } = getCustomDataPathsInAllWorkspaces(workspace.workspaceFolders); + let dataPaths = [ + ...getCustomDataPathsInAllWorkspaces(workspace.workspaceFolders), + ...getCustomDataPathsFromAllExtensions() + ]; // Options to control the language client let clientOptions: LanguageClientOptions = { From 1f28c5fc681f4c01226460b6d1c7e91b8acb4a5b Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 28 Jan 2019 09:31:06 +0100 Subject: [PATCH 197/274] #66907 Delay listening to workspace change events until it is complete --- .../standalone/browser/simpleServices.ts | 4 + src/vs/platform/workspace/common/workspace.ts | 8 +- .../relauncher.contribution.ts | 88 +++++++++++-------- .../node/configurationService.ts | 8 ++ .../workbench/test/workbenchTestServices.ts | 4 + 5 files changed, 74 insertions(+), 38 deletions(-) diff --git a/src/vs/editor/standalone/browser/simpleServices.ts b/src/vs/editor/standalone/browser/simpleServices.ts index b463eae52f8..e92924064ef 100644 --- a/src/vs/editor/standalone/browser/simpleServices.ts +++ b/src/vs/editor/standalone/browser/simpleServices.ts @@ -521,6 +521,10 @@ export class SimpleWorkspaceContextService implements IWorkspaceContextService { this.workspace = { id: '4064f6ec-cb38-4ad0-af64-ee6467e63c82', folders: [new WorkspaceFolder({ uri: resource, name: '', index: 0 })] }; } + getCompleteWorkspace(): Promise { + return Promise.resolve(this.getWorkspace()); + } + public getWorkspace(): IWorkspace { return this.workspace; } diff --git a/src/vs/platform/workspace/common/workspace.ts b/src/vs/platform/workspace/common/workspace.ts index 8abb0ca735d..9521062c3ac 100644 --- a/src/vs/platform/workspace/common/workspace.ts +++ b/src/vs/platform/workspace/common/workspace.ts @@ -46,7 +46,13 @@ export interface IWorkspaceContextService { onDidChangeWorkspaceFolders: Event; /** - * Provides access to the workspace object the platform is running with. + * Provides access to the complete workspace object. + */ + getCompleteWorkspace(): Promise; + + /** + * Provides access to the workspace object the window is running with. + * Use `getCompleteWorkspace` to get complete workspace object. */ getWorkspace(): IWorkspace; diff --git a/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts b/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts index 64a3d5341d6..21d823df728 100644 --- a/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts +++ b/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose, Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { IWorkbenchContributionsRegistry, IWorkbenchContribution, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { Registry } from 'vs/platform/registry/common/platform'; import { IWindowsService, IWindowService, IWindowsConfiguration } from 'vs/platform/windows/common/windows'; @@ -42,11 +42,6 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo private fileWatcherExclude: object; private useGridLayout: boolean; - private firstFolderResource?: URI; - private extensionHostRestarter: RunOnceScheduler; - - private onDidChangeWorkspaceFoldersUnbind: IDisposable; - constructor( @IWindowsService private readonly windowsService: IWindowsService, @IWindowService private readonly windowService: IWindowService, @@ -54,23 +49,11 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo @IEnvironmentService private readonly envService: IEnvironmentService, @IDialogService private readonly dialogService: IDialogService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, - @IExtensionService private readonly extensionService: IExtensionService ) { super(); - const workspace = this.contextService.getWorkspace(); - this.firstFolderResource = workspace.folders.length > 0 ? workspace.folders[0].uri : undefined; - this.extensionHostRestarter = new RunOnceScheduler(() => this.extensionService.restartExtensionHost(), 10); - this.onConfigurationChange(configurationService.getValue(), false); - this.handleWorkbenchState(); - - this.registerListeners(); - } - - private registerListeners(): void { this._register(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationChange(this.configurationService.getValue(), true))); - this._register(this.contextService.onDidChangeWorkbenchState(() => setTimeout(() => this.handleWorkbenchState()))); } private onConfigurationChange(config: IConfiguration, notify: boolean): void { @@ -155,6 +138,55 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo } } + private doConfirm(message: string, detail: string, primaryButton: string, confirmed: () => void): void { + this.windowService.isFocused().then(focused => { + if (focused) { + return this.dialogService.confirm({ + type: 'info', + message, + detail, + primaryButton + }).then(res => { + if (res.confirmed) { + confirmed(); + } + }); + } + + return undefined; + }); + } +} + +export class WorkspaceChangeExtHostRelauncher extends Disposable implements IWorkbenchContribution { + + private firstFolderResource?: URI; + private extensionHostRestarter: RunOnceScheduler; + + private onDidChangeWorkspaceFoldersUnbind: IDisposable; + + constructor( + @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, + @IExtensionService extensionService: IExtensionService + ) { + super(); + + this.extensionHostRestarter = this._register(new RunOnceScheduler(() => extensionService.restartExtensionHost(), 10)); + + this.contextService.getCompleteWorkspace() + .then(workspace => { + this.firstFolderResource = workspace.folders.length > 0 ? workspace.folders[0].uri : undefined; + this.handleWorkbenchState(); + this._register(this.contextService.onDidChangeWorkbenchState(() => setTimeout(() => this.handleWorkbenchState()))); + }); + + this._register(toDisposable(() => { + if (this.onDidChangeWorkspaceFoldersUnbind) { + this.onDidChangeWorkspaceFoldersUnbind.dispose(); + } + })); + } + private handleWorkbenchState(): void { // React to folder changes when we are in workspace state @@ -187,26 +219,8 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo this.extensionHostRestarter.schedule(); // buffer calls to extension host restart } } - - private doConfirm(message: string, detail: string, primaryButton: string, confirmed: () => void): void { - this.windowService.isFocused().then(focused => { - if (focused) { - return this.dialogService.confirm({ - type: 'info', - message, - detail, - primaryButton - }).then(res => { - if (res.confirmed) { - confirmed(); - } - }); - } - - return undefined; - }); - } } const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); workbenchRegistry.registerWorkbenchContribution(SettingsChangeRelauncher, LifecyclePhase.Restored); +workbenchRegistry.registerWorkbenchContribution(WorkspaceChangeExtHostRelauncher, LifecyclePhase.Restored); diff --git a/src/vs/workbench/services/configuration/node/configurationService.ts b/src/vs/workbench/services/configuration/node/configurationService.ts index e07ca65249a..4a8acfeda40 100644 --- a/src/vs/workbench/services/configuration/node/configurationService.ts +++ b/src/vs/workbench/services/configuration/node/configurationService.ts @@ -43,6 +43,8 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat public _serviceBrand: any; private workspace: Workspace; + private resolvePromise: Promise; + private resolveCallback: () => void; private _configuration: Configuration; private defaultConfiguration: DefaultConfigurationModel; private userConfiguration: UserConfiguration; @@ -70,6 +72,7 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat constructor(private environmentService: IEnvironmentService, private workspaceSettingsRootFolder: string = FOLDER_CONFIG_FOLDER_NAME) { super(); + this.resolvePromise = new Promise(c => this.resolveCallback = c); this.defaultConfiguration = new DefaultConfigurationModel(); this.userConfiguration = this._register(new UserConfiguration(environmentService.appSettingsPath)); this.workspaceConfiguration = this._register(new WorkspaceConfiguration(environmentService)); @@ -84,6 +87,10 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat // Workspace Context Service Impl + public getCompleteWorkspace(): Promise { + return this.resolvePromise.then(() => this.getWorkspace()); + } + public getWorkspace(): Workspace { return this.workspace; } @@ -317,6 +324,7 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat for (const workspaceFolder of changedWorkspaceFolders) { this.onWorkspaceFolderConfigurationChanged(workspaceFolder); } + this.resolveCallback(); }); } diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index 010050b0397..6096db99a7c 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -132,6 +132,10 @@ export class TestContextService implements IWorkspaceContextService { return WorkbenchState.EMPTY; } + getCompleteWorkspace(): Promise { + return Promise.resolve(this.getWorkspace()); + } + public getWorkspace(): IWorkbenchWorkspace { return this.workspace; } From 0562d28586c7c8e835ec8431a65f69f30559f0c9 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 28 Jan 2019 11:32:15 +0100 Subject: [PATCH 198/274] fixes #67188 --- src/vs/editor/contrib/referenceSearch/referencesTree.ts | 9 ++++++++- .../editor/contrib/referenceSearch/referencesWidget.ts | 7 ++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/contrib/referenceSearch/referencesTree.ts b/src/vs/editor/contrib/referenceSearch/referencesTree.ts index e9282d70092..296d55db965 100644 --- a/src/vs/editor/contrib/referenceSearch/referencesTree.ts +++ b/src/vs/editor/contrib/referenceSearch/referencesTree.ts @@ -20,7 +20,7 @@ import { escape } from 'vs/base/common/strings'; import { Disposable } from 'vs/base/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; -import { IListVirtualDelegate, IKeyboardNavigationLabelProvider } from 'vs/base/browser/ui/list/list'; +import { IListVirtualDelegate, IKeyboardNavigationLabelProvider, IIdentityProvider } from 'vs/base/browser/ui/list/list'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { basename } from 'vs/base/common/paths'; @@ -93,6 +93,13 @@ export class StringRepresentationProvider implements IKeyboardNavigationLabelPro } } +export class IdentityProvider implements IIdentityProvider { + + getId(element: TreeElement): { toString(): string; } { + return element.id; + } +} + //#region render: File class FileReferencesTemplate extends Disposable { diff --git a/src/vs/editor/contrib/referenceSearch/referencesWidget.ts b/src/vs/editor/contrib/referenceSearch/referencesWidget.ts index 1b4bfd87b19..1978ad1e501 100644 --- a/src/vs/editor/contrib/referenceSearch/referencesWidget.ts +++ b/src/vs/editor/contrib/referenceSearch/referencesWidget.ts @@ -21,7 +21,7 @@ import { IModelDeltaDecoration, TrackedRangeStickiness } from 'vs/editor/common/ import { ModelDecorationOptions, TextModel } from 'vs/editor/common/model/textModel'; import { Location } from 'vs/editor/common/modes'; import { ITextEditorModel, ITextModelService } from 'vs/editor/common/services/resolverService'; -import { AriaProvider, DataSource, Delegate, FileReferencesRenderer, OneReferenceRenderer, TreeElement, StringRepresentationProvider } from 'vs/editor/contrib/referenceSearch/referencesTree'; +import { AriaProvider, DataSource, Delegate, FileReferencesRenderer, OneReferenceRenderer, TreeElement, StringRepresentationProvider, IdentityProvider } from 'vs/editor/contrib/referenceSearch/referencesTree'; import * as nls from 'vs/nls'; import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -358,11 +358,12 @@ export class ReferenceWidget extends PeekViewWidget { this._instantiationService.createInstance(OneReferenceRenderer), ]; - const treeOptions = { + const treeOptions: IAsyncDataTreeOptions = { ariaLabel: nls.localize('treeAriaLabel', "References"), keyboardSupport: this._defaultTreeKeyboardSupport, accessibilityProvider: new AriaProvider(), - keyboardNavigationLabelProvider: this._instantiationService.createInstance(StringRepresentationProvider) + keyboardNavigationLabelProvider: this._instantiationService.createInstance(StringRepresentationProvider), + identityProvider: new IdentityProvider() }; const treeDataSource = this._instantiationService.createInstance(DataSource); From 366bfe96bf2fe4cff37de895c3fa2ccb776eb621 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Mon, 28 Jan 2019 11:49:44 +0100 Subject: [PATCH 199/274] Add dependsOn to task JSON schema for customized tasks Fixes #63874 --- src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.ts b/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.ts index f2a5ff58613..17b87290553 100644 --- a/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.ts +++ b/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.ts @@ -341,6 +341,7 @@ let taskConfiguration: IJSONSchema = { description: nls.localize('JsonSchema.tasks.matchers', 'The problem matcher(s) to use. Can either be a string or a problem matcher definition or an array of strings and problem matchers.') }, runOptions: Objects.deepClone(runOptions), + dependsOn: Objects.deepClone(dependsOn), } }; From 392d0bee87fda7e5e16662e15fde1a6492c1ffca Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Mon, 28 Jan 2019 12:03:18 +0100 Subject: [PATCH 200/274] Fix converting of URL to request options (fixes #66892) --- .../services/extensions/node/proxyResolver.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/services/extensions/node/proxyResolver.ts b/src/vs/workbench/services/extensions/node/proxyResolver.ts index c330eb973a2..c954a089f1d 100644 --- a/src/vs/workbench/services/extensions/node/proxyResolver.ts +++ b/src/vs/workbench/services/extensions/node/proxyResolver.ts @@ -233,14 +233,17 @@ function patches(originals: typeof http | typeof https, agent: http.Agent, setti if (!options.socketPath && (config === 'override' || config === 'on' && !options.agent) && options.agent !== agent) { if (url) { - const parsed = typeof url === 'string' ? nodeurl.parse(url) : url; - options = { + const parsed = typeof url === 'string' ? new nodeurl.URL(url) : url; + const urlOptions = { protocol: parsed.protocol, - hostname: parsed.hostname, + hostname: parsed.hostname.lastIndexOf('[', 0) === 0 ? parsed.hostname.slice(1, -1) : parsed.hostname, port: parsed.port, - path: parsed.pathname, - ...options + path: `${parsed.pathname}${parsed.search}` }; + if (parsed.username || parsed.password) { + options.auth = `${parsed.username}:${parsed.password}`; + } + options = { ...urlOptions, ...options }; } else { options = { ...options }; } From 5974c2ecaab418fe22e2340eacbbdc8eb2ccfec4 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 28 Jan 2019 12:05:22 +0100 Subject: [PATCH 201/274] move ui extensions to product json --- .../node/extensionManagementService.ts | 3 +- .../node/multiExtensionManagement.ts | 3 +- .../platform/extensions/common/extensions.ts | 37 +---------------- .../extensions/node/extensionsUtil.ts | 40 +++++++++++++++++++ src/vs/platform/node/product.ts | 1 + .../node/extensionsWorkbenchService.ts | 3 +- 6 files changed, 48 insertions(+), 39 deletions(-) create mode 100644 src/vs/platform/extensions/node/extensionsUtil.ts diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index b3656aee654..ab6982abed3 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -44,8 +44,9 @@ import { Schemas } from 'vs/base/common/network'; import { CancellationToken } from 'vs/base/common/cancellation'; import { getPathFromAmdModule } from 'vs/base/common/amd'; import { getManifest } from 'vs/platform/extensionManagement/node/extensionManagementUtil'; -import { IExtensionManifest, ExtensionType, ExtensionIdentifierWithVersion, isUIExtension } from 'vs/platform/extensions/common/extensions'; +import { IExtensionManifest, ExtensionType, ExtensionIdentifierWithVersion } from 'vs/platform/extensions/common/extensions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { isUIExtension } from 'vs/platform/extensions/node/extensionsUtil'; const ERROR_SCANNING_SYS_EXTENSIONS = 'scanningSystem'; const ERROR_SCANNING_USER_EXTENSIONS = 'scanningUser'; diff --git a/src/vs/platform/extensionManagement/node/multiExtensionManagement.ts b/src/vs/platform/extensionManagement/node/multiExtensionManagement.ts index 2e1ea397eb5..f5764c324d9 100644 --- a/src/vs/platform/extensionManagement/node/multiExtensionManagement.ts +++ b/src/vs/platform/extensionManagement/node/multiExtensionManagement.ts @@ -9,7 +9,7 @@ import { IExtensionManagementServerService, IExtensionManagementServer, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { flatten } from 'vs/base/common/arrays'; -import { isUIExtension, ExtensionType, IExtensionManifest } from 'vs/platform/extensions/common/extensions'; +import { ExtensionType, IExtensionManifest } from 'vs/platform/extensions/common/extensions'; import { URI } from 'vs/base/common/uri'; import { Disposable } from 'vs/base/common/lifecycle'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -19,6 +19,7 @@ import { getManifest } from 'vs/platform/extensionManagement/node/extensionManag import { ILogService } from 'vs/platform/log/common/log'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { localize } from 'vs/nls'; +import { isUIExtension } from 'vs/platform/extensions/node/extensionsUtil'; export class MultiExtensionManagementService extends Disposable implements IExtensionManagementService { diff --git a/src/vs/platform/extensions/common/extensions.ts b/src/vs/platform/extensions/common/extensions.ts index e00d3ed9d5f..1fc3e1913be 100644 --- a/src/vs/platform/extensions/common/extensions.ts +++ b/src/vs/platform/extensions/common/extensions.ts @@ -3,10 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { getGalleryExtensionId, areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import * as strings from 'vs/base/common/strings'; -import { isNonEmptyArray } from 'vs/base/common/arrays'; import { ILocalization } from 'vs/platform/localizations/common/localizations'; import { URI } from 'vs/base/common/uri'; @@ -174,39 +172,6 @@ export interface IExtension { readonly location: URI; } -const uiExtensions = new Set(); -uiExtensions.add('msjsdiag.debugger-for-chrome'); - -export function isUIExtension(manifest: IExtensionManifest, configurationService: IConfigurationService): boolean { - const extensionId = getGalleryExtensionId(manifest.publisher, manifest.name); - const configuredUIExtensions = configurationService.getValue('_workbench.uiExtensions') || []; - if (configuredUIExtensions.length) { - if (configuredUIExtensions.indexOf(extensionId) !== -1) { - return true; - } - if (configuredUIExtensions.indexOf(`-${extensionId}`) !== -1) { - return false; - } - } - switch (manifest.extensionKind) { - case 'ui': return true; - case 'workspace': return false; - default: { - if (uiExtensions.has(extensionId)) { - return true; - } - if (manifest.main) { - return false; - } - if (manifest.contributes && isNonEmptyArray(manifest.contributes.debuggers)) { - return false; - } - // Default is UI Extension - return true; - } - } -} - /** * **!Do not construct directly!** * diff --git a/src/vs/platform/extensions/node/extensionsUtil.ts b/src/vs/platform/extensions/node/extensionsUtil.ts new file mode 100644 index 00000000000..5041bf9577e --- /dev/null +++ b/src/vs/platform/extensions/node/extensionsUtil.ts @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IExtensionManifest } from 'vs/platform/extensions/common/extensions'; +import { getGalleryExtensionId, areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { isNonEmptyArray } from 'vs/base/common/arrays'; +import product from 'vs/platform/node/product'; + +export function isUIExtension(manifest: IExtensionManifest, configurationService: IConfigurationService): boolean { + const extensionId = getGalleryExtensionId(manifest.publisher, manifest.name); + const configuredUIExtensions = configurationService.getValue('_workbench.uiExtensions') || []; + if (configuredUIExtensions.length) { + if (configuredUIExtensions.indexOf(extensionId) !== -1) { + return true; + } + if (configuredUIExtensions.indexOf(`-${extensionId}`) !== -1) { + return false; + } + } + switch (manifest.extensionKind) { + case 'ui': return true; + case 'workspace': return false; + default: { + if (isNonEmptyArray(product.uiExtensions) && product.uiExtensions.some(id => areSameExtensions({ id }, { id: extensionId }))) { + return true; + } + if (manifest.main) { + return false; + } + if (manifest.contributes && isNonEmptyArray(manifest.contributes.debuggers)) { + return false; + } + // Default is UI Extension + return true; + } + } +} diff --git a/src/vs/platform/node/product.ts b/src/vs/platform/node/product.ts index dbb84fd473f..08b8b607e2b 100644 --- a/src/vs/platform/node/product.ts +++ b/src/vs/platform/node/product.ts @@ -80,6 +80,7 @@ export interface IProductConfiguration { }; logUploaderUrl: string; portable?: string; + uiExtensions?: string[]; } export interface ISurveyData { diff --git a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts index 58ed50763e3..167b592c380 100644 --- a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts +++ b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts @@ -34,7 +34,8 @@ import * as resources from 'vs/base/common/resources'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IFileService } from 'vs/platform/files/common/files'; -import { IExtensionManifest, ExtensionType, ExtensionIdentifierWithVersion, IExtension as IPlatformExtension, isUIExtension } from 'vs/platform/extensions/common/extensions'; +import { IExtensionManifest, ExtensionType, ExtensionIdentifierWithVersion, IExtension as IPlatformExtension } from 'vs/platform/extensions/common/extensions'; +import { isUIExtension } from 'vs/platform/extensions/node/extensionsUtil'; interface IExtensionStateProvider { (extension: Extension): T; From 8b7f1ed1e7268f2974e333f599f55e3604286f5b Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 28 Jan 2019 12:05:22 +0100 Subject: [PATCH 202/274] update extension, #64376 --- build/builtInExtensions.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/builtInExtensions.json b/build/builtInExtensions.json index 368480b1118..1ce94db95eb 100644 --- a/build/builtInExtensions.json +++ b/build/builtInExtensions.json @@ -31,7 +31,7 @@ }, { "name": "ms-vscode.references-view", - "version": "0.0.23", + "version": "0.0.24", "repo": "https://github.com/Microsoft/vscode-reference-view", "metadata": { "id": "dc489f46-520d-4556-ae85-1f9eab3c412d", From d0ba2734fa9636c288a099e13e9a9802ed12da18 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 28 Jan 2019 12:06:00 +0100 Subject: [PATCH 203/274] make sure to invoke command with correct argument types, #64376 --- src/vs/workbench/api/node/extHostCommands.ts | 31 +++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/api/node/extHostCommands.ts b/src/vs/workbench/api/node/extHostCommands.ts index 8a6b94a362d..4e91ca97d00 100644 --- a/src/vs/workbench/api/node/extHostCommands.ts +++ b/src/vs/workbench/api/node/extHostCommands.ts @@ -15,6 +15,9 @@ import * as modes from 'vs/editor/common/modes'; import * as vscode from 'vscode'; import { ILogService } from 'vs/platform/log/common/log'; import { revive } from 'vs/base/common/marshalling'; +import { Range } from 'vs/editor/common/core/range'; +import { Position } from 'vs/editor/common/core/position'; +import { URI } from 'vs/base/common/uri'; interface CommandHandler { callback: Function; @@ -42,7 +45,33 @@ export class ExtHostCommands implements ExtHostCommandsShape { this._proxy = mainContext.getProxy(MainContext.MainThreadCommands); this._logService = logService; this._converter = new CommandsConverter(this, heapService); - this._argumentProcessors = [{ processArgument(a) { return revive(a, 0); } }]; + this._argumentProcessors = [ + { + processArgument(a) { + // URI, Regex + return revive(a, 0); + } + }, + { + processArgument(arg) { + return cloneAndChange(arg, function (obj) { + // Reverse of https://github.com/Microsoft/vscode/blob/1f28c5fc681f4c01226460b6d1c7e91b8acb4a5b/src/vs/workbench/api/node/extHostCommands.ts#L112-L127 + if (Range.isIRange(obj)) { + return extHostTypeConverter.Range.to(obj); + } + if (Position.isIPosition(obj)) { + return extHostTypeConverter.Position.to(obj); + } + if (Range.isIRange((obj as modes.Location).range) && URI.isUri((obj as modes.Location).uri)) { + return extHostTypeConverter.location.to(obj); + } + if (!Array.isArray(obj)) { + return obj; + } + }); + } + } + ]; } get converter(): CommandsConverter { From 80a1a06e715b402ca8cd1641108611e150a49984 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Mon, 28 Jan 2019 12:11:54 +0100 Subject: [PATCH 204/274] Add a task lifecycle event for when a depends on task is started (#67221) Fixes #66561 --- .../parts/debug/electron-browser/debugService.ts | 2 +- src/vs/workbench/parts/tasks/common/tasks.ts | 3 ++- .../parts/tasks/electron-browser/terminalTaskSystem.ts | 9 +++++---- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/parts/debug/electron-browser/debugService.ts b/src/vs/workbench/parts/debug/electron-browser/debugService.ts index 8e930b91103..1ec06113de4 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugService.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugService.ts @@ -716,7 +716,7 @@ export class DebugService implements IDebugService { // task is already running - nothing to do. return Promise.resolve(null); } - once(e => e.kind === TaskEventKind.Active && e.taskId === task._id, this.taskService.onDidStateChange)(() => { + once(e => ((e.kind === TaskEventKind.Active) || (e.kind === TaskEventKind.DependsOnStarted)) && e.taskId === task._id, this.taskService.onDidStateChange)(() => { // Task is active, so everything seems to be fine, no need to prompt after 10 seconds // Use case being a slow running task should not be prompted even though it takes more than 10 seconds taskStarted = true; diff --git a/src/vs/workbench/parts/tasks/common/tasks.ts b/src/vs/workbench/parts/tasks/common/tasks.ts index 98128bbbfea..3b9248543fc 100644 --- a/src/vs/workbench/parts/tasks/common/tasks.ts +++ b/src/vs/workbench/parts/tasks/common/tasks.ts @@ -836,6 +836,7 @@ export class TaskSorter { } export const enum TaskEventKind { + DependsOnStarted = 'dependsOnStarted', Start = 'start', ProcessStarted = 'processStarted', Active = 'active', @@ -871,7 +872,7 @@ export const enum TaskRunSource { export namespace TaskEvent { export function create(kind: TaskEventKind.ProcessStarted | TaskEventKind.ProcessEnded, task: Task, processIdOrExitCode: number): TaskEvent; - export function create(kind: TaskEventKind.Start | TaskEventKind.Active | TaskEventKind.Inactive | TaskEventKind.Terminated | TaskEventKind.End, task: Task): TaskEvent; + export function create(kind: TaskEventKind.DependsOnStarted | TaskEventKind.Start | TaskEventKind.Active | TaskEventKind.Inactive | TaskEventKind.Terminated | TaskEventKind.End, task: Task): TaskEvent; export function create(kind: TaskEventKind.Changed): TaskEvent; export function create(kind: TaskEventKind, task?: Task, processIdOrExitCode?: number): TaskEvent { if (task) { diff --git a/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts b/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts index 7dac1287f27..305d2c05c46 100644 --- a/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts +++ b/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts @@ -314,12 +314,13 @@ export class TerminalTaskSystem implements ITaskSystem { let promises: Promise[] = []; if (task.configurationProperties.dependsOn) { task.configurationProperties.dependsOn.forEach((dependency) => { - let task = resolver.resolve(dependency.workspaceFolder, dependency.task!); - if (task) { - let key = task.getMapKey(); + let dependencyTask = resolver.resolve(dependency.workspaceFolder, dependency.task!); + if (dependencyTask) { + let key = dependencyTask.getMapKey(); let promise = this.activeTasks[key] ? this.activeTasks[key].promise : undefined; if (!promise) { - promise = this.executeTask(task, resolver, trigger); + this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.DependsOnStarted, task)); + promise = this.executeTask(dependencyTask, resolver, trigger); } promises.push(promise); } else { From a584b902638e4573a909646d59662817d432c165 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Mon, 28 Jan 2019 12:21:00 +0100 Subject: [PATCH 205/274] Improve description of task presentation silent. Fixes #65701 --- src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.ts b/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.ts index 17b87290553..ad8290e168e 100644 --- a/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.ts +++ b/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.ts @@ -105,7 +105,7 @@ const presentation: IJSONSchema = { enum: ['always', 'silent', 'never'], enumDescriptions: [ nls.localize('JsonSchema.tasks.presentation.reveal.always', 'Always reveals the terminal when this task is executed.'), - nls.localize('JsonSchema.tasks.presentation.reveal.silent', 'Only reveals the terminal if no problem matcher is associated with the task and an errors occurs executing it.'), + nls.localize('JsonSchema.tasks.presentation.reveal.silent', 'Only reveals the terminal if the task exits with an error.'), nls.localize('JsonSchema.tasks.presentation.reveal.never', 'Never reveals the terminal when this task is executed.'), ], default: 'always', From 0443e4c95d1a1d473ee5c8cf512de5408af7e034 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Mon, 28 Jan 2019 12:28:38 +0100 Subject: [PATCH 206/274] Refactor fix for #65118 --- .../services/extensions/node/proxyResolver.ts | 37 ++++++++++--------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/src/vs/workbench/services/extensions/node/proxyResolver.ts b/src/vs/workbench/services/extensions/node/proxyResolver.ts index c954a089f1d..6e3ebb2bc65 100644 --- a/src/vs/workbench/services/extensions/node/proxyResolver.ts +++ b/src/vs/workbench/services/extensions/node/proxyResolver.ts @@ -24,14 +24,14 @@ export function connectProxyResolver( extHostLogService: ExtHostLogService, mainThreadTelemetry: MainThreadTelemetryShape ) { - const agent = createProxyAgent(extHostWorkspace, configProvider, extHostLogService, mainThreadTelemetry); - const lookup = createPatchedModules(configProvider, agent); + const agents = createProxyAgents(extHostWorkspace, configProvider, extHostLogService, mainThreadTelemetry); + const lookup = createPatchedModules(configProvider, agents); return configureModuleLoading(extensionService, lookup); } const maxCacheEntries = 5000; // Cache can grow twice that much due to 'oldCache'. -function createProxyAgent( +function createProxyAgents( extHostWorkspace: ExtHostWorkspace, configProvider: ExtHostConfigProvider, extHostLogService: ExtHostLogService, @@ -156,7 +156,11 @@ function createProxyAgent( }); } - return new ProxyAgent({ resolveProxy }); + const httpAgent: http.Agent = new ProxyAgent({ resolveProxy }); + (httpAgent).defaultPort = 80; + const httpsAgent: http.Agent = new ProxyAgent({ resolveProxy }); + (httpsAgent).defaultPort = 443; + return { http: httpAgent, https: httpsAgent }; } function proxyFromConfigURL(configURL: string) { @@ -177,7 +181,7 @@ function proxyFromConfigURL(configURL: string) { return undefined; } -function createPatchedModules(configProvider: ExtHostConfigProvider, agent: http.Agent) { +function createPatchedModules(configProvider: ExtHostConfigProvider, agents: { http: http.Agent; https: http.Agent; }) { const setting = { config: configProvider.getConfiguration('http') .get('proxySupport') || 'off' @@ -189,25 +193,23 @@ function createPatchedModules(configProvider: ExtHostConfigProvider, agent: http return { http: { - off: assign({}, http, patches(http, agent, { config: 'off' }, true)), - on: assign({}, http, patches(http, agent, { config: 'on' }, true)), - override: assign({}, http, patches(http, agent, { config: 'override' }, true)), - onRequest: assign({}, http, patches(http, agent, setting, true)), - default: assign(http, patches(http, agent, setting, false)) // run last + off: assign({}, http, patches(http, agents.http, { config: 'off' }, true)), + on: assign({}, http, patches(http, agents.http, { config: 'on' }, true)), + override: assign({}, http, patches(http, agents.http, { config: 'override' }, true)), + onRequest: assign({}, http, patches(http, agents.http, setting, true)), + default: assign(http, patches(http, agents.http, setting, false)) // run last }, https: { - off: assign({}, https, patches(https, agent, { config: 'off' }, true)), - on: assign({}, https, patches(https, agent, { config: 'on' }, true)), - override: assign({}, https, patches(https, agent, { config: 'override' }, true)), - onRequest: assign({}, https, patches(https, agent, setting, true)), - default: assign(https, patches(https, agent, setting, false)) // run last + off: assign({}, https, patches(https, agents.https, { config: 'off' }, true)), + on: assign({}, https, patches(https, agents.https, { config: 'on' }, true)), + override: assign({}, https, patches(https, agents.https, { config: 'override' }, true)), + onRequest: assign({}, https, patches(https, agents.https, setting, true)), + default: assign(https, patches(https, agents.https, setting, false)) // run last } }; } function patches(originals: typeof http | typeof https, agent: http.Agent, setting: { config: string; }, onRequest: boolean) { - const defaultPort = originals === https ? 443 : 80; - return { get: patch(originals.get), request: patch(originals.request) @@ -248,7 +250,6 @@ function patches(originals: typeof http | typeof https, agent: http.Agent, setti options = { ...options }; } options.agent = agent; - options.defaultPort = defaultPort; // Lets Node's http module omit the port, if it is the default port, in the Host header. (https://github.com/Microsoft/vscode/issues/65118) return original(options, callback); } From 7d90c4836e4594e98bd6da1d33cfb47ae2d7a71e Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Mon, 28 Jan 2019 12:50:47 +0100 Subject: [PATCH 207/274] Refactor fix for #65118 --- .../services/extensions/node/proxyResolver.ts | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/services/extensions/node/proxyResolver.ts b/src/vs/workbench/services/extensions/node/proxyResolver.ts index 6e3ebb2bc65..4a8df829c28 100644 --- a/src/vs/workbench/services/extensions/node/proxyResolver.ts +++ b/src/vs/workbench/services/extensions/node/proxyResolver.ts @@ -193,23 +193,23 @@ function createPatchedModules(configProvider: ExtHostConfigProvider, agents: { h return { http: { - off: assign({}, http, patches(http, agents.http, { config: 'off' }, true)), - on: assign({}, http, patches(http, agents.http, { config: 'on' }, true)), - override: assign({}, http, patches(http, agents.http, { config: 'override' }, true)), - onRequest: assign({}, http, patches(http, agents.http, setting, true)), - default: assign(http, patches(http, agents.http, setting, false)) // run last + off: assign({}, http, patches(http, agents.http, agents.https, { config: 'off' }, true)), + on: assign({}, http, patches(http, agents.http, agents.https, { config: 'on' }, true)), + override: assign({}, http, patches(http, agents.http, agents.https, { config: 'override' }, true)), + onRequest: assign({}, http, patches(http, agents.http, agents.https, setting, true)), + default: assign(http, patches(http, agents.http, agents.https, setting, false)) // run last }, https: { - off: assign({}, https, patches(https, agents.https, { config: 'off' }, true)), - on: assign({}, https, patches(https, agents.https, { config: 'on' }, true)), - override: assign({}, https, patches(https, agents.https, { config: 'override' }, true)), - onRequest: assign({}, https, patches(https, agents.https, setting, true)), - default: assign(https, patches(https, agents.https, setting, false)) // run last + off: assign({}, https, patches(https, agents.https, agents.http, { config: 'off' }, true)), + on: assign({}, https, patches(https, agents.https, agents.http, { config: 'on' }, true)), + override: assign({}, https, patches(https, agents.https, agents.http, { config: 'override' }, true)), + onRequest: assign({}, https, patches(https, agents.https, agents.http, setting, true)), + default: assign(https, patches(https, agents.https, agents.http, setting, false)) // run last } }; } -function patches(originals: typeof http | typeof https, agent: http.Agent, setting: { config: string; }, onRequest: boolean) { +function patches(originals: typeof http | typeof https, agent: http.Agent, otherAgent: http.Agent, setting: { config: string; }, onRequest: boolean) { return { get: patch(originals.get), request: patch(originals.request) @@ -233,7 +233,7 @@ function patches(originals: typeof http | typeof https, agent: http.Agent, setti return original.apply(null, arguments as unknown as any[]); } - if (!options.socketPath && (config === 'override' || config === 'on' && !options.agent) && options.agent !== agent) { + if (!options.socketPath && (config === 'override' || config === 'on' && !options.agent) && options.agent !== agent && options.agent !== otherAgent) { if (url) { const parsed = typeof url === 'string' ? new nodeurl.URL(url) : url; const urlOptions = { From f82458104246c6abde789979c973eed659df268f Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 28 Jan 2019 14:03:01 +0100 Subject: [PATCH 208/274] fix #67120 --- src/vs/platform/files/common/files.ts | 6 +++++- src/vs/vscode.proposed.d.ts | 2 +- .../api/electron-browser/mainThreadFileSystem.ts | 6 +++--- src/vs/workbench/api/node/extHost.protocol.ts | 4 ++-- src/vs/workbench/api/node/extHostFileSystem.ts | 4 ++-- .../services/files/electron-browser/streams.ts | 10 +++++++--- 6 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/vs/platform/files/common/files.ts b/src/vs/platform/files/common/files.ts index b007b6f1840..fce0e320b86 100644 --- a/src/vs/platform/files/common/files.ts +++ b/src/vs/platform/files/common/files.ts @@ -168,6 +168,10 @@ export interface FileWriteOptions { create: boolean; } +export interface FileOpenOptions { + create: boolean; +} + export interface FileDeleteOptions { recursive: boolean; } @@ -219,7 +223,7 @@ export interface IFileSystemProvider { readFile?(resource: URI): Promise; writeFile?(resource: URI, content: Uint8Array, opts: FileWriteOptions): Promise; - open?(resource: URI): Promise; + open?(resource: URI, opts: FileOpenOptions): Promise; close?(fd: number): Promise; read?(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise; write?(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise; diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index b95a09fadff..c1b069e85a8 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -67,7 +67,7 @@ declare module 'vscode' { //#region Joh - read/write in chunks export interface FileSystemProvider { - open?(resource: Uri): number | Thenable; + open?(resource: Uri, options: { create: boolean }): number | Thenable; close?(fd: number): void | Thenable; read?(fd: number, pos: number, data: Uint8Array, offset: number, length: number): number | Thenable; write?(fd: number, pos: number, data: Uint8Array, offset: number, length: number): number | Thenable; diff --git a/src/vs/workbench/api/electron-browser/mainThreadFileSystem.ts b/src/vs/workbench/api/electron-browser/mainThreadFileSystem.ts index 0a948e1cfbb..caea6c69d7f 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadFileSystem.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadFileSystem.ts @@ -6,7 +6,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; -import { FileWriteOptions, FileSystemProviderCapabilities, IFileChange, IFileService, IFileSystemProvider, IStat, IWatchOptions, FileType, FileOverwriteOptions, FileDeleteOptions } from 'vs/platform/files/common/files'; +import { FileWriteOptions, FileSystemProviderCapabilities, IFileChange, IFileService, IFileSystemProvider, IStat, IWatchOptions, FileType, FileOverwriteOptions, FileDeleteOptions, FileOpenOptions } from 'vs/platform/files/common/files'; import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; import { ExtHostContext, ExtHostFileSystemShape, IExtHostContext, IFileChangeDto, MainContext, MainThreadFileSystemShape } from '../node/extHost.protocol'; import { ResourceLabelFormatter, ILabelService } from 'vs/platform/label/common/label'; @@ -130,8 +130,8 @@ class RemoteFileSystemProvider implements IFileSystemProvider { return this._proxy.$copy(this._handle, resource, target, opts); } - open(resource: URI): Promise { - return this._proxy.$open(this._handle, resource); + open(resource: URI, opts: FileOpenOptions): Promise { + return this._proxy.$open(this._handle, resource, opts); } close(fd: number): Promise { diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 72e15943a45..c9bb77f1063 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -20,7 +20,7 @@ import { CharacterPair, CommentRule, EnterAction } from 'vs/editor/common/modes/ import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands'; import { ConfigurationTarget, IConfigurationData, IConfigurationModel } from 'vs/platform/configuration/common/configuration'; import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; -import { FileChangeType, FileDeleteOptions, FileOverwriteOptions, FileSystemProviderCapabilities, FileType, FileWriteOptions, IStat, IWatchOptions } from 'vs/platform/files/common/files'; +import { FileChangeType, FileDeleteOptions, FileOverwriteOptions, FileSystemProviderCapabilities, FileType, FileWriteOptions, IStat, IWatchOptions, FileOpenOptions } from 'vs/platform/files/common/files'; import { ResourceLabelFormatter } from 'vs/platform/label/common/label'; import { LogLevel } from 'vs/platform/log/common/log'; import { IMarkerData } from 'vs/platform/markers/common/markers'; @@ -731,7 +731,7 @@ export interface ExtHostFileSystemShape { $delete(handle: number, resource: UriComponents, opts: FileDeleteOptions): Promise; $watch(handle: number, session: number, resource: UriComponents, opts: IWatchOptions): void; $unwatch(handle: number, session: number): void; - $open(handle: number, resource: UriComponents): Promise; + $open(handle: number, resource: UriComponents, opts: FileOpenOptions): Promise; $close(handle: number, fd: number): Promise; $read(handle: number, fd: number, pos: number, length: number): Promise; $write(handle: number, fd: number, pos: number, data: Buffer): Promise; diff --git a/src/vs/workbench/api/node/extHostFileSystem.ts b/src/vs/workbench/api/node/extHostFileSystem.ts index f1fe101d1c1..a1e8edc9204 100644 --- a/src/vs/workbench/api/node/extHostFileSystem.ts +++ b/src/vs/workbench/api/node/extHostFileSystem.ts @@ -280,9 +280,9 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape { } } - $open(handle: number, resource: UriComponents): Promise { + $open(handle: number, resource: UriComponents, opts: files.FileOpenOptions): Promise { this._checkProviderExists(handle); - return Promise.resolve(this._fsProvider.get(handle).open(URI.revive(resource))); + return Promise.resolve(this._fsProvider.get(handle).open(URI.revive(resource), opts)); } $close(handle: number, fd: number): Promise { diff --git a/src/vs/workbench/services/files/electron-browser/streams.ts b/src/vs/workbench/services/files/electron-browser/streams.ts index 60117fe986d..d4af4580a88 100644 --- a/src/vs/workbench/services/files/electron-browser/streams.ts +++ b/src/vs/workbench/services/files/electron-browser/streams.ts @@ -50,7 +50,7 @@ function createWritable(provider: IFileSystemProvider, resource: URI, opts: File async _write(chunk: Buffer, encoding, callback: Function) { try { if (typeof this._fd !== 'number') { - this._fd = await provider.open(resource); + this._fd = await provider.open(resource, { create: true }); } let bytesWritten = await provider.write(this._fd, this._pos, chunk, 0, chunk.length); this._pos += bytesWritten; @@ -60,7 +60,11 @@ function createWritable(provider: IFileSystemProvider, resource: URI, opts: File } } _final(callback: (err?: any) => any) { - provider.close(this._fd).then(callback, callback); + if (typeof this._fd !== 'number') { + provider.open(resource, { create: true }).then(fd => provider.close(fd)).finally(callback); + } else { + provider.close(this._fd).finally(callback); + } } }; } @@ -88,7 +92,7 @@ function createReadable(provider: IFileSystemProvider, resource: URI, position: this._reading = true; try { if (typeof this._fd !== 'number') { - this._fd = await provider.open(resource); + this._fd = await provider.open(resource, { create: false }); } while (this._reading) { let buffer = Buffer.allocUnsafe(size); From 8846757657096a3d58817c313a2dcaebbe0b6ae6 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 28 Jan 2019 05:38:25 -0800 Subject: [PATCH 209/274] Move cwd ownership into terminal process --- .../mainThreadTerminalService.ts | 16 +++++++++ src/vs/workbench/api/node/extHost.protocol.ts | 4 +++ .../api/node/extHostTerminalService.ts | 28 +++++++++++++--- .../parts/terminal/common/terminal.ts | 14 ++++---- .../electron-browser/terminalActions.ts | 18 +++++----- .../electron-browser/terminalInstance.ts | 30 ++++++----------- .../terminalProcessManager.ts | 9 +++++ .../workbench/parts/terminal/node/terminal.ts | 5 ++- .../parts/terminal/node/terminalProcess.ts | 23 +++++++++++++ .../node/terminalProcessExtHostProxy.ts | 33 +++++++++++++++++++ 10 files changed, 138 insertions(+), 42 deletions(-) diff --git a/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts b/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts index 84ed8b00b64..3edd37683f0 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts @@ -214,6 +214,14 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape request.proxy.onInput(data => this._proxy.$acceptProcessInput(request.proxy.terminalId, data)); request.proxy.onResize(dimensions => this._proxy.$acceptProcessResize(request.proxy.terminalId, dimensions.cols, dimensions.rows)); request.proxy.onShutdown(immediate => this._proxy.$acceptProcessShutdown(request.proxy.terminalId, immediate)); + request.proxy.onRequestCwd(() => { + console.log('onRequestCwd', request.proxy.terminalId); + this._proxy.$acceptProcessRequestCwd(request.proxy.terminalId); + }); + request.proxy.onRequestInitialCwd(() => { + console.log('onRequestInitialCwd', request.proxy.terminalId); + this._proxy.$acceptProcessRequestInitialCwd(request.proxy.terminalId); + }); } public $sendProcessTitle(terminalId: number, title: string): void { @@ -232,4 +240,12 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape this._terminalProcesses[terminalId].emitExit(exitCode); delete this._terminalProcesses[terminalId]; } + + public $sendProcessInitialCwd(terminalId: number, initialCwd: string): void { + this._terminalProcesses[terminalId].emitInitialCwd(initialCwd); + } + + public $sendProcessCwd(terminalId: number, cwd: string): void { + this._terminalProcesses[terminalId].emitCwd(cwd); + } } diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index c9bb77f1063..85e68f5080d 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -363,6 +363,8 @@ export interface MainThreadTerminalServiceShape extends IDisposable { $sendProcessData(terminalId: number, data: string): void; $sendProcessPid(terminalId: number, pid: number): void; $sendProcessExit(terminalId: number, exitCode: number): void; + $sendProcessInitialCwd(terminalId: number, cwd: string): void; + $sendProcessCwd(terminalId: number, initialCwd: string): void; // Renderer $terminalRendererSetName(terminalId: number, name: string): void; @@ -938,6 +940,8 @@ export interface ExtHostTerminalServiceShape { $acceptProcessInput(id: number, data: string): void; $acceptProcessResize(id: number, cols: number, rows: number): void; $acceptProcessShutdown(id: number, immediate: boolean): void; + $acceptProcessRequestInitialCwd(id: number): void; + $acceptProcessRequestCwd(id: number): void; } export interface ExtHostSCMShape { diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts index 99ba5c8878d..c99a43a3561 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -430,11 +430,12 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { // Fork the process and listen for messages this._logService.debug(`Terminal process launching on ext host`, shellLaunchConfig, initialCwd, cols, rows, env); - this._terminalProcesses[id] = new TerminalProcess(shellLaunchConfig, initialCwd, cols, rows, env, terminalConfig.get('windowsEnableConpty')); - this._terminalProcesses[id].onProcessIdReady(pid => this._proxy.$sendProcessPid(id, pid)); - this._terminalProcesses[id].onProcessTitleChanged(title => this._proxy.$sendProcessTitle(id, title)); - this._terminalProcesses[id].onProcessData(data => this._proxy.$sendProcessData(id, data)); - this._terminalProcesses[id].onProcessExit((exitCode) => this._onProcessExit(id, exitCode)); + const p = new TerminalProcess(shellLaunchConfig, initialCwd, cols, rows, env, terminalConfig.get('windowsEnableConpty')); + p.onProcessIdReady(pid => this._proxy.$sendProcessPid(id, pid)); + p.onProcessTitleChanged(title => this._proxy.$sendProcessTitle(id, title)); + p.onProcessData(data => this._proxy.$sendProcessData(id, data)); + p.onProcessExit((exitCode) => this._onProcessExit(id, exitCode)); + this._terminalProcesses[id] = p; } @@ -457,6 +458,23 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { this._terminalProcesses[id].shutdown(immediate); } + // TODO: Also support initial cwd as it's evaluated on the ext host + public $acceptProcessRequestInitialCwd(id: number): void { + console.log('$acceptProcessRequestInitialCwd', id); + this._terminalProcesses[id].getInitialCwd().then(initialCwd => { + console.log('initialCwd', initialCwd); + this._proxy.$sendProcessInitialCwd(id, initialCwd); + }); + } + + public $acceptProcessRequestCwd(id: number): void { + console.log('$acceptProcessRequestCwd', id); + this._terminalProcesses[id].getCwd().then(cwd => { + console.log('cwd', cwd); + this._proxy.$sendProcessCwd(id, cwd); + }); + } + private _onProcessExit(id: number, exitCode: number): void { // Remove listeners this._terminalProcesses[id].dispose(); diff --git a/src/vs/workbench/parts/terminal/common/terminal.ts b/src/vs/workbench/parts/terminal/common/terminal.ts index 155b7c68578..8f61e7761ce 100644 --- a/src/vs/workbench/parts/terminal/common/terminal.ts +++ b/src/vs/workbench/parts/terminal/common/terminal.ts @@ -417,11 +417,6 @@ export interface ITerminalInstance { */ readonly commandTracker: ITerminalCommandTracker; - /** - * The cwd that the terminal instance was initialized with. - */ - readonly initialCwd: string; - /** * Dispose the terminal instance, removing it from the panel/service and freeing up resources. * @@ -614,6 +609,7 @@ export interface ITerminalInstance { toggleEscapeSequenceLogging(): void; + getInitialCwd(): Promise; getCwd(): Promise; } @@ -630,7 +626,6 @@ export interface ITerminalProcessManager extends IDisposable { readonly processState: ProcessState; readonly ptyProcessReady: Promise; readonly shellProcessId: number; - readonly initialCwd: string; readonly onProcessReady: Event; readonly onProcessData: Event; @@ -642,6 +637,9 @@ export interface ITerminalProcessManager extends IDisposable { createProcess(shellLaunchConfig: IShellLaunchConfig, cols: number, rows: number); write(data: string): void; setDimensions(cols: number, rows: number): void; + + getInitialCwd(): Promise; + getCwd(): Promise; } export const enum ProcessState { @@ -671,10 +669,14 @@ export interface ITerminalProcessExtHostProxy extends IDisposable { emitTitle(title: string): void; emitPid(pid: number): void; emitExit(exitCode: number): void; + emitInitialCwd(initialCwd: string): void; + emitCwd(cwd: string): void; onInput: Event; onResize: Event<{ cols: number, rows: number }>; onShutdown: Event; + onRequestInitialCwd: Event; + onRequestCwd: Event; } export interface ITerminalProcessExtHostRequest { diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts index c77fbe34407..31564bafef7 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts @@ -35,7 +35,7 @@ export const TERMINAL_PICKER_PREFIX = 'term '; function getCwdForSplit(configHelper: ITerminalConfigHelper, instance: ITerminalInstance, folders?: IWorkspaceFolder[], commandService?: ICommandService): Promise { switch (configHelper.config.splitCwd) { - case 'workspaceRoot': { + case 'workspaceRoot': // allow original behavior let pathPromise: Promise = Promise.resolve(''); if (folders.length > 1) { @@ -53,15 +53,12 @@ function getCwdForSplit(configHelper: ITerminalConfigHelper, instance: ITerminal } return pathPromise; - } - case 'initial': { - return new Promise(resolve => { - resolve(instance.initialCwd); - }); - } - case 'inherited': { + case 'initial': + console.log('getCwdForSplit initial'); + return instance.getInitialCwd(); + case 'inherited': + console.log('getCwdForSplit inherited'); return instance.getCwd(); - } } } @@ -385,8 +382,9 @@ export class SplitTerminalAction extends Action { if (!instance) { return Promise.resolve(undefined); } - + console.log('SplitTerminalAction'); return getCwdForSplit(this._terminalService.configHelper, instance, this.workspaceContextService.getWorkspace().folders, this.commandService).then(cwd => { + console.log('SplitTerminalAction cwd', cwd); if (cwd || (cwd === '')) { this._terminalService.splitInstance(instance, { cwd }); return this._terminalService.showPanel(true); diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts index 4adb53f2ac0..c0c72063d10 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts @@ -36,7 +36,7 @@ import { INotificationService, Severity, IPromptChoice } from 'vs/platform/notif import { ILogService } from 'vs/platform/log/common/log'; import { TerminalCommandTracker } from 'vs/workbench/parts/terminal/node/terminalCommandTracker'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { execFile, exec } from 'child_process'; +import { execFile } from 'child_process'; import { TERMINAL_COMMAND_ID } from 'vs/workbench/parts/terminal/common/terminalCommands'; import { TerminalProcessManager } from 'vs/workbench/parts/terminal/electron-browser/terminalProcessManager'; @@ -434,8 +434,8 @@ export class TerminalInstance implements ITerminalInstance { this._xterm.on('data', data => this._processManager!.write(data)); // TODO: How does the cwd work on detached processes? this._linkHandler = this._instantiationService.createInstance(TerminalLinkHandler, this._xterm, platform.platform); - this.processReady.then(() => { - this._linkHandler.processCwd = this._processManager!.initialCwd; + this.processReady.then(async () => { + this._linkHandler.processCwd = await this._processManager!.getInitialCwd(); }); } this._xterm.on('focus', () => this._onFocus.fire(this)); @@ -1314,28 +1314,18 @@ export class TerminalInstance implements ITerminalInstance { this._xterm.setOption('debug', this._xterm._core.debug); } - public get initialCwd(): string { - if (this._processManager) { - return this._processManager.initialCwd; + public getInitialCwd(): Promise { + if (!this._processManager) { + return Promise.resolve(''); } - return ''; + return this._processManager.getInitialCwd(); } public getCwd(): Promise { - if (!platform.isWindows) { - let pid = this.processId; - return new Promise(resolve => { - exec('lsof -p ' + pid + ' | grep cwd', (error, stdout, stderr) => { - if (stdout !== '') { - resolve(stdout.substring(stdout.indexOf('/'), stdout.length - 1)); - } - }); - }); - } else { - return new Promise(resolve => { - resolve(this.initialCwd); - }); + if (!this._processManager) { + return Promise.resolve(''); } + return this._processManager.getCwd(); } } diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts index f838343e3ff..ed59e4a655a 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts @@ -35,6 +35,7 @@ export class TerminalProcessManager implements ITerminalProcessManager { public processState: ProcessState = ProcessState.UNINITIALIZED; public ptyProcessReady: Promise; public shellProcessId: number; + // TODO: This should be removed in favor of async getInitialCwd public initialCwd: string; private _process: ITerminalChildProcess | null = null; @@ -200,6 +201,14 @@ export class TerminalProcessManager implements ITerminalProcessManager { } } + public getInitialCwd(): Promise { + return this._process.getInitialCwd(); + } + + public getCwd(): Promise { + return this._process.getCwd(); + } + private _onExit(exitCode: number): void { this._process = null; diff --git a/src/vs/workbench/parts/terminal/node/terminal.ts b/src/vs/workbench/parts/terminal/node/terminal.ts index 83768c9025f..73b41c24e5d 100644 --- a/src/vs/workbench/parts/terminal/node/terminal.ts +++ b/src/vs/workbench/parts/terminal/node/terminal.ts @@ -10,7 +10,7 @@ import { readFile, fileExists } from 'vs/base/node/pfs'; import { Event } from 'vs/base/common/event'; /** - * An interface representing a raw terminal child process, this is a subset of the + * An interface representing a raw terminal child process, this contains a subset of the * child_process.ChildProcess node.js interface. */ export interface ITerminalChildProcess { @@ -28,6 +28,9 @@ export interface ITerminalChildProcess { shutdown(immediate: boolean): void; input(data: string): void; resize(cols: number, rows: number): void; + + getInitialCwd(): Promise; + getCwd(): Promise; } export function getDefaultShell(p: platform.Platform): string { diff --git a/src/vs/workbench/parts/terminal/node/terminalProcess.ts b/src/vs/workbench/parts/terminal/node/terminalProcess.ts index ac913edc443..16dfd0580b4 100644 --- a/src/vs/workbench/parts/terminal/node/terminalProcess.ts +++ b/src/vs/workbench/parts/terminal/node/terminalProcess.ts @@ -11,6 +11,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { ITerminalChildProcess } from 'vs/workbench/parts/terminal/node/terminal'; import { IDisposable } from 'vs/base/common/lifecycle'; import { IShellLaunchConfig } from 'vs/workbench/parts/terminal/common/terminal'; +import { exec } from 'child_process'; export class TerminalProcess implements ITerminalChildProcess, IDisposable { private _exitCode: number; @@ -20,6 +21,7 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable { private _processStartupComplete: Promise; private _isDisposed: boolean = false; private _titleInterval: NodeJS.Timer | null = null; + private _initialCwd: string; private readonly _onProcessData = new Emitter(); public get onProcessData(): Event { return this._onProcessData.event; } @@ -47,6 +49,7 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable { shellName = 'xterm-256color'; } + this._initialCwd = cwd; const useConpty = windowsEnableConpty && process.platform === 'win32' && this._getWindowsBuildNumber() >= 18309; const options: pty.IPtyForkOptions = { name: shellName, @@ -187,4 +190,24 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable { // exception in winpty. this._ptyProcess.resize(Math.max(cols, 1), Math.max(rows, 1)); } + + public getInitialCwd(): Promise { + return Promise.resolve(this._initialCwd); + } + + public getCwd(): Promise { + if (platform.isWindows) { + return new Promise(resolve => { + resolve(this._initialCwd); + }); + } + + return new Promise(resolve => { + exec('lsof -p ' + this._ptyProcess.pid + ' | grep cwd', (error, stdout, stderr) => { + if (stdout !== '') { + resolve(stdout.substring(stdout.indexOf('/'), stdout.length - 1)); + } + }); + }); + } } diff --git a/src/vs/workbench/parts/terminal/node/terminalProcessExtHostProxy.ts b/src/vs/workbench/parts/terminal/node/terminalProcessExtHostProxy.ts index ea06eac26f5..653e09b65dd 100644 --- a/src/vs/workbench/parts/terminal/node/terminalProcessExtHostProxy.ts +++ b/src/vs/workbench/parts/terminal/node/terminalProcessExtHostProxy.ts @@ -28,6 +28,13 @@ export class TerminalProcessExtHostProxy implements ITerminalChildProcess, ITerm public get onResize(): Event<{ cols: number, rows: number }> { return this._onResize.event; } private readonly _onShutdown = new Emitter(); public get onShutdown(): Event { return this._onShutdown.event; } + private readonly _onRequestInitialCwd = new Emitter(); + public get onRequestInitialCwd(): Event { return this._onRequestInitialCwd.event; } + private readonly _onRequestCwd = new Emitter(); + public get onRequestCwd(): Event { return this._onRequestCwd.event; } + + private _pendingInitialCwdRequests: ((value?: string | Thenable) => void)[] = []; + private _pendingCwdRequests: ((value?: string | Thenable) => void)[] = []; constructor( public terminalId: number, @@ -68,6 +75,18 @@ export class TerminalProcessExtHostProxy implements ITerminalChildProcess, ITerm this.dispose(); } + public emitInitialCwd(initialCwd: string): void { + while (this._pendingInitialCwdRequests.length > 0) { + this._pendingInitialCwdRequests.pop()(initialCwd); + } + } + + public emitCwd(cwd: string): void { + while (this._pendingCwdRequests.length > 0) { + this._pendingCwdRequests.pop()(cwd); + } + } + public shutdown(immediate: boolean): void { this._onShutdown.fire(immediate); } @@ -79,4 +98,18 @@ export class TerminalProcessExtHostProxy implements ITerminalChildProcess, ITerm public resize(cols: number, rows: number): void { this._onResize.fire({ cols, rows }); } + + public getInitialCwd(): Promise { + return new Promise(resolve => { + this._onRequestInitialCwd.fire(); + this._pendingInitialCwdRequests.push(resolve); + }); + } + + public getCwd(): Promise { + return new Promise(resolve => { + this._onRequestCwd.fire(); + this._pendingCwdRequests.push(resolve); + }); + } } \ No newline at end of file From e384ca7461d40f3bead9474b7cd077b8466447f5 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 28 Jan 2019 06:01:56 -0800 Subject: [PATCH 210/274] Clean up terminal cwd handling --- .../mainThreadTerminalService.ts | 10 ++-------- .../api/node/extHostTerminalService.ts | 13 ++----------- .../electron-browser/terminalActions.ts | 19 +++++++++---------- .../terminalProcessManager.ts | 17 ++++++++--------- 4 files changed, 21 insertions(+), 38 deletions(-) diff --git a/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts b/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts index 3edd37683f0..bb267c2dbb4 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts @@ -214,14 +214,8 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape request.proxy.onInput(data => this._proxy.$acceptProcessInput(request.proxy.terminalId, data)); request.proxy.onResize(dimensions => this._proxy.$acceptProcessResize(request.proxy.terminalId, dimensions.cols, dimensions.rows)); request.proxy.onShutdown(immediate => this._proxy.$acceptProcessShutdown(request.proxy.terminalId, immediate)); - request.proxy.onRequestCwd(() => { - console.log('onRequestCwd', request.proxy.terminalId); - this._proxy.$acceptProcessRequestCwd(request.proxy.terminalId); - }); - request.proxy.onRequestInitialCwd(() => { - console.log('onRequestInitialCwd', request.proxy.terminalId); - this._proxy.$acceptProcessRequestInitialCwd(request.proxy.terminalId); - }); + request.proxy.onRequestCwd(() => this._proxy.$acceptProcessRequestCwd(request.proxy.terminalId)); + request.proxy.onRequestInitialCwd(() => this._proxy.$acceptProcessRequestInitialCwd(request.proxy.terminalId)); } public $sendProcessTitle(terminalId: number, title: string): void { diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts index c99a43a3561..fd343a8de75 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -458,21 +458,12 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { this._terminalProcesses[id].shutdown(immediate); } - // TODO: Also support initial cwd as it's evaluated on the ext host public $acceptProcessRequestInitialCwd(id: number): void { - console.log('$acceptProcessRequestInitialCwd', id); - this._terminalProcesses[id].getInitialCwd().then(initialCwd => { - console.log('initialCwd', initialCwd); - this._proxy.$sendProcessInitialCwd(id, initialCwd); - }); + this._terminalProcesses[id].getInitialCwd().then(initialCwd => this._proxy.$sendProcessInitialCwd(id, initialCwd)); } public $acceptProcessRequestCwd(id: number): void { - console.log('$acceptProcessRequestCwd', id); - this._terminalProcesses[id].getCwd().then(cwd => { - console.log('cwd', cwd); - this._proxy.$sendProcessCwd(id, cwd); - }); + this._terminalProcesses[id].getCwd().then(cwd => this._proxy.$sendProcessCwd(id, cwd)); } private _onProcessExit(id: number, exitCode: number): void { diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts index 31564bafef7..ac30cb45070 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts @@ -30,15 +30,19 @@ import { Command } from 'vs/editor/browser/editorExtensions'; import { timeout } from 'vs/base/common/async'; import { FindReplaceState } from 'vs/editor/contrib/find/findState'; import { ISelectOptionItem } from 'vs/base/browser/ui/selectBox/selectBox'; +import { URI } from 'vs/base/common/uri'; export const TERMINAL_PICKER_PREFIX = 'term '; -function getCwdForSplit(configHelper: ITerminalConfigHelper, instance: ITerminalInstance, folders?: IWorkspaceFolder[], commandService?: ICommandService): Promise { +function getCwdForSplit(configHelper: ITerminalConfigHelper, instance: ITerminalInstance, folders?: IWorkspaceFolder[], commandService?: ICommandService): Promise { switch (configHelper.config.splitCwd) { case 'workspaceRoot': - // allow original behavior - let pathPromise: Promise = Promise.resolve(''); - if (folders.length > 1) { + let pathPromise: Promise; + if (folders.length === 0) { + pathPromise = Promise.resolve(''); + } else if (folders.length === 1) { + pathPromise = Promise.resolve(folders[0].uri); + } else if (folders.length > 1) { // Only choose a path when there's more than 1 folder const options: IPickOptions = { placeHolder: nls.localize('workbench.action.terminal.newWorkspacePlaceholder', "Select current working directory for new terminal") @@ -48,16 +52,13 @@ function getCwdForSplit(configHelper: ITerminalConfigHelper, instance: ITerminal // Don't split the instance if the workspace picker was canceled return undefined; } - return Promise.resolve(workspace.uri.fsPath); + return Promise.resolve(workspace.uri); }); } - return pathPromise; case 'initial': - console.log('getCwdForSplit initial'); return instance.getInitialCwd(); case 'inherited': - console.log('getCwdForSplit inherited'); return instance.getCwd(); } } @@ -382,9 +383,7 @@ export class SplitTerminalAction extends Action { if (!instance) { return Promise.resolve(undefined); } - console.log('SplitTerminalAction'); return getCwdForSplit(this._terminalService.configHelper, instance, this.workspaceContextService.getWorkspace().folders, this.commandService).then(cwd => { - console.log('SplitTerminalAction cwd', cwd); if (cwd || (cwd === '')) { this._terminalService.splitInstance(instance, { cwd }); return this._terminalService.showPanel(true); diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts index ed59e4a655a..93f6d2ac0f1 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts @@ -35,8 +35,6 @@ export class TerminalProcessManager implements ITerminalProcessManager { public processState: ProcessState = ProcessState.UNINITIALIZED; public ptyProcessReady: Promise; public shellProcessId: number; - // TODO: This should be removed in favor of async getInitialCwd - public initialCwd: string; private _process: ITerminalChildProcess | null = null; private _preLaunchInputQueue: string[] = []; @@ -92,24 +90,25 @@ export class TerminalProcessManager implements ITerminalProcessManager { rows: number ): void { let launchRemotely = false; + const forceExtHostProcess = (this._configHelper.config as any).extHostProcess; if (shellLaunchConfig.cwd && typeof shellLaunchConfig.cwd === 'object') { launchRemotely = !!getRemoteAuthority(shellLaunchConfig.cwd); shellLaunchConfig.cwd = shellLaunchConfig.cwd.fsPath; } else { - launchRemotely = !!this._windowService.getConfiguration().remoteAuthority || (this._configHelper.config as any).extHostProcess; + launchRemotely = !!this._windowService.getConfiguration().remoteAuthority; } - if (launchRemotely) { - const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(REMOTE_HOST_SCHEME); + if (launchRemotely || forceExtHostProcess) { + const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(forceExtHostProcess ? undefined : REMOTE_HOST_SCHEME); this._process = this._instantiationService.createInstance(TerminalProcessExtHostProxy, this._terminalId, shellLaunchConfig, activeWorkspaceRootUri, cols, rows); } else { if (!shellLaunchConfig.executable) { this._configHelper.mergeDefaultShellPathAndArgs(shellLaunchConfig); } - // TODO: @daniel + const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(Schemas.file); - this.initialCwd = terminalEnvironment.getCwd(shellLaunchConfig, activeWorkspaceRootUri, this._configHelper.config.cwd); + const initialCwd = terminalEnvironment.getCwd(shellLaunchConfig, activeWorkspaceRootUri, this._configHelper.config.cwd); // Compel type system as process.env should not have any undefined entries let env: platform.IProcessEnvironment = {}; @@ -140,8 +139,8 @@ export class TerminalProcessManager implements ITerminalProcessManager { terminalEnvironment.addTerminalEnvironmentKeys(env, platform.locale, this._configHelper.config.setLocaleVariables); } - this._logService.debug(`Terminal process launching`, shellLaunchConfig, this.initialCwd, cols, rows, env); - this._process = new TerminalProcess(shellLaunchConfig, this.initialCwd, cols, rows, env, this._configHelper.config.windowsEnableConpty); + this._logService.debug(`Terminal process launching`, shellLaunchConfig, initialCwd, cols, rows, env); + this._process = new TerminalProcess(shellLaunchConfig, initialCwd, cols, rows, env, this._configHelper.config.windowsEnableConpty); } this.processState = ProcessState.LAUNCHING; From bc848b75430ff3bed6f744a461ca30121659c7ca Mon Sep 17 00:00:00 2001 From: isidor Date: Mon, 28 Jan 2019 15:06:15 +0100 Subject: [PATCH 211/274] explorer: forgetchildren when item gets outdated fixes #67112 --- .../parts/files/common/explorerModel.ts | 25 +++++++++++++------ .../files/electron-browser/explorerService.ts | 4 +-- .../electron-browser/explorerModel.test.ts | 2 +- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/parts/files/common/explorerModel.ts b/src/vs/workbench/parts/files/common/explorerModel.ts index db6a66248c8..0bbd5075ca8 100644 --- a/src/vs/workbench/parts/files/common/explorerModel.ts +++ b/src/vs/workbench/parts/files/common/explorerModel.ts @@ -73,7 +73,7 @@ export class ExplorerModel implements IDisposable { } export class ExplorerItem { - public isDirectoryResolved: boolean; + private _isDirectoryResolved: boolean; public isError: boolean; constructor( @@ -86,7 +86,11 @@ export class ExplorerItem { private _mtime?: number, private _etag?: string, ) { - this.isDirectoryResolved = false; + this._isDirectoryResolved = false; + } + + get isDirectoryResolved(): boolean { + return this._isDirectoryResolved; } get isSymbolicLink(): boolean { @@ -157,7 +161,7 @@ export class ExplorerItem { // isDirectoryResolved is a very important indicator in the stat model that tells if the folder was fully resolved // the folder is fully resolved if either it has a list of children or the client requested this by using the resolveTo // array of resource path to resolve. - stat.isDirectoryResolved = !!raw.children || (!!resolveTo && resolveTo.some((r) => { + stat._isDirectoryResolved = !!raw.children || (!!resolveTo && resolveTo.some((r) => { return resources.isEqualOrParent(r, stat.resource); })); @@ -185,7 +189,7 @@ export class ExplorerItem { // Stop merging when a folder is not resolved to avoid loosing local data const mergingDirectories = disk.isDirectory || local.isDirectory; - if (mergingDirectories && local.isDirectoryResolved && !disk.isDirectoryResolved) { + if (mergingDirectories && local._isDirectoryResolved && !disk._isDirectoryResolved) { return; } @@ -194,13 +198,13 @@ export class ExplorerItem { local.updateName(disk.name); local._isDirectory = disk.isDirectory; local._mtime = disk.mtime; - local.isDirectoryResolved = disk.isDirectoryResolved; + local._isDirectoryResolved = disk._isDirectoryResolved; local._isSymbolicLink = disk.isSymbolicLink; local._isReadonly = disk.isReadonly; local.isError = disk.isError; // Merge Children if resolved - if (mergingDirectories && disk.isDirectoryResolved) { + if (mergingDirectories && disk._isDirectoryResolved) { // Map resource => stat const oldLocalChildren = new ResourceMap(); @@ -244,11 +248,11 @@ export class ExplorerItem { fetchChildren(fileService: IFileService): Promise { let promise: Promise = Promise.resolve(undefined); - if (!this.isDirectoryResolved) { + if (!this._isDirectoryResolved) { promise = fileService.resolveFile(this.resource, { resolveSingleChildDescendants: true }).then(stat => { const resolved = ExplorerItem.create(stat, this); ExplorerItem.mergeLocalWithDisk(resolved, this); - this.isDirectoryResolved = true; + this._isDirectoryResolved = true; }); } @@ -269,6 +273,11 @@ export class ExplorerItem { this.children.delete(this.getPlatformAwareName(child.name)); } + forgetChildren(): void { + this.children.clear(); + this._isDirectoryResolved = false; + } + private getPlatformAwareName(name: string): string { return (isLinux || !name) ? name : name.toLowerCase(); } diff --git a/src/vs/workbench/parts/files/electron-browser/explorerService.ts b/src/vs/workbench/parts/files/electron-browser/explorerService.ts index a439f87af54..8ac994aede6 100644 --- a/src/vs/workbench/parts/files/electron-browser/explorerService.ts +++ b/src/vs/workbench/parts/files/electron-browser/explorerService.ts @@ -156,7 +156,7 @@ export class ExplorerService implements IExplorerService { } refresh(): void { - this.model.roots.forEach(r => r.isDirectoryResolved = false); + this.model.roots.forEach(r => r.forgetChildren()); this._onDidChangeItem.fire(undefined); } @@ -249,7 +249,7 @@ export class ExplorerService implements IExplorerService { // Filter to the ones we care e = this.filterToViewRelevantEvents(e); const explorerItemChanged = (item: ExplorerItem) => { - item.isDirectoryResolved = false; + item.forgetChildren(); this._onDidChangeItem.fire(item); }; diff --git a/src/vs/workbench/parts/files/test/electron-browser/explorerModel.test.ts b/src/vs/workbench/parts/files/test/electron-browser/explorerModel.test.ts index a5f20068a38..c7dc1ab2380 100644 --- a/src/vs/workbench/parts/files/test/electron-browser/explorerModel.test.ts +++ b/src/vs/workbench/parts/files/test/electron-browser/explorerModel.test.ts @@ -268,7 +268,7 @@ suite('Files - View Model', () => { const child = new ExplorerItem(URI.file(join('C:\\', '/path/to/foo.html')), undefined, true, false, false, 'foo.html', Date.now(), d); merge2.removeChild(child); merge2.addChild(child); - merge2.isDirectoryResolved = true; + (merge2)._isDirectoryResolved = true; ExplorerItem.mergeLocalWithDisk(merge2, merge1); assert.strictEqual(merge1.getChild('foo.html').name, 'foo.html'); assert.deepEqual(merge1.getChild('foo.html').parent, merge1, 'Check parent'); From c5e6633fd189b80ecedf2e99bd61c0a83e1ee611 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 28 Jan 2019 15:10:58 +0100 Subject: [PATCH 212/274] avoid doing unnecessary filters fixes #65213 fixes #67222 --- src/vs/base/browser/ui/tree/abstractTree.ts | 4 ++++ src/vs/base/browser/ui/tree/asyncDataTree.ts | 25 ++++++++++---------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index e8916590c1d..3cace23df45 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -547,6 +547,10 @@ class TypeFilterController implements IDisposable { } private onDidSpliceModel(): void { + if (!this.enabled || this.pattern.length === 0) { + return; + } + this.tree.refilter(); this.render(); } diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index e6b0b855f9d..80b14c23900 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -28,8 +28,8 @@ enum AsyncDataTreeNodeState { interface IAsyncDataTreeNode { element: TInput | T; readonly parent: IAsyncDataTreeNode | null; + readonly children: IAsyncDataTreeNode[]; readonly id?: string | null; - readonly children?: IAsyncDataTreeNode[]; state: AsyncDataTreeNodeState; } @@ -224,7 +224,7 @@ function asObjectTreeOptions(options?: IAsyncDataTreeOpt function asTreeElement(node: IAsyncDataTreeNode): ITreeElement> { return { element: node, - children: Iterator.map(Iterator.fromArray(node.children!), asTreeElement) + children: Iterator.map(Iterator.fromArray(node.children), asTreeElement) }; } @@ -296,14 +296,14 @@ export class AsyncDataTree implements IDisposable this.root = { element: undefined!, parent: null, + children: [], state: AsyncDataTreeNodeState.Uninitialized, }; if (this.identityProvider) { this.root = { ...this.root, - id: null, - children: [], + id: null }; } @@ -539,7 +539,7 @@ export class AsyncDataTree implements IDisposable private async refreshNode(node: IAsyncDataTreeNode, recursive: boolean, reason: ChildrenResolutionReason, viewStateContext?: IAsyncDataTreeViewStateContext): Promise { await this.queueRefresh(node, recursive, reason, viewStateContext); - if (recursive && node.children) { + if (recursive) { await Promise.all(node.children.map(child => this.refreshNode(child, recursive, reason, viewStateContext))); } } @@ -652,7 +652,7 @@ export class AsyncDataTree implements IDisposable if (this.identityProvider) { nodeChildren = new Map(); - for (const child of node.children!) { + for (const child of node.children) { nodeChildren.set(child.id!, child); } } @@ -663,6 +663,7 @@ export class AsyncDataTree implements IDisposable element: { element, parent: node, + children: [], state: AsyncDataTreeNodeState.Uninitialized }, collapsible: !!this.dataSource.hasChildren(element), @@ -708,7 +709,7 @@ export class AsyncDataTree implements IDisposable asyncDataTreeNode.state = AsyncDataTreeNodeState.Uninitialized; if (this.tree.isCollapsed(asyncDataTreeNode === this.root ? null : asyncDataTreeNode)) { - asyncDataTreeNode.children!.length = 0; + asyncDataTreeNode.children.length = 0; return { element: asyncDataTreeNode, @@ -721,7 +722,7 @@ export class AsyncDataTree implements IDisposable let children: Iterator>> | undefined = undefined; if (collapsible) { - children = Iterator.map(Iterator.fromArray(asyncDataTreeNode.children!), asTreeElement); + children = Iterator.map(Iterator.fromArray(asyncDataTreeNode.children), asTreeElement); } return { @@ -750,10 +751,10 @@ export class AsyncDataTree implements IDisposable } }; - this.tree.setChildren(node === this.root ? null : node, children, onDidCreateNode, onDidDeleteNode); - - if (this.identityProvider) { - node.children!.splice(0, node.children!.length, ...children.map(c => c.element)); + // perf: if the node was and still is a leaf, avoid all these expensive no-ops + if (node.children.length > 0 || children.length > 0) { + this.tree.setChildren(node === this.root ? null : node, children, onDidCreateNode, onDidDeleteNode); + node.children.splice(0, node.children.length, ...children.map(c => c.element)); } } From 5d5d386403ac434a8a81dac554eb947fc6ca08dd Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 28 Jan 2019 06:23:46 -0800 Subject: [PATCH 213/274] Fix strict null checks --- .../terminal/electron-browser/terminalProcessManager.ts | 6 ++++++ .../parts/terminal/node/terminalProcessExtHostProxy.ts | 6 +++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts index 93f6d2ac0f1..6eb9ec9906c 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts @@ -201,10 +201,16 @@ export class TerminalProcessManager implements ITerminalProcessManager { } public getInitialCwd(): Promise { + if (!this._process) { + return Promise.resolve(''); + } return this._process.getInitialCwd(); } public getCwd(): Promise { + if (!this._process) { + return Promise.resolve(''); + } return this._process.getCwd(); } diff --git a/src/vs/workbench/parts/terminal/node/terminalProcessExtHostProxy.ts b/src/vs/workbench/parts/terminal/node/terminalProcessExtHostProxy.ts index 653e09b65dd..dbf10b10214 100644 --- a/src/vs/workbench/parts/terminal/node/terminalProcessExtHostProxy.ts +++ b/src/vs/workbench/parts/terminal/node/terminalProcessExtHostProxy.ts @@ -77,13 +77,13 @@ export class TerminalProcessExtHostProxy implements ITerminalChildProcess, ITerm public emitInitialCwd(initialCwd: string): void { while (this._pendingInitialCwdRequests.length > 0) { - this._pendingInitialCwdRequests.pop()(initialCwd); + this._pendingInitialCwdRequests.pop()!(initialCwd); } } public emitCwd(cwd: string): void { while (this._pendingCwdRequests.length > 0) { - this._pendingCwdRequests.pop()(cwd); + this._pendingCwdRequests.pop()!(cwd); } } @@ -100,7 +100,7 @@ export class TerminalProcessExtHostProxy implements ITerminalChildProcess, ITerm } public getInitialCwd(): Promise { - return new Promise(resolve => { + return new Promise(resolve => { this._onRequestInitialCwd.fire(); this._pendingInitialCwdRequests.push(resolve); }); From ea84e77a9e12be13d76f2e11746f55eaf0b0b05b Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 28 Jan 2019 15:44:34 +0100 Subject: [PATCH 214/274] fix #64429 --- .../browser/parts/editor/media/notabstitlecontrol.css | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css b/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css index 6b309377039..4c96ebbc1f1 100644 --- a/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css @@ -60,7 +60,9 @@ .monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item.root_folder::before, .monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item.root_folder + .monaco-breadcrumb-item::before, -.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control.relative-path .monaco-breadcrumb-item:nth-child(2)::before { +.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control.relative-path .monaco-breadcrumb-item:nth-child(2)::before, +.windows > .monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item:nth-child(2)::before, +{ /* workspace folder, item following workspace folder, or relative path -> hide first seperator */ display: none; } @@ -68,7 +70,7 @@ .monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item.root_folder::after { /* use dot separator for workspace folder */ content: '\00a0•\00a0'; - padding: 0px; + padding: 0; } .monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item:last-child { From 363e829071b1b7d123024172836f0f05776acc70 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 28 Jan 2019 06:59:31 -0800 Subject: [PATCH 215/274] Fix clicking on dock in Ubuntu opening additional windows Fixes #65460 --- resources/linux/code.desktop | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/linux/code.desktop b/resources/linux/code.desktop index f93060d565b..dbc7818cecf 100644 --- a/resources/linux/code.desktop +++ b/resources/linux/code.desktop @@ -5,7 +5,7 @@ GenericName=Text Editor Exec=/usr/share/@@NAME@@/@@NAME@@ --unity-launch %F Icon=@@ICON@@ Type=Application -StartupNotify=true +StartupNotify=false StartupWMClass=@@NAME_SHORT@@ Categories=Utility;TextEditor;Development;IDE; MimeType=text/plain;inode/directory; From 7d94471759fa47fc4283a8845e4b5a9bded2cbf3 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 28 Jan 2019 15:41:56 +0100 Subject: [PATCH 216/274] fixes #65397 --- .../workbench/browser/actions/listCommands.ts | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/browser/actions/listCommands.ts b/src/vs/workbench/browser/actions/listCommands.ts index 196930c81b7..1ee1e0e8491 100644 --- a/src/vs/workbench/browser/actions/listCommands.ts +++ b/src/vs/workbench/browser/actions/listCommands.ts @@ -27,7 +27,7 @@ function ensureDOMFocus(widget: ListWidget): void { } } -function focusDown(accessor: ServicesAccessor, arg2?: number): void { +function focusDown(accessor: ServicesAccessor, arg2?: number, loop: boolean = true): void { const focused = accessor.get(IListService).lastFocusedList; const count = typeof arg2 === 'number' ? arg2 : 1; @@ -50,7 +50,7 @@ function focusDown(accessor: ServicesAccessor, arg2?: number): void { const tree = focused; const fakeKeyboardEvent = new KeyboardEvent('keydown'); - tree.focusNext(count, true, fakeKeyboardEvent); + tree.focusNext(count, loop, fakeKeyboardEvent); const listFocus = tree.getFocus(); if (listFocus.length) { @@ -99,6 +99,11 @@ function expandMultiSelection(focused: List | PagedList | ITree | Obje const list = focused; const focus = list.getFocus() ? list.getFocus()[0] : undefined; + + if (previousFocus === focus) { + return; + } + const selection = list.getSelection(); const fakeKeyboardEvent = new KeyboardEvent('keydown', { shiftKey: true }); @@ -137,7 +142,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ // Focus down first const previousFocus = list.getFocus() ? list.getFocus()[0] : undefined; - focusDown(accessor, arg2); + focusDown(accessor, arg2, false); // Then adjust selection expandMultiSelection(focused, previousFocus); @@ -157,7 +162,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ } }); -function focusUp(accessor: ServicesAccessor, arg2?: number): void { +function focusUp(accessor: ServicesAccessor, arg2?: number, loop: boolean = true): void { const focused = accessor.get(IListService).lastFocusedList; const count = typeof arg2 === 'number' ? arg2 : 1; @@ -180,7 +185,7 @@ function focusUp(accessor: ServicesAccessor, arg2?: number): void { const tree = focused; const fakeKeyboardEvent = new KeyboardEvent('keydown'); - tree.focusPrevious(count, true, fakeKeyboardEvent); + tree.focusPrevious(count, loop, fakeKeyboardEvent); const listFocus = tree.getFocus(); if (listFocus.length) { @@ -223,7 +228,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ // Focus up first const previousFocus = list.getFocus() ? list.getFocus()[0] : undefined; - focusUp(accessor, arg2); + focusUp(accessor, arg2, false); // Then adjust selection expandMultiSelection(focused, previousFocus); From 6f2daa3fe2be9bd4343d3bb856549ae9530937fd Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 28 Jan 2019 16:03:39 +0100 Subject: [PATCH 217/274] tree.updateWidth(element) --- src/vs/base/browser/ui/list/listView.ts | 56 ++++++++++++------- src/vs/base/browser/ui/list/listWidget.ts | 4 ++ src/vs/base/browser/ui/tree/abstractTree.ts | 10 ++++ src/vs/base/browser/ui/tree/asyncDataTree.ts | 5 ++ .../electron-browser/views/explorerView.ts | 3 +- .../electron-browser/views/explorerViewer.ts | 4 +- 6 files changed, 60 insertions(+), 22 deletions(-) diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 18f70e285a8..b599fe92406 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -165,6 +165,7 @@ export class ListView implements ISpliceable, IDisposable { private setRowLineHeight: boolean; private supportDynamicHeights: boolean; private horizontalScrolling: boolean; + private scrollWidth: number | undefined; private canUseTranslate3d: boolean | undefined = undefined; private dnd: IListViewDragAndDrop; @@ -387,9 +388,24 @@ export class ListView implements ISpliceable, IDisposable { } } + this.scrollWidth = scrollWidth; this.scrollableElement.setScrollDimensions({ scrollWidth: scrollWidth + 10 }); } + updateWidth(index: number): void { + if (!this.horizontalScrolling || typeof this.scrollWidth === 'undefined') { + return; + } + + const item = this.items[index]; + this.measureItemWidth(item); + + if (typeof item.width !== 'undefined' && item.width > this.scrollWidth) { + this.scrollWidth = item.width; + this.scrollableElement.setScrollDimensions({ scrollWidth: this.scrollWidth + 10 }); + } + } + get length(): number { return this.items.length; } @@ -530,29 +546,10 @@ export class ListView implements ISpliceable, IDisposable { throw new Error(`No renderer found for template id ${item.templateId}`); } - if (this.horizontalScrolling) { - item.row.domNode!.style.width = 'fit-content'; - } - if (renderer) { renderer.renderElement(item.element, index, item.row.templateData); } - if (this.horizontalScrolling) { - item.width = DOM.getContentWidth(item.row.domNode!); - const style = window.getComputedStyle(item.row.domNode!); - - if (style.paddingLeft) { - item.width += parseFloat(style.paddingLeft); - } - - if (style.paddingRight) { - item.width += parseFloat(style.paddingRight); - } - - item.row.domNode!.style.width = ''; - } - const uri = this.dnd.getDragURI(item.element); item.dragStartDisposable.dispose(); @@ -563,10 +560,31 @@ export class ListView implements ISpliceable, IDisposable { } if (this.horizontalScrolling) { + this.measureItemWidth(item); this.eventuallyUpdateScrollWidth(); } } + private measureItemWidth(item: IItem): void { + if (!item.row || !item.row.domNode) { + return; + } + + item.row.domNode.style.width = 'fit-content'; + item.width = DOM.getContentWidth(item.row.domNode); + const style = window.getComputedStyle(item.row.domNode); + + if (style.paddingLeft) { + item.width += parseFloat(style.paddingLeft); + } + + if (style.paddingRight) { + item.width += parseFloat(style.paddingRight); + } + + item.row.domNode.style.width = ''; + } + private updateItemInDOM(item: IItem, index: number): void { item.row!.domNode!.style.top = `${this.elementTop(index)}px`; item.row!.domNode!.style.height = `${item.size}px`; diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 81dedadaa7e..87da7ffcb11 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -1243,6 +1243,10 @@ export class List implements ISpliceable, IDisposable { this.eventBufferer.bufferEvents(() => this.spliceable.splice(start, deleteCount, elements)); } + updateWidth(index: number): void { + this.view.updateWidth(index); + } + element(index: number): T { return this.view.element(index); } diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 3cace23df45..145e9366547 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -734,6 +734,16 @@ export abstract class AbstractTree implements IDisposable return this._options; } + updateWidth(element: TRef): void { + const index = this.model.getListIndex(element); + + if (index === -1) { + return; + } + + this.view.updateWidth(index); + } + // Widget getHTMLElement(): HTMLElement { diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index 80b14c23900..c4ea62fed4a 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -395,6 +395,11 @@ export class AsyncDataTree implements IDisposable this.tree.refresh(node); } + updateWidth(element: T): void { + const node = this.getDataNode(element); + this.tree.updateWidth(node); + } + // Tree getNode(element: TInput | T = this.root.element): ITreeNode { diff --git a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts index 7bf3b826f07..67b3aa76db8 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -249,7 +249,8 @@ export class ExplorerView extends ViewletPanel { const explorerLabels = this.instantiationService.createInstance(ResourceLabels, { onDidChangeVisibility: this.onDidChangeBodyVisibility } as IResourceLabelsContainer); this.disposables.push(explorerLabels); - const filesRenderer = this.instantiationService.createInstance(FilesRenderer, explorerLabels); + const updateWidth = (stat: ExplorerItem) => this.tree.updateWidth(stat); + const filesRenderer = this.instantiationService.createInstance(FilesRenderer, explorerLabels, updateWidth); this.disposables.push(filesRenderer); this.disposables.push(createFileIconThemableTreeContainerScope(container, this.themeService)); diff --git a/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts b/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts index d2b763b4890..e9f412f022b 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts @@ -107,6 +107,7 @@ export class FilesRenderer implements ITreeRenderer void, @IContextViewService private readonly contextViewService: IContextViewService, @IThemeService private readonly themeService: IThemeService, @IConfigurationService private readonly configurationService: IConfigurationService, @@ -152,8 +153,7 @@ export class FilesRenderer implements ITreeRenderer { - // todo@isidor horizontal scrolling - // this.tree.updateWidth(stat); + this.updateWidth(stat); }); } From 5f4406ce838fb7520f4d7b5c99e16315bbd6c282 Mon Sep 17 00:00:00 2001 From: isidor Date: Mon, 28 Jan 2019 16:00:59 +0100 Subject: [PATCH 218/274] fixes #67187 --- src/vs/workbench/parts/files/browser/files.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/parts/files/browser/files.ts b/src/vs/workbench/parts/files/browser/files.ts index e6953ee8700..006ae8c00db 100644 --- a/src/vs/workbench/parts/files/browser/files.ts +++ b/src/vs/workbench/parts/files/browser/files.ts @@ -27,8 +27,11 @@ export function getResourceForCommand(resource: URI | object, listService: IList if (focused.length) { focus = focused[0]; } - } else { - focus = list.getFocus(); + } else if (list instanceof WorkbenchAsyncDataTree) { + const focused = list.getFocus(); + if (focused.length) { + focus = focused[0]; + } } if (focus instanceof ExplorerItem) { @@ -47,7 +50,8 @@ export function getMultiSelectedResources(resource: URI | object, listService: I // Explorer if (list instanceof WorkbenchAsyncDataTree) { const selection = list.getSelection().map((fs: ExplorerItem) => fs.resource); - const focus = list.getFocus(); + const focusedElements = list.getFocus(); + const focus = focusedElements.length ? focusedElements[0] : undefined; const mainUriStr = URI.isUri(resource) ? resource.toString() : focus instanceof ExplorerItem ? focus.resource.toString() : undefined; // If the resource is passed it has to be a part of the returned context. // We only respect the selection if it contains the focused element. From 21585312c29ff26afac6fbc55eab8d3820254b4d Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 28 Jan 2019 16:05:18 +0100 Subject: [PATCH 219/274] [json] enable semantic selection --- .../client/src/cssMain.ts | 6 +- .../client/src/typings/ref.d.ts | 2 +- .../client/src/vscode.proposed.d.ts | 1142 ----------------- extensions/css-language-features/package.json | 2 +- .../client/src/typings/ref.d.ts | 1 + .../client/src/vscode.proposed.d.ts | 1142 ----------------- .../client/src/jsonMain.ts | 22 +- .../client/src/typings/ref.d.ts | 1 + .../json-language-features/package.json | 3 +- .../server/package.json | 2 +- .../server/src/jsonServerMain.ts | 11 + .../json-language-features/server/yarn.lock | 8 +- 12 files changed, 46 insertions(+), 2296 deletions(-) delete mode 100644 extensions/css-language-features/client/src/vscode.proposed.d.ts delete mode 100644 extensions/html-language-features/client/src/vscode.proposed.d.ts diff --git a/extensions/css-language-features/client/src/cssMain.ts b/extensions/css-language-features/client/src/cssMain.ts index 3108b13e5bd..18f82a02d77 100644 --- a/extensions/css-language-features/client/src/cssMain.ts +++ b/extensions/css-language-features/client/src/cssMain.ts @@ -94,9 +94,9 @@ export function activate(context: ExtensionContext) { }); } }; - languages.registerSelectionRangeProvider('css', selectionRangeProvider); - languages.registerSelectionRangeProvider('less', selectionRangeProvider); - languages.registerSelectionRangeProvider('scss', selectionRangeProvider); + documentSelector.forEach(selector => { + languages.registerSelectionRangeProvider(selector, selectionRangeProvider); + }); function initCompletionProvider(): Disposable { const regionCompletionRegExpr = /^(\s*)(\/(\*\s*(#\w*)?)?)?$/; diff --git a/extensions/css-language-features/client/src/typings/ref.d.ts b/extensions/css-language-features/client/src/typings/ref.d.ts index 9c1a5df18ed..de602d3f8d6 100644 --- a/extensions/css-language-features/client/src/typings/ref.d.ts +++ b/extensions/css-language-features/client/src/typings/ref.d.ts @@ -2,5 +2,5 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ - /// +/// diff --git a/extensions/css-language-features/client/src/vscode.proposed.d.ts b/extensions/css-language-features/client/src/vscode.proposed.d.ts deleted file mode 100644 index 44ad9a1d01b..00000000000 --- a/extensions/css-language-features/client/src/vscode.proposed.d.ts +++ /dev/null @@ -1,1142 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -/** - * This is the place for API experiments and proposals. - * These API are NOT stable and subject to change. They are only available in the Insiders - * distribution and CANNOT be used in published extensions. - * - * To test these API in local environment: - * - Use Insiders release of VS Code. - * - Add `"enableProposedApi": true` to your package.json. - * - Copy this file to your project. - */ - -declare module 'vscode' { - - //#region Joh - vscode.open - - export namespace env { - - /** - * Opens an *external* item, e.g. a http(s) or mailto-link, using the - * default application. - * - * *Note* that [`showTextDocument`](#window.showTextDocument) is the right - * way to open a text document inside the editor, not this function. - * - * @param target The uri that should be opened. - * @returns A promise indicating if open was successful. - */ - export function open(target: Uri): Thenable; - } - - //#endregion - - //#region Joh - selection range provider - - export class SelectionRangeKind { - - /** - * Empty Kind. - */ - static readonly Empty: SelectionRangeKind; - - /** - * The statment kind, its value is `statement`, possible extensions can be - * `statement.if` etc - */ - static readonly Statement: SelectionRangeKind; - - /** - * The declaration kind, its value is `declaration`, possible extensions can be - * `declaration.function`, `declaration.class` etc. - */ - static readonly Declaration: SelectionRangeKind; - - readonly value: string; - - private constructor(value: string); - - append(value: string): SelectionRangeKind; - } - - export class SelectionRange { - kind: SelectionRangeKind; - range: Range; - constructor(range: Range, kind: SelectionRangeKind); - } - - export interface SelectionRangeProvider { - /** - * Provide selection ranges starting at a given position. The first range must [contain](#Range.contains) - * position and subsequent ranges must contain the previous range. - */ - provideSelectionRanges(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; - } - - export namespace languages { - export function registerSelectionRangeProvider(selector: DocumentSelector, provider: SelectionRangeProvider): Disposable; - } - - //#endregion - - //#region Joh - read/write in chunks - - export interface FileSystemProvider { - open?(resource: Uri): number | Thenable; - close?(fd: number): void | Thenable; - read?(fd: number, pos: number, data: Uint8Array, offset: number, length: number): number | Thenable; - write?(fd: number, pos: number, data: Uint8Array, offset: number, length: number): number | Thenable; - } - - //#endregion - - //#region Rob: search provider - - /** - * The parameters of a query for text search. - */ - export interface TextSearchQuery { - /** - * The text pattern to search for. - */ - pattern: string; - - /** - * Whether or not `pattern` should match multiple lines of text. - */ - isMultiline?: boolean; - - /** - * Whether or not `pattern` should be interpreted as a regular expression. - */ - isRegExp?: boolean; - - /** - * Whether or not the search should be case-sensitive. - */ - isCaseSensitive?: boolean; - - /** - * Whether or not to search for whole word matches only. - */ - isWordMatch?: boolean; - } - - /** - * A file glob pattern to match file paths against. - * TODO@roblou - merge this with the GlobPattern docs/definition in vscode.d.ts. - * @see [GlobPattern](#GlobPattern) - */ - export type GlobString = string; - - /** - * Options common to file and text search - */ - export interface SearchOptions { - /** - * The root folder to search within. - */ - folder: Uri; - - /** - * Files that match an `includes` glob pattern should be included in the search. - */ - includes: GlobString[]; - - /** - * Files that match an `excludes` glob pattern should be excluded from the search. - */ - excludes: GlobString[]; - - /** - * Whether external files that exclude files, like .gitignore, should be respected. - * See the vscode setting `"search.useIgnoreFiles"`. - */ - useIgnoreFiles: boolean; - - /** - * Whether symlinks should be followed while searching. - * See the vscode setting `"search.followSymlinks"`. - */ - followSymlinks: boolean; - - /** - * Whether global files that exclude files, like .gitignore, should be respected. - * See the vscode setting `"search.useGlobalIgnoreFiles"`. - */ - useGlobalIgnoreFiles: boolean; - - } - - /** - * Options to specify the size of the result text preview. - * These options don't affect the size of the match itself, just the amount of preview text. - */ - export interface TextSearchPreviewOptions { - /** - * The maximum number of lines in the preview. - * Only search providers that support multiline search will ever return more than one line in the match. - */ - matchLines: number; - - /** - * The maximum number of characters included per line. - */ - charsPerLine: number; - } - - /** - * Options that apply to text search. - */ - export interface TextSearchOptions extends SearchOptions { - /** - * The maximum number of results to be returned. - */ - maxResults: number; - - /** - * Options to specify the size of the result text preview. - */ - previewOptions?: TextSearchPreviewOptions; - - /** - * Exclude files larger than `maxFileSize` in bytes. - */ - maxFileSize?: number; - - /** - * Interpret files using this encoding. - * See the vscode setting `"files.encoding"` - */ - encoding?: string; - - /** - * Number of lines of context to include before each match. - */ - beforeContext?: number; - - /** - * Number of lines of context to include after each match. - */ - afterContext?: number; - } - - /** - * Information collected when text search is complete. - */ - export interface TextSearchComplete { - /** - * Whether the search hit the limit on the maximum number of search results. - * `maxResults` on [`TextSearchOptions`](#TextSearchOptions) specifies the max number of results. - * - If exactly that number of matches exist, this should be false. - * - If `maxResults` matches are returned and more exist, this should be true. - * - If search hits an internal limit which is less than `maxResults`, this should be true. - */ - limitHit?: boolean; - } - - /** - * The parameters of a query for file search. - */ - export interface FileSearchQuery { - /** - * The search pattern to match against file paths. - */ - pattern: string; - } - - /** - * Options that apply to file search. - */ - export interface FileSearchOptions extends SearchOptions { - /** - * The maximum number of results to be returned. - */ - maxResults?: number; - - /** - * A CancellationToken that represents the session for this search query. If the provider chooses to, this object can be used as the key for a cache, - * and searches with the same session object can search the same cache. When the token is cancelled, the session is complete and the cache can be cleared. - */ - session?: CancellationToken; - } - - /** - * Options that apply to requesting the file index. - */ - export interface FileIndexOptions extends SearchOptions { } - - /** - * A preview of the text result. - */ - export interface TextSearchMatchPreview { - /** - * The matching lines of text, or a portion of the matching line that contains the match. - */ - text: string; - - /** - * The Range within `text` corresponding to the text of the match. - * The number of matches must match the TextSearchMatch's range property. - */ - matches: Range | Range[]; - } - - /** - * A match from a text search - */ - export interface TextSearchMatch { - /** - * The uri for the matching document. - */ - uri: Uri; - - /** - * The range of the match within the document, or multiple ranges for multiple matches. - */ - ranges: Range | Range[]; - - /** - * A preview of the text match. - */ - preview: TextSearchMatchPreview; - } - - /** - * A line of context surrounding a TextSearchMatch. - */ - export interface TextSearchContext { - /** - * The uri for the matching document. - */ - uri: Uri; - - /** - * One line of text. - * previewOptions.charsPerLine applies to this - */ - text: string; - - /** - * The line number of this line of context. - */ - lineNumber: number; - } - - export type TextSearchResult = TextSearchMatch | TextSearchContext; - - /** - * A FileIndexProvider provides a list of files in the given folder. VS Code will filter that list for searching with quickopen or from other extensions. - * - * A FileIndexProvider is the simpler of two ways to implement file search in VS Code. Use a FileIndexProvider if you are able to provide a listing of all files - * in a folder, and want VS Code to filter them according to the user's search query. - * - * The FileIndexProvider will be invoked once when quickopen is opened, and VS Code will filter the returned list. It will also be invoked when - * `workspace.findFiles` is called. - * - * If a [`FileSearchProvider`](#FileSearchProvider) is registered for the scheme, that provider will be used instead. - */ - export interface FileIndexProvider { - /** - * Provide the set of files in the folder. - * @param options A set of options to consider while searching. - * @param token A cancellation token. - */ - provideFileIndex(options: FileIndexOptions, token: CancellationToken): ProviderResult; - } - - /** - * A FileSearchProvider provides search results for files in the given folder that match a query string. It can be invoked by quickopen or other extensions. - * - * A FileSearchProvider is the more powerful of two ways to implement file search in VS Code. Use a FileSearchProvider if you wish to search within a folder for - * all files that match the user's query. - * - * The FileSearchProvider will be invoked on every keypress in quickopen. When `workspace.findFiles` is called, it will be invoked with an empty query string, - * and in that case, every file in the folder should be returned. - * - * @see [FileIndexProvider](#FileIndexProvider) - */ - export interface FileSearchProvider { - /** - * Provide the set of files that match a certain file path pattern. - * @param query The parameters for this query. - * @param options A set of options to consider while searching files. - * @param progress A progress callback that must be invoked for all results. - * @param token A cancellation token. - */ - provideFileSearchResults(query: FileSearchQuery, options: FileSearchOptions, token: CancellationToken): ProviderResult; - } - - /** - * A TextSearchProvider provides search results for text results inside files in the workspace. - */ - export interface TextSearchProvider { - /** - * Provide results that match the given text pattern. - * @param query The parameters for this query. - * @param options A set of options to consider while searching. - * @param progress A progress callback that must be invoked for all results. - * @param token A cancellation token. - */ - provideTextSearchResults(query: TextSearchQuery, options: TextSearchOptions, progress: Progress, token: CancellationToken): ProviderResult; - } - - /** - * Options that can be set on a findTextInFiles search. - */ - export interface FindTextInFilesOptions { - /** - * A [glob pattern](#GlobPattern) that defines the files to search for. The glob pattern - * will be matched against the file paths of files relative to their workspace. Use a [relative pattern](#RelativePattern) - * to restrict the search results to a [workspace folder](#WorkspaceFolder). - */ - include?: GlobPattern; - - /** - * A [glob pattern](#GlobPattern) that defines files and folders to exclude. The glob pattern - * will be matched against the file paths of resulting matches relative to their workspace. When `undefined` only default excludes will - * apply, when `null` no excludes will apply. - */ - exclude?: GlobPattern | null; - - /** - * The maximum number of results to search for - */ - maxResults?: number; - - /** - * Whether external files that exclude files, like .gitignore, should be respected. - * See the vscode setting `"search.useIgnoreFiles"`. - */ - useIgnoreFiles?: boolean; - - /** - * Whether global files that exclude files, like .gitignore, should be respected. - * See the vscode setting `"search.useGlobalIgnoreFiles"`. - */ - useGlobalIgnoreFiles?: boolean; - - /** - * Whether symlinks should be followed while searching. - * See the vscode setting `"search.followSymlinks"`. - */ - followSymlinks?: boolean; - - /** - * Interpret files using this encoding. - * See the vscode setting `"files.encoding"` - */ - encoding?: string; - - /** - * Options to specify the size of the result text preview. - */ - previewOptions?: TextSearchPreviewOptions; - - /** - * Number of lines of context to include before each match. - */ - beforeContext?: number; - - /** - * Number of lines of context to include after each match. - */ - afterContext?: number; - } - - export namespace workspace { - /** - * DEPRECATED - */ - export function registerSearchProvider(): Disposable; - - /** - * Register a file index provider. - * - * Only one provider can be registered per scheme. - * - * @param scheme The provider will be invoked for workspace folders that have this file scheme. - * @param provider The provider. - * @return A [disposable](#Disposable) that unregisters this provider when being disposed. - */ - export function registerFileIndexProvider(scheme: string, provider: FileIndexProvider): Disposable; - - /** - * Register a search provider. - * - * Only one provider can be registered per scheme. - * - * @param scheme The provider will be invoked for workspace folders that have this file scheme. - * @param provider The provider. - * @return A [disposable](#Disposable) that unregisters this provider when being disposed. - */ - export function registerFileSearchProvider(scheme: string, provider: FileSearchProvider): Disposable; - - /** - * Register a text search provider. - * - * Only one provider can be registered per scheme. - * - * @param scheme The provider will be invoked for workspace folders that have this file scheme. - * @param provider The provider. - * @return A [disposable](#Disposable) that unregisters this provider when being disposed. - */ - export function registerTextSearchProvider(scheme: string, provider: TextSearchProvider): Disposable; - - /** - * Search text in files across all [workspace folders](#workspace.workspaceFolders) in the workspace. - * @param query The query parameters for the search - the search string, whether it's case-sensitive, or a regex, or matches whole words. - * @param callback A callback, called for each result - * @param token A token that can be used to signal cancellation to the underlying search engine. - * @return A thenable that resolves when the search is complete. - */ - export function findTextInFiles(query: TextSearchQuery, callback: (result: TextSearchResult) => void, token?: CancellationToken): Thenable; - - /** - * Search text in files across all [workspace folders](#workspace.workspaceFolders) in the workspace. - * @param query The query parameters for the search - the search string, whether it's case-sensitive, or a regex, or matches whole words. - * @param options An optional set of query options. Include and exclude patterns, maxResults, etc. - * @param callback A callback, called for each result - * @param token A token that can be used to signal cancellation to the underlying search engine. - * @return A thenable that resolves when the search is complete. - */ - export function findTextInFiles(query: TextSearchQuery, options: FindTextInFilesOptions, callback: (result: TextSearchResult) => void, token?: CancellationToken): Thenable; - } - - //#endregion - - //#region Joao: diff command - - /** - * The contiguous set of modified lines in a diff. - */ - export interface LineChange { - readonly originalStartLineNumber: number; - readonly originalEndLineNumber: number; - readonly modifiedStartLineNumber: number; - readonly modifiedEndLineNumber: number; - } - - export namespace commands { - - /** - * Registers a diff information command that can be invoked via a keyboard shortcut, - * a menu item, an action, or directly. - * - * Diff information commands are different from ordinary [commands](#commands.registerCommand) as - * they only execute when there is an active diff editor when the command is called, and the diff - * information has been computed. Also, the command handler of an editor command has access to - * the diff information. - * - * @param command A unique identifier for the command. - * @param callback A command handler function with access to the [diff information](#LineChange). - * @param thisArg The `this` context used when invoking the handler function. - * @return Disposable which unregisters this command on disposal. - */ - export function registerDiffInformationCommand(command: string, callback: (diff: LineChange[], ...args: any[]) => any, thisArg?: any): Disposable; - } - - //#endregion - - //#region Joh: decorations - - //todo@joh -> make class - export interface DecorationData { - letter?: string; - title?: string; - color?: ThemeColor; - priority?: number; - bubble?: boolean; - source?: string; // hacky... we should remove it and use equality under the hood - } - - export interface SourceControlResourceDecorations { - source?: string; - letter?: string; - color?: ThemeColor; - } - - export interface DecorationProvider { - onDidChangeDecorations: Event; - provideDecoration(uri: Uri, token: CancellationToken): ProviderResult; - } - - export namespace window { - export function registerDecorationProvider(provider: DecorationProvider): Disposable; - } - - //#endregion - - //#region André: debug - - // deprecated - - export interface DebugConfigurationProvider { - /** - * Deprecated, use DebugAdapterDescriptorFactory.provideDebugAdapter instead. - * @deprecated Use DebugAdapterDescriptorFactory.createDebugAdapterDescriptor instead - */ - debugAdapterExecutable?(folder: WorkspaceFolder | undefined, token?: CancellationToken): ProviderResult; - } - - //#endregion - - //#region Rob, Matt: logging - - /** - * The severity level of a log message - */ - export enum LogLevel { - Trace = 1, - Debug = 2, - Info = 3, - Warning = 4, - Error = 5, - Critical = 6, - Off = 7 - } - - export namespace env { - /** - * Current logging level. - */ - export const logLevel: LogLevel; - - /** - * An [event](#Event) that fires when the log level has changed. - */ - export const onDidChangeLogLevel: Event; - } - - //#endregion - - //#region Joao: SCM validation - - /** - * Represents the validation type of the Source Control input. - */ - export enum SourceControlInputBoxValidationType { - - /** - * Something not allowed by the rules of a language or other means. - */ - Error = 0, - - /** - * Something suspicious but allowed. - */ - Warning = 1, - - /** - * Something to inform about but not a problem. - */ - Information = 2 - } - - export interface SourceControlInputBoxValidation { - - /** - * The validation message to display. - */ - readonly message: string; - - /** - * The validation type. - */ - readonly type: SourceControlInputBoxValidationType; - } - - /** - * Represents the input box in the Source Control viewlet. - */ - export interface SourceControlInputBox { - - /** - * A validation function for the input box. It's possible to change - * the validation provider simply by setting this property to a different function. - */ - validateInput?(value: string, cursorPosition: number): ProviderResult; - } - - //#endregion - - //#region Joao: SCM selected provider - - export interface SourceControl { - - /** - * Whether the source control is selected. - */ - readonly selected: boolean; - - /** - * An event signaling when the selection state changes. - */ - readonly onDidChangeSelection: Event; - } - - //#endregion - - //#region Joao: SCM Input Box - - /** - * Represents the input box in the Source Control viewlet. - */ - export interface SourceControlInputBox { - - /** - * Controls whether the input box is visible (default is `true`). - */ - visible: boolean; - } - - //#endregion - - //#region Comments - /** - * Comments provider related APIs are still in early stages, they may be changed significantly during our API experiments. - */ - - interface CommentInfo { - /** - * All of the comment threads associated with the document. - */ - threads: CommentThread[]; - - /** - * The ranges of the document which support commenting. - */ - commentingRanges?: Range[]; - - /** - * If it's in draft mode or not - */ - inDraftMode?: boolean; - } - - export enum CommentThreadCollapsibleState { - /** - * Determines an item is collapsed - */ - Collapsed = 0, - /** - * Determines an item is expanded - */ - Expanded = 1 - } - - /** - * A collection of comments representing a conversation at a particular range in a document. - */ - interface CommentThread { - /** - * A unique identifier of the comment thread. - */ - threadId: string; - - /** - * The uri of the document the thread has been created on. - */ - resource: Uri; - - /** - * The range the comment thread is located within the document. The thread icon will be shown - * at the first line of the range. - */ - range: Range; - - /** - * The ordered comments of the thread. - */ - comments: Comment[]; - - /** - * Whether the thread should be collapsed or expanded when opening the document. Defaults to Collapsed. - */ - collapsibleState?: CommentThreadCollapsibleState; - } - - /** - * A comment is displayed within the editor or the Comments Panel, depending on how it is provided. - */ - interface Comment { - /** - * The id of the comment - */ - commentId: string; - - /** - * The text of the comment - */ - body: MarkdownString; - - /** - * The display name of the user who created the comment - */ - userName: string; - - /** - * The icon path for the user who created the comment - */ - userIconPath?: Uri; - - - /** - * @deprecated Use userIconPath instead. The avatar src of the user who created the comment - */ - gravatar?: string; - - /** - * Whether the current user has permission to edit the comment. - * - * This will be treated as false if the comment is provided by a `WorkspaceCommentProvider`, or - * if it is provided by a `DocumentCommentProvider` and no `editComment` method is given. - */ - canEdit?: boolean; - - /** - * Whether the current user has permission to delete the comment. - * - * This will be treated as false if the comment is provided by a `WorkspaceCommentProvider`, or - * if it is provided by a `DocumentCommentProvider` and no `deleteComment` method is given. - */ - canDelete?: boolean; - - /** - * The command to be executed if the comment is selected in the Comments Panel - */ - command?: Command; - - isDraft?: boolean; - } - - export interface CommentThreadChangedEvent { - /** - * Added comment threads. - */ - readonly added: CommentThread[]; - - /** - * Removed comment threads. - */ - readonly removed: CommentThread[]; - - /** - * Changed comment threads. - */ - readonly changed: CommentThread[]; - - /** - * Changed draft mode - */ - readonly inDraftMode: boolean; - } - - interface DocumentCommentProvider { - /** - * Provide the commenting ranges and comment threads for the given document. The comments are displayed within the editor. - */ - provideDocumentComments(document: TextDocument, token: CancellationToken): Promise; - - /** - * Called when a user adds a new comment thread in the document at the specified range, with body text. - */ - createNewCommentThread(document: TextDocument, range: Range, text: string, token: CancellationToken): Promise; - - /** - * Called when a user replies to a new comment thread in the document at the specified range, with body text. - */ - replyToCommentThread(document: TextDocument, range: Range, commentThread: CommentThread, text: string, token: CancellationToken): Promise; - - /** - * Called when a user edits the comment body to the be new text. - */ - editComment?(document: TextDocument, comment: Comment, text: string, token: CancellationToken): Promise; - - /** - * Called when a user deletes the comment. - */ - deleteComment?(document: TextDocument, comment: Comment, token: CancellationToken): Promise; - - startDraft?(document: TextDocument, token: CancellationToken): Promise; - deleteDraft?(document: TextDocument, token: CancellationToken): Promise; - finishDraft?(document: TextDocument, token: CancellationToken): Promise; - - startDraftLabel?: string; - deleteDraftLabel?: string; - finishDraftLabel?: string; - - /** - * Notify of updates to comment threads. - */ - onDidChangeCommentThreads: Event; - } - - interface WorkspaceCommentProvider { - /** - * Provide all comments for the workspace. Comments are shown within the comments panel. Selecting a comment - * from the panel runs the comment's command. - */ - provideWorkspaceComments(token: CancellationToken): Promise; - - /** - * Notify of updates to comment threads. - */ - onDidChangeCommentThreads: Event; - } - - namespace workspace { - export function registerDocumentCommentProvider(provider: DocumentCommentProvider): Disposable; - export function registerWorkspaceCommentProvider(provider: WorkspaceCommentProvider): Disposable; - } - //#endregion - - //#region Terminal - - export interface Terminal { - /** - * Fires when the terminal's pty slave pseudo-device is written to. In other words, this - * provides access to the raw data stream from the process running within the terminal, - * including VT sequences. - */ - onDidWriteData: Event; - } - - /** - * Represents the dimensions of a terminal. - */ - export interface TerminalDimensions { - /** - * The number of columns in the terminal. - */ - readonly columns: number; - - /** - * The number of rows in the terminal. - */ - readonly rows: number; - } - - /** - * Represents a terminal without a process where all interaction and output in the terminal is - * controlled by an extension. This is similar to an output window but has the same VT sequence - * compatility as the regular terminal. - * - * Note that an instance of [Terminal](#Terminal) will be created when a TerminalRenderer is - * created with all its APIs available for use by extensions. When using the Terminal object - * of a TerminalRenderer it acts just like normal only the extension that created the - * TerminalRenderer essentially acts as a process. For example when an - * [Terminal.onDidWriteData](#Terminal.onDidWriteData) listener is registered, that will fire - * when [TerminalRenderer.write](#TerminalRenderer.write) is called. Similarly when - * [Terminal.sendText](#Terminal.sendText) is triggered that will fire the - * [TerminalRenderer.onDidAcceptInput](#TerminalRenderer.onDidAcceptInput) event. - * - * **Example:** Create a terminal renderer, show it and write hello world in red - * ```typescript - * const renderer = window.createTerminalRenderer('foo'); - * renderer.terminal.then(t => t.show()); - * renderer.write('\x1b[31mHello world\x1b[0m'); - * ``` - */ - export interface TerminalRenderer { - /** - * The name of the terminal, this will appear in the terminal selector. - */ - name: string; - - /** - * The dimensions of the terminal, the rows and columns of the terminal can only be set to - * a value smaller than the maximum value, if this is undefined the terminal will auto fit - * to the maximum value [maximumDimensions](TerminalRenderer.maximumDimensions). - * - * **Example:** Override the dimensions of a TerminalRenderer to 20 columns and 10 rows - * ```typescript - * terminalRenderer.dimensions = { - * cols: 20, - * rows: 10 - * }; - * ``` - */ - dimensions: TerminalDimensions | undefined; - - /** - * The maximum dimensions of the terminal, this will be undefined immediately after a - * terminal renderer is created and also until the terminal becomes visible in the UI. - * Listen to [onDidChangeMaximumDimensions](TerminalRenderer.onDidChangeMaximumDimensions) - * to get notified when this value changes. - */ - readonly maximumDimensions: TerminalDimensions | undefined; - - /** - * The corressponding [Terminal](#Terminal) for this TerminalRenderer. - */ - readonly terminal: Terminal; - - /** - * Write text to the terminal. Unlike [Terminal.sendText](#Terminal.sendText) which sends - * text to the underlying _process_, this will write the text to the terminal itself. - * - * **Example:** Write red text to the terminal - * ```typescript - * terminalRenderer.write('\x1b[31mHello world\x1b[0m'); - * ``` - * - * **Example:** Move the cursor to the 10th row and 20th column and write an asterisk - * ```typescript - * terminalRenderer.write('\x1b[10;20H*'); - * ``` - * - * @param text The text to write. - */ - write(text: string): void; - - /** - * An event which fires on keystrokes in the terminal or when an extension calls - * [Terminal.sendText](#Terminal.sendText). Keystrokes are converted into their - * corresponding VT sequence representation. - * - * **Example:** Simulate interaction with the terminal from an outside extension or a - * workbench command such as `workbench.action.terminal.runSelectedText` - * ```typescript - * const terminalRenderer = window.createTerminalRenderer('test'); - * terminalRenderer.onDidAcceptInput(data => { - * cosole.log(data); // 'Hello world' - * }); - * terminalRenderer.terminal.then(t => t.sendText('Hello world')); - * ``` - */ - readonly onDidAcceptInput: Event; - - /** - * An event which fires when the [maximum dimensions](#TerminalRenderer.maimumDimensions) of - * the terminal renderer change. - */ - readonly onDidChangeMaximumDimensions: Event; - } - - export namespace window { - /** - * Create a [TerminalRenderer](#TerminalRenderer). - * - * @param name The name of the terminal renderer, this shows up in the terminal selector. - */ - export function createTerminalRenderer(name: string): TerminalRenderer; - } - - //#endregion - - //#region Joh -> exclusive document filters - - export interface DocumentFilter { - exclusive?: boolean; - } - - //#endregion - - //#region mjbvz,joh: https://github.com/Microsoft/vscode/issues/43768 - export interface FileRenameEvent { - readonly oldUri: Uri; - readonly newUri: Uri; - } - - export interface FileWillRenameEvent { - readonly oldUri: Uri; - readonly newUri: Uri; - waitUntil(thenable: Thenable): void; - } - - export namespace workspace { - export const onWillRenameFile: Event; - export const onDidRenameFile: Event; - } - //#endregion - - //#region Alex - OnEnter enhancement - export interface OnEnterRule { - /** - * This rule will only execute if the text above the this line matches this regular expression. - */ - oneLineAboveText?: RegExp; - } - //#endregion - - //#region Tree View - - export interface TreeView { - - /** - * An optional human-readable message that will be rendered in the view. - */ - message?: string | MarkdownString; - - } - - /** - * Label describing the [Tree item](#TreeItem) - */ - export interface TreeItemLabel { - - /** - * A human-readable string describing the [Tree item](#TreeItem). - */ - label: string; - - /** - * Ranges in the label to highlight. A range is defined as a tuple of two number where the - * first is the inclusive start index and the second the exclusive end index - */ - highlights?: [number, number][]; - - } - - export class TreeItem2 extends TreeItem { - /** - * Label describing this item. When `falsy`, it is derived from [resourceUri](#TreeItem.resourceUri). - */ - label?: string | TreeItemLabel | /* for compilation */ any; - - /** - * @param label Label describing this item - * @param collapsibleState [TreeItemCollapsibleState](#TreeItemCollapsibleState) of the tree item. Default is [TreeItemCollapsibleState.None](#TreeItemCollapsibleState.None) - */ - constructor(label: TreeItemLabel, collapsibleState?: TreeItemCollapsibleState); - } - //#endregion - - //#region SignatureHelpContext active paramters - mjbvz - export interface SignatureHelpContext { - /** - * The currently active [`SignatureHelp`](#SignatureHelp). - * - * Will have the [`SignatureHelp.activeSignature`] field updated based on user arrowing through sig help - */ - readonly activeSignatureHelp?: SignatureHelp; - } - //#endregion - - //#region CodeAction.isPreferred - mjbvz - export interface CodeAction { - /** - * If the action is a preferred action or fix to take. - * - * A quick fix should be marked preferred if it properly addresses the underlying error. - * A refactoring should be marked preferred if it is the most reasonable choice of actions to take. - */ - isPreferred?: boolean; - } - //#endregion - - - //#region Autofix - mjbvz - export namespace CodeActionKind { - /** - * Base kind for an auto fix source action: `source.autoFix`. - */ - export const SourceAutoFix: CodeActionKind; - } - //#endregion -} \ No newline at end of file diff --git a/extensions/css-language-features/package.json b/extensions/css-language-features/package.json index 7ef56c5f423..514196d3608 100644 --- a/extensions/css-language-features/package.json +++ b/extensions/css-language-features/package.json @@ -1,5 +1,4 @@ { - "enableProposedApi": true, "name": "css-language-features", "displayName": "%displayName%", "description": "%description%", @@ -16,6 +15,7 @@ "onCommand:_css.applyCodeAction" ], "main": "./client/out/cssMain", + "enableProposedApi": true, "scripts": { "compile": "gulp compile-extension:css-language-features-client compile-extension:css-language-features-server", "watch": "gulp watch-extension:css-language-features-client watch-extension:css-language-features-server", diff --git a/extensions/html-language-features/client/src/typings/ref.d.ts b/extensions/html-language-features/client/src/typings/ref.d.ts index 9c1a5df18ed..be1d1b0b776 100644 --- a/extensions/html-language-features/client/src/typings/ref.d.ts +++ b/extensions/html-language-features/client/src/typings/ref.d.ts @@ -4,3 +4,4 @@ *--------------------------------------------------------------------------------------------*/ /// +/// diff --git a/extensions/html-language-features/client/src/vscode.proposed.d.ts b/extensions/html-language-features/client/src/vscode.proposed.d.ts deleted file mode 100644 index 44ad9a1d01b..00000000000 --- a/extensions/html-language-features/client/src/vscode.proposed.d.ts +++ /dev/null @@ -1,1142 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -/** - * This is the place for API experiments and proposals. - * These API are NOT stable and subject to change. They are only available in the Insiders - * distribution and CANNOT be used in published extensions. - * - * To test these API in local environment: - * - Use Insiders release of VS Code. - * - Add `"enableProposedApi": true` to your package.json. - * - Copy this file to your project. - */ - -declare module 'vscode' { - - //#region Joh - vscode.open - - export namespace env { - - /** - * Opens an *external* item, e.g. a http(s) or mailto-link, using the - * default application. - * - * *Note* that [`showTextDocument`](#window.showTextDocument) is the right - * way to open a text document inside the editor, not this function. - * - * @param target The uri that should be opened. - * @returns A promise indicating if open was successful. - */ - export function open(target: Uri): Thenable; - } - - //#endregion - - //#region Joh - selection range provider - - export class SelectionRangeKind { - - /** - * Empty Kind. - */ - static readonly Empty: SelectionRangeKind; - - /** - * The statment kind, its value is `statement`, possible extensions can be - * `statement.if` etc - */ - static readonly Statement: SelectionRangeKind; - - /** - * The declaration kind, its value is `declaration`, possible extensions can be - * `declaration.function`, `declaration.class` etc. - */ - static readonly Declaration: SelectionRangeKind; - - readonly value: string; - - private constructor(value: string); - - append(value: string): SelectionRangeKind; - } - - export class SelectionRange { - kind: SelectionRangeKind; - range: Range; - constructor(range: Range, kind: SelectionRangeKind); - } - - export interface SelectionRangeProvider { - /** - * Provide selection ranges starting at a given position. The first range must [contain](#Range.contains) - * position and subsequent ranges must contain the previous range. - */ - provideSelectionRanges(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; - } - - export namespace languages { - export function registerSelectionRangeProvider(selector: DocumentSelector, provider: SelectionRangeProvider): Disposable; - } - - //#endregion - - //#region Joh - read/write in chunks - - export interface FileSystemProvider { - open?(resource: Uri): number | Thenable; - close?(fd: number): void | Thenable; - read?(fd: number, pos: number, data: Uint8Array, offset: number, length: number): number | Thenable; - write?(fd: number, pos: number, data: Uint8Array, offset: number, length: number): number | Thenable; - } - - //#endregion - - //#region Rob: search provider - - /** - * The parameters of a query for text search. - */ - export interface TextSearchQuery { - /** - * The text pattern to search for. - */ - pattern: string; - - /** - * Whether or not `pattern` should match multiple lines of text. - */ - isMultiline?: boolean; - - /** - * Whether or not `pattern` should be interpreted as a regular expression. - */ - isRegExp?: boolean; - - /** - * Whether or not the search should be case-sensitive. - */ - isCaseSensitive?: boolean; - - /** - * Whether or not to search for whole word matches only. - */ - isWordMatch?: boolean; - } - - /** - * A file glob pattern to match file paths against. - * TODO@roblou - merge this with the GlobPattern docs/definition in vscode.d.ts. - * @see [GlobPattern](#GlobPattern) - */ - export type GlobString = string; - - /** - * Options common to file and text search - */ - export interface SearchOptions { - /** - * The root folder to search within. - */ - folder: Uri; - - /** - * Files that match an `includes` glob pattern should be included in the search. - */ - includes: GlobString[]; - - /** - * Files that match an `excludes` glob pattern should be excluded from the search. - */ - excludes: GlobString[]; - - /** - * Whether external files that exclude files, like .gitignore, should be respected. - * See the vscode setting `"search.useIgnoreFiles"`. - */ - useIgnoreFiles: boolean; - - /** - * Whether symlinks should be followed while searching. - * See the vscode setting `"search.followSymlinks"`. - */ - followSymlinks: boolean; - - /** - * Whether global files that exclude files, like .gitignore, should be respected. - * See the vscode setting `"search.useGlobalIgnoreFiles"`. - */ - useGlobalIgnoreFiles: boolean; - - } - - /** - * Options to specify the size of the result text preview. - * These options don't affect the size of the match itself, just the amount of preview text. - */ - export interface TextSearchPreviewOptions { - /** - * The maximum number of lines in the preview. - * Only search providers that support multiline search will ever return more than one line in the match. - */ - matchLines: number; - - /** - * The maximum number of characters included per line. - */ - charsPerLine: number; - } - - /** - * Options that apply to text search. - */ - export interface TextSearchOptions extends SearchOptions { - /** - * The maximum number of results to be returned. - */ - maxResults: number; - - /** - * Options to specify the size of the result text preview. - */ - previewOptions?: TextSearchPreviewOptions; - - /** - * Exclude files larger than `maxFileSize` in bytes. - */ - maxFileSize?: number; - - /** - * Interpret files using this encoding. - * See the vscode setting `"files.encoding"` - */ - encoding?: string; - - /** - * Number of lines of context to include before each match. - */ - beforeContext?: number; - - /** - * Number of lines of context to include after each match. - */ - afterContext?: number; - } - - /** - * Information collected when text search is complete. - */ - export interface TextSearchComplete { - /** - * Whether the search hit the limit on the maximum number of search results. - * `maxResults` on [`TextSearchOptions`](#TextSearchOptions) specifies the max number of results. - * - If exactly that number of matches exist, this should be false. - * - If `maxResults` matches are returned and more exist, this should be true. - * - If search hits an internal limit which is less than `maxResults`, this should be true. - */ - limitHit?: boolean; - } - - /** - * The parameters of a query for file search. - */ - export interface FileSearchQuery { - /** - * The search pattern to match against file paths. - */ - pattern: string; - } - - /** - * Options that apply to file search. - */ - export interface FileSearchOptions extends SearchOptions { - /** - * The maximum number of results to be returned. - */ - maxResults?: number; - - /** - * A CancellationToken that represents the session for this search query. If the provider chooses to, this object can be used as the key for a cache, - * and searches with the same session object can search the same cache. When the token is cancelled, the session is complete and the cache can be cleared. - */ - session?: CancellationToken; - } - - /** - * Options that apply to requesting the file index. - */ - export interface FileIndexOptions extends SearchOptions { } - - /** - * A preview of the text result. - */ - export interface TextSearchMatchPreview { - /** - * The matching lines of text, or a portion of the matching line that contains the match. - */ - text: string; - - /** - * The Range within `text` corresponding to the text of the match. - * The number of matches must match the TextSearchMatch's range property. - */ - matches: Range | Range[]; - } - - /** - * A match from a text search - */ - export interface TextSearchMatch { - /** - * The uri for the matching document. - */ - uri: Uri; - - /** - * The range of the match within the document, or multiple ranges for multiple matches. - */ - ranges: Range | Range[]; - - /** - * A preview of the text match. - */ - preview: TextSearchMatchPreview; - } - - /** - * A line of context surrounding a TextSearchMatch. - */ - export interface TextSearchContext { - /** - * The uri for the matching document. - */ - uri: Uri; - - /** - * One line of text. - * previewOptions.charsPerLine applies to this - */ - text: string; - - /** - * The line number of this line of context. - */ - lineNumber: number; - } - - export type TextSearchResult = TextSearchMatch | TextSearchContext; - - /** - * A FileIndexProvider provides a list of files in the given folder. VS Code will filter that list for searching with quickopen or from other extensions. - * - * A FileIndexProvider is the simpler of two ways to implement file search in VS Code. Use a FileIndexProvider if you are able to provide a listing of all files - * in a folder, and want VS Code to filter them according to the user's search query. - * - * The FileIndexProvider will be invoked once when quickopen is opened, and VS Code will filter the returned list. It will also be invoked when - * `workspace.findFiles` is called. - * - * If a [`FileSearchProvider`](#FileSearchProvider) is registered for the scheme, that provider will be used instead. - */ - export interface FileIndexProvider { - /** - * Provide the set of files in the folder. - * @param options A set of options to consider while searching. - * @param token A cancellation token. - */ - provideFileIndex(options: FileIndexOptions, token: CancellationToken): ProviderResult; - } - - /** - * A FileSearchProvider provides search results for files in the given folder that match a query string. It can be invoked by quickopen or other extensions. - * - * A FileSearchProvider is the more powerful of two ways to implement file search in VS Code. Use a FileSearchProvider if you wish to search within a folder for - * all files that match the user's query. - * - * The FileSearchProvider will be invoked on every keypress in quickopen. When `workspace.findFiles` is called, it will be invoked with an empty query string, - * and in that case, every file in the folder should be returned. - * - * @see [FileIndexProvider](#FileIndexProvider) - */ - export interface FileSearchProvider { - /** - * Provide the set of files that match a certain file path pattern. - * @param query The parameters for this query. - * @param options A set of options to consider while searching files. - * @param progress A progress callback that must be invoked for all results. - * @param token A cancellation token. - */ - provideFileSearchResults(query: FileSearchQuery, options: FileSearchOptions, token: CancellationToken): ProviderResult; - } - - /** - * A TextSearchProvider provides search results for text results inside files in the workspace. - */ - export interface TextSearchProvider { - /** - * Provide results that match the given text pattern. - * @param query The parameters for this query. - * @param options A set of options to consider while searching. - * @param progress A progress callback that must be invoked for all results. - * @param token A cancellation token. - */ - provideTextSearchResults(query: TextSearchQuery, options: TextSearchOptions, progress: Progress, token: CancellationToken): ProviderResult; - } - - /** - * Options that can be set on a findTextInFiles search. - */ - export interface FindTextInFilesOptions { - /** - * A [glob pattern](#GlobPattern) that defines the files to search for. The glob pattern - * will be matched against the file paths of files relative to their workspace. Use a [relative pattern](#RelativePattern) - * to restrict the search results to a [workspace folder](#WorkspaceFolder). - */ - include?: GlobPattern; - - /** - * A [glob pattern](#GlobPattern) that defines files and folders to exclude. The glob pattern - * will be matched against the file paths of resulting matches relative to their workspace. When `undefined` only default excludes will - * apply, when `null` no excludes will apply. - */ - exclude?: GlobPattern | null; - - /** - * The maximum number of results to search for - */ - maxResults?: number; - - /** - * Whether external files that exclude files, like .gitignore, should be respected. - * See the vscode setting `"search.useIgnoreFiles"`. - */ - useIgnoreFiles?: boolean; - - /** - * Whether global files that exclude files, like .gitignore, should be respected. - * See the vscode setting `"search.useGlobalIgnoreFiles"`. - */ - useGlobalIgnoreFiles?: boolean; - - /** - * Whether symlinks should be followed while searching. - * See the vscode setting `"search.followSymlinks"`. - */ - followSymlinks?: boolean; - - /** - * Interpret files using this encoding. - * See the vscode setting `"files.encoding"` - */ - encoding?: string; - - /** - * Options to specify the size of the result text preview. - */ - previewOptions?: TextSearchPreviewOptions; - - /** - * Number of lines of context to include before each match. - */ - beforeContext?: number; - - /** - * Number of lines of context to include after each match. - */ - afterContext?: number; - } - - export namespace workspace { - /** - * DEPRECATED - */ - export function registerSearchProvider(): Disposable; - - /** - * Register a file index provider. - * - * Only one provider can be registered per scheme. - * - * @param scheme The provider will be invoked for workspace folders that have this file scheme. - * @param provider The provider. - * @return A [disposable](#Disposable) that unregisters this provider when being disposed. - */ - export function registerFileIndexProvider(scheme: string, provider: FileIndexProvider): Disposable; - - /** - * Register a search provider. - * - * Only one provider can be registered per scheme. - * - * @param scheme The provider will be invoked for workspace folders that have this file scheme. - * @param provider The provider. - * @return A [disposable](#Disposable) that unregisters this provider when being disposed. - */ - export function registerFileSearchProvider(scheme: string, provider: FileSearchProvider): Disposable; - - /** - * Register a text search provider. - * - * Only one provider can be registered per scheme. - * - * @param scheme The provider will be invoked for workspace folders that have this file scheme. - * @param provider The provider. - * @return A [disposable](#Disposable) that unregisters this provider when being disposed. - */ - export function registerTextSearchProvider(scheme: string, provider: TextSearchProvider): Disposable; - - /** - * Search text in files across all [workspace folders](#workspace.workspaceFolders) in the workspace. - * @param query The query parameters for the search - the search string, whether it's case-sensitive, or a regex, or matches whole words. - * @param callback A callback, called for each result - * @param token A token that can be used to signal cancellation to the underlying search engine. - * @return A thenable that resolves when the search is complete. - */ - export function findTextInFiles(query: TextSearchQuery, callback: (result: TextSearchResult) => void, token?: CancellationToken): Thenable; - - /** - * Search text in files across all [workspace folders](#workspace.workspaceFolders) in the workspace. - * @param query The query parameters for the search - the search string, whether it's case-sensitive, or a regex, or matches whole words. - * @param options An optional set of query options. Include and exclude patterns, maxResults, etc. - * @param callback A callback, called for each result - * @param token A token that can be used to signal cancellation to the underlying search engine. - * @return A thenable that resolves when the search is complete. - */ - export function findTextInFiles(query: TextSearchQuery, options: FindTextInFilesOptions, callback: (result: TextSearchResult) => void, token?: CancellationToken): Thenable; - } - - //#endregion - - //#region Joao: diff command - - /** - * The contiguous set of modified lines in a diff. - */ - export interface LineChange { - readonly originalStartLineNumber: number; - readonly originalEndLineNumber: number; - readonly modifiedStartLineNumber: number; - readonly modifiedEndLineNumber: number; - } - - export namespace commands { - - /** - * Registers a diff information command that can be invoked via a keyboard shortcut, - * a menu item, an action, or directly. - * - * Diff information commands are different from ordinary [commands](#commands.registerCommand) as - * they only execute when there is an active diff editor when the command is called, and the diff - * information has been computed. Also, the command handler of an editor command has access to - * the diff information. - * - * @param command A unique identifier for the command. - * @param callback A command handler function with access to the [diff information](#LineChange). - * @param thisArg The `this` context used when invoking the handler function. - * @return Disposable which unregisters this command on disposal. - */ - export function registerDiffInformationCommand(command: string, callback: (diff: LineChange[], ...args: any[]) => any, thisArg?: any): Disposable; - } - - //#endregion - - //#region Joh: decorations - - //todo@joh -> make class - export interface DecorationData { - letter?: string; - title?: string; - color?: ThemeColor; - priority?: number; - bubble?: boolean; - source?: string; // hacky... we should remove it and use equality under the hood - } - - export interface SourceControlResourceDecorations { - source?: string; - letter?: string; - color?: ThemeColor; - } - - export interface DecorationProvider { - onDidChangeDecorations: Event; - provideDecoration(uri: Uri, token: CancellationToken): ProviderResult; - } - - export namespace window { - export function registerDecorationProvider(provider: DecorationProvider): Disposable; - } - - //#endregion - - //#region André: debug - - // deprecated - - export interface DebugConfigurationProvider { - /** - * Deprecated, use DebugAdapterDescriptorFactory.provideDebugAdapter instead. - * @deprecated Use DebugAdapterDescriptorFactory.createDebugAdapterDescriptor instead - */ - debugAdapterExecutable?(folder: WorkspaceFolder | undefined, token?: CancellationToken): ProviderResult; - } - - //#endregion - - //#region Rob, Matt: logging - - /** - * The severity level of a log message - */ - export enum LogLevel { - Trace = 1, - Debug = 2, - Info = 3, - Warning = 4, - Error = 5, - Critical = 6, - Off = 7 - } - - export namespace env { - /** - * Current logging level. - */ - export const logLevel: LogLevel; - - /** - * An [event](#Event) that fires when the log level has changed. - */ - export const onDidChangeLogLevel: Event; - } - - //#endregion - - //#region Joao: SCM validation - - /** - * Represents the validation type of the Source Control input. - */ - export enum SourceControlInputBoxValidationType { - - /** - * Something not allowed by the rules of a language or other means. - */ - Error = 0, - - /** - * Something suspicious but allowed. - */ - Warning = 1, - - /** - * Something to inform about but not a problem. - */ - Information = 2 - } - - export interface SourceControlInputBoxValidation { - - /** - * The validation message to display. - */ - readonly message: string; - - /** - * The validation type. - */ - readonly type: SourceControlInputBoxValidationType; - } - - /** - * Represents the input box in the Source Control viewlet. - */ - export interface SourceControlInputBox { - - /** - * A validation function for the input box. It's possible to change - * the validation provider simply by setting this property to a different function. - */ - validateInput?(value: string, cursorPosition: number): ProviderResult; - } - - //#endregion - - //#region Joao: SCM selected provider - - export interface SourceControl { - - /** - * Whether the source control is selected. - */ - readonly selected: boolean; - - /** - * An event signaling when the selection state changes. - */ - readonly onDidChangeSelection: Event; - } - - //#endregion - - //#region Joao: SCM Input Box - - /** - * Represents the input box in the Source Control viewlet. - */ - export interface SourceControlInputBox { - - /** - * Controls whether the input box is visible (default is `true`). - */ - visible: boolean; - } - - //#endregion - - //#region Comments - /** - * Comments provider related APIs are still in early stages, they may be changed significantly during our API experiments. - */ - - interface CommentInfo { - /** - * All of the comment threads associated with the document. - */ - threads: CommentThread[]; - - /** - * The ranges of the document which support commenting. - */ - commentingRanges?: Range[]; - - /** - * If it's in draft mode or not - */ - inDraftMode?: boolean; - } - - export enum CommentThreadCollapsibleState { - /** - * Determines an item is collapsed - */ - Collapsed = 0, - /** - * Determines an item is expanded - */ - Expanded = 1 - } - - /** - * A collection of comments representing a conversation at a particular range in a document. - */ - interface CommentThread { - /** - * A unique identifier of the comment thread. - */ - threadId: string; - - /** - * The uri of the document the thread has been created on. - */ - resource: Uri; - - /** - * The range the comment thread is located within the document. The thread icon will be shown - * at the first line of the range. - */ - range: Range; - - /** - * The ordered comments of the thread. - */ - comments: Comment[]; - - /** - * Whether the thread should be collapsed or expanded when opening the document. Defaults to Collapsed. - */ - collapsibleState?: CommentThreadCollapsibleState; - } - - /** - * A comment is displayed within the editor or the Comments Panel, depending on how it is provided. - */ - interface Comment { - /** - * The id of the comment - */ - commentId: string; - - /** - * The text of the comment - */ - body: MarkdownString; - - /** - * The display name of the user who created the comment - */ - userName: string; - - /** - * The icon path for the user who created the comment - */ - userIconPath?: Uri; - - - /** - * @deprecated Use userIconPath instead. The avatar src of the user who created the comment - */ - gravatar?: string; - - /** - * Whether the current user has permission to edit the comment. - * - * This will be treated as false if the comment is provided by a `WorkspaceCommentProvider`, or - * if it is provided by a `DocumentCommentProvider` and no `editComment` method is given. - */ - canEdit?: boolean; - - /** - * Whether the current user has permission to delete the comment. - * - * This will be treated as false if the comment is provided by a `WorkspaceCommentProvider`, or - * if it is provided by a `DocumentCommentProvider` and no `deleteComment` method is given. - */ - canDelete?: boolean; - - /** - * The command to be executed if the comment is selected in the Comments Panel - */ - command?: Command; - - isDraft?: boolean; - } - - export interface CommentThreadChangedEvent { - /** - * Added comment threads. - */ - readonly added: CommentThread[]; - - /** - * Removed comment threads. - */ - readonly removed: CommentThread[]; - - /** - * Changed comment threads. - */ - readonly changed: CommentThread[]; - - /** - * Changed draft mode - */ - readonly inDraftMode: boolean; - } - - interface DocumentCommentProvider { - /** - * Provide the commenting ranges and comment threads for the given document. The comments are displayed within the editor. - */ - provideDocumentComments(document: TextDocument, token: CancellationToken): Promise; - - /** - * Called when a user adds a new comment thread in the document at the specified range, with body text. - */ - createNewCommentThread(document: TextDocument, range: Range, text: string, token: CancellationToken): Promise; - - /** - * Called when a user replies to a new comment thread in the document at the specified range, with body text. - */ - replyToCommentThread(document: TextDocument, range: Range, commentThread: CommentThread, text: string, token: CancellationToken): Promise; - - /** - * Called when a user edits the comment body to the be new text. - */ - editComment?(document: TextDocument, comment: Comment, text: string, token: CancellationToken): Promise; - - /** - * Called when a user deletes the comment. - */ - deleteComment?(document: TextDocument, comment: Comment, token: CancellationToken): Promise; - - startDraft?(document: TextDocument, token: CancellationToken): Promise; - deleteDraft?(document: TextDocument, token: CancellationToken): Promise; - finishDraft?(document: TextDocument, token: CancellationToken): Promise; - - startDraftLabel?: string; - deleteDraftLabel?: string; - finishDraftLabel?: string; - - /** - * Notify of updates to comment threads. - */ - onDidChangeCommentThreads: Event; - } - - interface WorkspaceCommentProvider { - /** - * Provide all comments for the workspace. Comments are shown within the comments panel. Selecting a comment - * from the panel runs the comment's command. - */ - provideWorkspaceComments(token: CancellationToken): Promise; - - /** - * Notify of updates to comment threads. - */ - onDidChangeCommentThreads: Event; - } - - namespace workspace { - export function registerDocumentCommentProvider(provider: DocumentCommentProvider): Disposable; - export function registerWorkspaceCommentProvider(provider: WorkspaceCommentProvider): Disposable; - } - //#endregion - - //#region Terminal - - export interface Terminal { - /** - * Fires when the terminal's pty slave pseudo-device is written to. In other words, this - * provides access to the raw data stream from the process running within the terminal, - * including VT sequences. - */ - onDidWriteData: Event; - } - - /** - * Represents the dimensions of a terminal. - */ - export interface TerminalDimensions { - /** - * The number of columns in the terminal. - */ - readonly columns: number; - - /** - * The number of rows in the terminal. - */ - readonly rows: number; - } - - /** - * Represents a terminal without a process where all interaction and output in the terminal is - * controlled by an extension. This is similar to an output window but has the same VT sequence - * compatility as the regular terminal. - * - * Note that an instance of [Terminal](#Terminal) will be created when a TerminalRenderer is - * created with all its APIs available for use by extensions. When using the Terminal object - * of a TerminalRenderer it acts just like normal only the extension that created the - * TerminalRenderer essentially acts as a process. For example when an - * [Terminal.onDidWriteData](#Terminal.onDidWriteData) listener is registered, that will fire - * when [TerminalRenderer.write](#TerminalRenderer.write) is called. Similarly when - * [Terminal.sendText](#Terminal.sendText) is triggered that will fire the - * [TerminalRenderer.onDidAcceptInput](#TerminalRenderer.onDidAcceptInput) event. - * - * **Example:** Create a terminal renderer, show it and write hello world in red - * ```typescript - * const renderer = window.createTerminalRenderer('foo'); - * renderer.terminal.then(t => t.show()); - * renderer.write('\x1b[31mHello world\x1b[0m'); - * ``` - */ - export interface TerminalRenderer { - /** - * The name of the terminal, this will appear in the terminal selector. - */ - name: string; - - /** - * The dimensions of the terminal, the rows and columns of the terminal can only be set to - * a value smaller than the maximum value, if this is undefined the terminal will auto fit - * to the maximum value [maximumDimensions](TerminalRenderer.maximumDimensions). - * - * **Example:** Override the dimensions of a TerminalRenderer to 20 columns and 10 rows - * ```typescript - * terminalRenderer.dimensions = { - * cols: 20, - * rows: 10 - * }; - * ``` - */ - dimensions: TerminalDimensions | undefined; - - /** - * The maximum dimensions of the terminal, this will be undefined immediately after a - * terminal renderer is created and also until the terminal becomes visible in the UI. - * Listen to [onDidChangeMaximumDimensions](TerminalRenderer.onDidChangeMaximumDimensions) - * to get notified when this value changes. - */ - readonly maximumDimensions: TerminalDimensions | undefined; - - /** - * The corressponding [Terminal](#Terminal) for this TerminalRenderer. - */ - readonly terminal: Terminal; - - /** - * Write text to the terminal. Unlike [Terminal.sendText](#Terminal.sendText) which sends - * text to the underlying _process_, this will write the text to the terminal itself. - * - * **Example:** Write red text to the terminal - * ```typescript - * terminalRenderer.write('\x1b[31mHello world\x1b[0m'); - * ``` - * - * **Example:** Move the cursor to the 10th row and 20th column and write an asterisk - * ```typescript - * terminalRenderer.write('\x1b[10;20H*'); - * ``` - * - * @param text The text to write. - */ - write(text: string): void; - - /** - * An event which fires on keystrokes in the terminal or when an extension calls - * [Terminal.sendText](#Terminal.sendText). Keystrokes are converted into their - * corresponding VT sequence representation. - * - * **Example:** Simulate interaction with the terminal from an outside extension or a - * workbench command such as `workbench.action.terminal.runSelectedText` - * ```typescript - * const terminalRenderer = window.createTerminalRenderer('test'); - * terminalRenderer.onDidAcceptInput(data => { - * cosole.log(data); // 'Hello world' - * }); - * terminalRenderer.terminal.then(t => t.sendText('Hello world')); - * ``` - */ - readonly onDidAcceptInput: Event; - - /** - * An event which fires when the [maximum dimensions](#TerminalRenderer.maimumDimensions) of - * the terminal renderer change. - */ - readonly onDidChangeMaximumDimensions: Event; - } - - export namespace window { - /** - * Create a [TerminalRenderer](#TerminalRenderer). - * - * @param name The name of the terminal renderer, this shows up in the terminal selector. - */ - export function createTerminalRenderer(name: string): TerminalRenderer; - } - - //#endregion - - //#region Joh -> exclusive document filters - - export interface DocumentFilter { - exclusive?: boolean; - } - - //#endregion - - //#region mjbvz,joh: https://github.com/Microsoft/vscode/issues/43768 - export interface FileRenameEvent { - readonly oldUri: Uri; - readonly newUri: Uri; - } - - export interface FileWillRenameEvent { - readonly oldUri: Uri; - readonly newUri: Uri; - waitUntil(thenable: Thenable): void; - } - - export namespace workspace { - export const onWillRenameFile: Event; - export const onDidRenameFile: Event; - } - //#endregion - - //#region Alex - OnEnter enhancement - export interface OnEnterRule { - /** - * This rule will only execute if the text above the this line matches this regular expression. - */ - oneLineAboveText?: RegExp; - } - //#endregion - - //#region Tree View - - export interface TreeView { - - /** - * An optional human-readable message that will be rendered in the view. - */ - message?: string | MarkdownString; - - } - - /** - * Label describing the [Tree item](#TreeItem) - */ - export interface TreeItemLabel { - - /** - * A human-readable string describing the [Tree item](#TreeItem). - */ - label: string; - - /** - * Ranges in the label to highlight. A range is defined as a tuple of two number where the - * first is the inclusive start index and the second the exclusive end index - */ - highlights?: [number, number][]; - - } - - export class TreeItem2 extends TreeItem { - /** - * Label describing this item. When `falsy`, it is derived from [resourceUri](#TreeItem.resourceUri). - */ - label?: string | TreeItemLabel | /* for compilation */ any; - - /** - * @param label Label describing this item - * @param collapsibleState [TreeItemCollapsibleState](#TreeItemCollapsibleState) of the tree item. Default is [TreeItemCollapsibleState.None](#TreeItemCollapsibleState.None) - */ - constructor(label: TreeItemLabel, collapsibleState?: TreeItemCollapsibleState); - } - //#endregion - - //#region SignatureHelpContext active paramters - mjbvz - export interface SignatureHelpContext { - /** - * The currently active [`SignatureHelp`](#SignatureHelp). - * - * Will have the [`SignatureHelp.activeSignature`] field updated based on user arrowing through sig help - */ - readonly activeSignatureHelp?: SignatureHelp; - } - //#endregion - - //#region CodeAction.isPreferred - mjbvz - export interface CodeAction { - /** - * If the action is a preferred action or fix to take. - * - * A quick fix should be marked preferred if it properly addresses the underlying error. - * A refactoring should be marked preferred if it is the most reasonable choice of actions to take. - */ - isPreferred?: boolean; - } - //#endregion - - - //#region Autofix - mjbvz - export namespace CodeActionKind { - /** - * Base kind for an auto fix source action: `source.autoFix`. - */ - export const SourceAutoFix: CodeActionKind; - } - //#endregion -} \ No newline at end of file diff --git a/extensions/json-language-features/client/src/jsonMain.ts b/extensions/json-language-features/client/src/jsonMain.ts index 79d30295ff1..b1bc1f4097c 100644 --- a/extensions/json-language-features/client/src/jsonMain.ts +++ b/extensions/json-language-features/client/src/jsonMain.ts @@ -8,7 +8,7 @@ import * as fs from 'fs'; import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); -import { workspace, window, languages, commands, ExtensionContext, extensions, Uri, LanguageConfiguration, Diagnostic, StatusBarAlignment, TextEditor } from 'vscode'; +import { workspace, window, languages, commands, ExtensionContext, extensions, Uri, LanguageConfiguration, Diagnostic, StatusBarAlignment, TextEditor, TextDocument, Position, SelectionRange, Range, SelectionRangeKind } from 'vscode'; import { LanguageClient, LanguageClientOptions, RequestType, ServerOptions, TransportKind, NotificationType, DidChangeConfigurationNotification, HandleDiagnosticsSignature } from 'vscode-languageclient'; import TelemetryReporter from 'vscode-extension-telemetry'; @@ -193,6 +193,26 @@ export function activate(context: ExtensionContext) { toDispose.push(commands.registerCommand('_json.retryResolveSchema', handleRetryResolveSchemaCommand)); client.sendNotification(SchemaAssociationNotification.type, getSchemaAssociation(context)); + + }); + + const selectionRangeProvider = { + async provideSelectionRanges(document: TextDocument, position: Position): Promise { + const textDocument = client.code2ProtocolConverter.asTextDocumentIdentifier(document); + const rawRanges = await client.sendRequest('$/textDocument/selectionRange', { textDocument, position }); + if (Array.isArray(rawRanges)) { + return rawRanges.map(r => { + return { + range: client.protocol2CodeConverter.asRange(r), + kind: SelectionRangeKind.Declaration + }; + }); + } + return []; + } + }; + documentSelector.forEach(selector => { + languages.registerSelectionRangeProvider(selector, selectionRangeProvider); }); let languageConfiguration: LanguageConfiguration = { diff --git a/extensions/json-language-features/client/src/typings/ref.d.ts b/extensions/json-language-features/client/src/typings/ref.d.ts index 9c1a5df18ed..be1d1b0b776 100644 --- a/extensions/json-language-features/client/src/typings/ref.d.ts +++ b/extensions/json-language-features/client/src/typings/ref.d.ts @@ -4,3 +4,4 @@ *--------------------------------------------------------------------------------------------*/ /// +/// diff --git a/extensions/json-language-features/package.json b/extensions/json-language-features/package.json index 1eb1e6e2f88..6e982dac417 100644 --- a/extensions/json-language-features/package.json +++ b/extensions/json-language-features/package.json @@ -14,6 +14,7 @@ "onLanguage:jsonc" ], "main": "./client/out/jsonMain", + "enableProposedApi": true, "scripts": { "compile": "gulp compile-extension:json-language-features-client compile-extension:json-language-features-server", "watch": "gulp watch-extension:json-language-features-client watch-extension:json-language-features-server", @@ -107,4 +108,4 @@ "devDependencies": { "@types/node": "^8.10.25" } -} +} \ No newline at end of file diff --git a/extensions/json-language-features/server/package.json b/extensions/json-language-features/server/package.json index db0182fa088..0868d9aa049 100644 --- a/extensions/json-language-features/server/package.json +++ b/extensions/json-language-features/server/package.json @@ -14,7 +14,7 @@ "dependencies": { "jsonc-parser": "^2.0.2", "request-light": "^0.2.4", - "vscode-json-languageservice": "^3.2.1", + "vscode-json-languageservice": "^3.3.0-next.0", "vscode-languageserver": "^5.1.0", "vscode-nls": "^4.0.0", "vscode-uri": "^1.0.6" diff --git a/extensions/json-language-features/server/src/jsonServerMain.ts b/extensions/json-language-features/server/src/jsonServerMain.ts index e3b2dcb5f8f..5c3539454e4 100644 --- a/extensions/json-language-features/server/src/jsonServerMain.ts +++ b/extensions/json-language-features/server/src/jsonServerMain.ts @@ -427,5 +427,16 @@ connection.onFoldingRanges((params, token) => { }, null, `Error while computing folding ranges for ${params.textDocument.uri}`, token); }); +connection.onRequest('$/textDocument/selectionRange', async (params, token) => { + return runSafe(() => { + const document = documents.get(params.textDocument.uri); + if (document) { + const jsonDocument = getJSONDocument(document); + return languageService.getSelectionRanges(document, params.position, jsonDocument); + } + return []; + }, [], `Error while computing selection ranges for ${params.textDocument.uri}`, token); +}); + // Listen on the connection connection.listen(); diff --git a/extensions/json-language-features/server/yarn.lock b/extensions/json-language-features/server/yarn.lock index 012e346360a..1c933ebfb3c 100644 --- a/extensions/json-language-features/server/yarn.lock +++ b/extensions/json-language-features/server/yarn.lock @@ -73,10 +73,10 @@ request-light@^0.2.4: https-proxy-agent "^2.2.1" vscode-nls "^4.0.0" -vscode-json-languageservice@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.2.1.tgz#991d51128ebd81c5525d0578cabfa5b03e3cba2a" - integrity sha512-ee9MJ70/xR55ywvm0bZsDLhA800HCRE27AYgMNTU14RSg20Y+ngHdQnUt6OmiTXrQDI/7sne6QUOtHIN0hPQYA== +vscode-json-languageservice@^3.3.0-next.0: + version "3.3.0-next.0" + resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.3.0-next.0.tgz#c17db95d0eacc24f80d3b3f120ab5e03943769a0" + integrity sha512-YZXL3yHzbr0/Ar5dGdeM/f5Y0l41z/Y4QSQTdL3Hl3ScuY76IPcDEnf7iuk9yx+QoPfEHFCBDv5Rg6XVcMl8Tg== dependencies: jsonc-parser "^2.0.2" vscode-languageserver-types "^3.13.0" From 645d35508bd7fd6972d0b0defed74f2464055c6f Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 28 Jan 2019 16:16:19 +0100 Subject: [PATCH 220/274] fix tree model getListIndex --- src/vs/base/browser/ui/tree/indexTreeModel.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/vs/base/browser/ui/tree/indexTreeModel.ts b/src/vs/base/browser/ui/tree/indexTreeModel.ts index c00e38b55ee..3df971f5b55 100644 --- a/src/vs/base/browser/ui/tree/indexTreeModel.ts +++ b/src/vs/base/browser/ui/tree/indexTreeModel.ts @@ -144,7 +144,8 @@ export class IndexTreeModel, TFilterData = voi } getListIndex(location: number[]): number { - return this.getTreeNodeWithListIndex(location).listIndex; + const { listIndex, visible } = this.getTreeNodeWithListIndex(location); + return visible ? listIndex : -1; } getListRenderCount(location: number[]): number { @@ -423,12 +424,12 @@ export class IndexTreeModel, TFilterData = voi } // expensive - private getTreeNodeWithListIndex(location: number[]): { node: IMutableTreeNode, listIndex: number, revealed: boolean } { + private getTreeNodeWithListIndex(location: number[]): { node: IMutableTreeNode, listIndex: number, revealed: boolean, visible: boolean } { if (location.length === 0) { - return { node: this.root, listIndex: -1, revealed: true }; + return { node: this.root, listIndex: -1, revealed: true, visible: false }; } - const { parentNode, listIndex, revealed } = this.getParentNodeWithListIndex(location); + const { parentNode, listIndex, revealed, visible } = this.getParentNodeWithListIndex(location); const index = location[location.length - 1]; if (index < 0 || index > parentNode.children.length) { @@ -437,10 +438,10 @@ export class IndexTreeModel, TFilterData = voi const node = parentNode.children[index]; - return { node, listIndex, revealed }; + return { node, listIndex, revealed, visible: visible && node.visible }; } - private getParentNodeWithListIndex(location: number[], node: IMutableTreeNode = this.root, listIndex: number = 0, revealed = true): { parentNode: IMutableTreeNode; listIndex: number; revealed: boolean; } { + private getParentNodeWithListIndex(location: number[], node: IMutableTreeNode = this.root, listIndex: number = 0, revealed = true, visible = true): { parentNode: IMutableTreeNode; listIndex: number; revealed: boolean; visible: boolean; } { const [index, ...rest] = location; if (index < 0 || index > node.children.length) { @@ -453,12 +454,13 @@ export class IndexTreeModel, TFilterData = voi } revealed = revealed && !node.collapsed; + visible = visible && node.visible; if (rest.length === 0) { - return { parentNode: node, listIndex, revealed }; + return { parentNode: node, listIndex, revealed, visible }; } - return this.getParentNodeWithListIndex(rest, node.children[index], listIndex + 1, revealed); + return this.getParentNodeWithListIndex(rest, node.children[index], listIndex + 1, revealed, visible); } getNode(location: number[] = []): ITreeNode { From 71931e19487874201a6d70c0dd8fe22d39a6e9aa Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 28 Jan 2019 16:18:43 +0100 Subject: [PATCH 221/274] fixes #67087 --- src/vs/base/browser/ui/tree/abstractTree.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 145e9366547..921d5101b44 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -21,6 +21,7 @@ import { fuzzyScore, FuzzyScore } from 'vs/base/common/filters'; import { getVisibleState, isFilterResult } from 'vs/base/browser/ui/tree/indexTreeModel'; import { localize } from 'vs/nls'; import { disposableTimeout } from 'vs/base/common/async'; +import { isMacintosh } from 'vs/base/common/platform'; function asTreeDragAndDropData(data: IDragAndDropData): IDragAndDropData { if (data instanceof ElementsDragAndDropData) { @@ -432,14 +433,14 @@ class TypeFilterController implements IDisposable { const onKeyDown = Event.chain(domEvent(this.view.getHTMLElement(), 'keydown')) .filter(e => !isInputElement(e.target as HTMLElement) || e.target === this.filterOnTypeDomNode) .map(e => new StandardKeyboardEvent(e)) - .filter(e => isPrintableCharEvent(e) || (this._pattern.length > 0 && (e.keyCode === KeyCode.Escape || e.keyCode === KeyCode.Backspace) && !e.altKey && !e.ctrlKey && !e.metaKey)) + .filter(e => isPrintableCharEvent(e) || (this._pattern.length > 0 && ((e.keyCode === KeyCode.Escape || e.keyCode === KeyCode.Backspace) && !e.altKey && !e.ctrlKey && !e.metaKey) || (e.keyCode === KeyCode.Backspace && (isMacintosh ? e.altKey : e.ctrlKey)))) .forEach(e => { e.stopPropagation(); e.preventDefault(); }) .event; const onClear = domEvent(this.clearDomNode, 'click'); const onInput = Event.chain(Event.any(onKeyDown, onClear)) .reduce((previous: string, e) => { - if (e instanceof MouseEvent || e.keyCode === KeyCode.Escape) { + if (e instanceof MouseEvent || e.keyCode === KeyCode.Escape || (e.keyCode === KeyCode.Backspace && (isMacintosh ? e.altKey : e.ctrlKey))) { return ''; } From be1ca3cc4291292a9be05be6e0f94320d9682401 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 28 Jan 2019 07:43:38 -0800 Subject: [PATCH 222/274] Move env sanitizing to base/node/processes Part of #64897 --- src/vs/base/node/processes.ts | 20 +++++++++++++++ .../test/node/processes/processes.test.ts | 25 +++++++++++++++++++ .../api/node/extHostTerminalService.ts | 3 ++- .../terminalProcessManager.ts | 3 ++- .../terminal/node/terminalEnvironment.ts | 18 ------------- .../test/node/terminalEnvironment.test.ts | 24 ------------------ 6 files changed, 49 insertions(+), 44 deletions(-) diff --git a/src/vs/base/node/processes.ts b/src/vs/base/node/processes.ts index 36b95e0f378..15de8c293f6 100644 --- a/src/vs/base/node/processes.ts +++ b/src/vs/base/node/processes.ts @@ -72,6 +72,26 @@ export function getWindowsShell(): string { return process.env['comspec'] || 'cmd.exe'; } +/** + * Sanitizes a VS Code process environment by removing all Electron/VS Code-related values. + */ +export function sanitizeProcessEnvironment(env: Platform.IProcessEnvironment): void { + const keysToRemove = [ + /^ELECTRON_.+$/, + /^GOOGLE_API_KEY$/, + /^VSCODE_.+$/ + ]; + const envKeys = Object.keys(env); + envKeys.forEach(envKey => { + for (let i = 0; i < keysToRemove.length; i++) { + if (envKey.search(keysToRemove[i]) !== -1) { + delete env[envKey]; + break; + } + } + }); +} + export abstract class AbstractProcess { private cmd: string; private args: string[]; diff --git a/src/vs/base/test/node/processes/processes.test.ts b/src/vs/base/test/node/processes/processes.test.ts index 76719506d6e..10199cf5bf3 100644 --- a/src/vs/base/test/node/processes/processes.test.ts +++ b/src/vs/base/test/node/processes/processes.test.ts @@ -84,4 +84,29 @@ suite('Processes', () => { } }); }); + + + test('sanitizeProcessEnvironment', () => { + let env = { + FOO: 'bar', + ELECTRON_ENABLE_STACK_DUMPING: 'x', + ELECTRON_ENABLE_LOGGING: 'x', + ELECTRON_NO_ASAR: 'x', + ELECTRON_NO_ATTACH_CONSOLE: 'x', + ELECTRON_RUN_AS_NODE: 'x', + GOOGLE_API_KEY: 'x', + VSCODE_CLI: 'x', + VSCODE_DEV: 'x', + VSCODE_IPC_HOOK: 'x', + VSCODE_LOGS: 'x', + VSCODE_NLS_CONFIG: 'x', + VSCODE_PORTABLE: 'x', + VSCODE_PID: 'x', + VSCODE_NODE_CACHED_DATA_DIR: 'x', + VSCODE_NEW_VAR: 'x' + }; + processes.sanitizeProcessEnvironment(env); + assert.equal(env['FOO'], 'bar'); + assert.equal(Object.keys(env).length, 1); + }); }); diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts index fd343a8de75..884bd047a33 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -18,6 +18,7 @@ import { generateRandomPipeName } from 'vs/base/parts/ipc/node/ipc.net'; import * as http from 'http'; import * as fs from 'fs'; import { ExtHostCommands } from 'vs/workbench/api/node/extHostCommands'; +import { sanitizeProcessEnvironment } from 'vs/base/node/processes'; const RENDERER_NO_PROCESS_ID = -1; @@ -417,7 +418,7 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { // Sanitize the environment, removing any undesirable VS Code and Electron environment // variables - terminalEnvironment.sanitizeEnvironment(env); + sanitizeProcessEnvironment(env); // Continue env initialization, merging in the env from the launch // config and adding keys that are needed to create the process diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts index 6eb9ec9906c..a49259ddcd4 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts @@ -19,6 +19,7 @@ import { IConfigurationResolverService } from 'vs/workbench/services/configurati import { IWindowService } from 'vs/platform/windows/common/windows'; import { Schemas } from 'vs/base/common/network'; import { REMOTE_HOST_SCHEME, getRemoteAuthority } from 'vs/platform/remote/common/remoteHosts'; +import { sanitizeProcessEnvironment } from 'vs/base/node/processes'; /** The amount of time to consider terminal errors to be related to the launch */ const LAUNCHING_DURATION = 500; @@ -133,7 +134,7 @@ export class TerminalProcessManager implements ITerminalProcessManager { // Sanitize the environment, removing any undesirable VS Code and Electron environment // variables - terminalEnvironment.sanitizeEnvironment(env); + sanitizeProcessEnvironment(env); // Adding other env keys necessary to create the process terminalEnvironment.addTerminalEnvironmentKeys(env, platform.locale, this._configHelper.config.setLocaleVariables); diff --git a/src/vs/workbench/parts/terminal/node/terminalEnvironment.ts b/src/vs/workbench/parts/terminal/node/terminalEnvironment.ts index 2175f1beb12..7942c4c8700 100644 --- a/src/vs/workbench/parts/terminal/node/terminalEnvironment.ts +++ b/src/vs/workbench/parts/terminal/node/terminalEnvironment.ts @@ -51,24 +51,6 @@ function _mergeEnvironmentValue(env: ITerminalEnvironment, key: string, value: s } } -export function sanitizeEnvironment(env: ITerminalEnvironment): void { - // Remove keys based on strings - const keysToRemove = [ - /^ELECTRON_.+$/, - /^GOOGLE_API_KEY$/, - /^VSCODE_.+$/ - ]; - const envKeys = Object.keys(env); - envKeys.forEach(envKey => { - for (let i = 0; i < keysToRemove.length; i++) { - if (envKey.search(keysToRemove[i]) !== -1) { - delete env[envKey]; - break; - } - } - }); -} - export function addTerminalEnvironmentKeys(env: ITerminalEnvironment, locale: string | undefined, setLocaleVariables: boolean): void { env['TERM_PROGRAM'] = 'vscode'; env['TERM_PROGRAM_VERSION'] = pkg.version; diff --git a/src/vs/workbench/parts/terminal/test/node/terminalEnvironment.test.ts b/src/vs/workbench/parts/terminal/test/node/terminalEnvironment.test.ts index 44806ac20ba..db8533967d8 100644 --- a/src/vs/workbench/parts/terminal/test/node/terminalEnvironment.test.ts +++ b/src/vs/workbench/parts/terminal/test/node/terminalEnvironment.test.ts @@ -32,30 +32,6 @@ suite('Workbench - TerminalEnvironment', () => { assert.equal(env4['LANG'], 'en_US.UTF-8', 'LANG is equal to the parent environment\'s LANG'); }); - test('sanitizeEnvironment', () => { - let env = { - FOO: 'bar', - ELECTRON_ENABLE_STACK_DUMPING: 'x', - ELECTRON_ENABLE_LOGGING: 'x', - ELECTRON_NO_ASAR: 'x', - ELECTRON_NO_ATTACH_CONSOLE: 'x', - ELECTRON_RUN_AS_NODE: 'x', - GOOGLE_API_KEY: 'x', - VSCODE_CLI: 'x', - VSCODE_DEV: 'x', - VSCODE_IPC_HOOK: 'x', - VSCODE_LOGS: 'x', - VSCODE_NLS_CONFIG: 'x', - VSCODE_PORTABLE: 'x', - VSCODE_PID: 'x', - VSCODE_NODE_CACHED_DATA_DIR: 'x', - VSCODE_NEW_VAR: 'x' - }; - terminalEnvironment.sanitizeEnvironment(env); - assert.equal(env['FOO'], 'bar'); - assert.equal(Object.keys(env).length, 1); - }); - suite('mergeEnvironments', () => { test('should add keys', () => { const parent = { From 04accde33c4797388b432bffd4e7beef5f450fda Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 28 Jan 2019 16:44:48 +0100 Subject: [PATCH 223/274] Add proposed API: change event for extensions.all --- src/vs/vscode.proposed.d.ts | 9 +++++++++ src/vs/workbench/api/node/extHost.api.impl.ts | 3 +++ .../extensions/node/extensionDescriptionRegistry.ts | 6 ++++++ 3 files changed, 18 insertions(+) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index c1b069e85a8..a32810ebda4 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1055,6 +1055,15 @@ declare module 'vscode' { } //#endregion + //#region Alex - extensions.all change event + export namespace extensions { + /** + * An event which fires when `extensions.all` changes. + */ + export const onDidChange: Event; + } + //#endregion + //#region Tree View export interface TreeView { diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index 249c88fa891..ebdf93c02d4 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -272,6 +272,9 @@ export function createApiFactory( }, get all(): Extension[] { return extensionRegistry.getAllExtensionDescriptions().map((desc) => new Extension(extensionService, desc)); + }, + get onDidChange() { + return extensionRegistry.onDidChange; } }; diff --git a/src/vs/workbench/services/extensions/node/extensionDescriptionRegistry.ts b/src/vs/workbench/services/extensions/node/extensionDescriptionRegistry.ts index ad385a196c9..3eddf8c90ed 100644 --- a/src/vs/workbench/services/extensions/node/extensionDescriptionRegistry.ts +++ b/src/vs/workbench/services/extensions/node/extensionDescriptionRegistry.ts @@ -5,8 +5,12 @@ import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { Emitter } from 'vs/base/common/event'; export class ExtensionDescriptionRegistry { + private readonly _onDidChange = new Emitter(); + public readonly onDidChange = this._onDidChange.event; + private _extensionDescriptions: IExtensionDescription[]; private _extensionsMap: Map; private _extensionsArr: IExtensionDescription[]; @@ -53,6 +57,7 @@ export class ExtensionDescriptionRegistry { extensionIds.forEach(extensionId => toKeep.add(ExtensionIdentifier.toKey(extensionId))); this._extensionDescriptions = this._extensionDescriptions.filter(extension => toKeep.has(ExtensionIdentifier.toKey(extension.identifier))); this._initialize(); + this._onDidChange.fire(undefined); } public deltaExtensions(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]) { @@ -61,6 +66,7 @@ export class ExtensionDescriptionRegistry { toRemove.forEach(extensionId => toRemoveSet.add(ExtensionIdentifier.toKey(extensionId))); this._extensionDescriptions = this._extensionDescriptions.filter(extension => !toRemoveSet.has(ExtensionIdentifier.toKey(extension.identifier))); this._initialize(); + this._onDidChange.fire(undefined); } public containsActivationEvent(activationEvent: string): boolean { From ac2d914e213040b03cff5c28db6cf890a2eb7327 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 28 Jan 2019 16:50:24 +0100 Subject: [PATCH 224/274] fix #67245 (again) --- src/vs/platform/menubar/electron-main/menubar.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/vs/platform/menubar/electron-main/menubar.ts b/src/vs/platform/menubar/electron-main/menubar.ts index b249609a84f..7bda195c819 100644 --- a/src/vs/platform/menubar/electron-main/menubar.ts +++ b/src/vs/platform/menubar/electron-main/menubar.ts @@ -709,14 +709,15 @@ export class Menubar { if (activeWindow) { if (invocation.type === 'commandId') { - if (!activeWindow.isReady && isMacintosh && invocation.commandId === 'workbench.action.toggleDevTools' && !this.environmentService.isBuilt) { + this.windowsMainService.sendToFocused('vscode:runAction', { id: invocation.commandId, from: 'menu' } as IRunActionInWindowRequest); + } else { + if (isMacintosh && invocation.userSettingsLabel === 'alt+cmd+i' && !this.environmentService.isBuilt && !activeWindow.isReady) { // prevent this action from running twice on macOS (https://github.com/Microsoft/vscode/issues/62719) // we already register a keybinding in bootstrap-window.js for opening developer tools in case something // goes wrong and that keybinding is only removed when the application has loaded (= window ready). return; } - this.windowsMainService.sendToFocused('vscode:runAction', { id: invocation.commandId, from: 'menu' } as IRunActionInWindowRequest); - } else { + this.windowsMainService.sendToFocused('vscode:runKeybinding', { userSettingsLabel: invocation.userSettingsLabel } as IRunKeybindingInWindowRequest); } } From 379e39ff0ed32a31def1068feeda01c9fc41aba8 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 28 Jan 2019 16:56:57 +0100 Subject: [PATCH 225/274] fixes #67250 --- .../workbench/browser/actions/listCommands.ts | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/actions/listCommands.ts b/src/vs/workbench/browser/actions/listCommands.ts index 1ee1e0e8491..e5ee5abd4e3 100644 --- a/src/vs/workbench/browser/actions/listCommands.ts +++ b/src/vs/workbench/browser/actions/listCommands.ts @@ -309,7 +309,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ // Tree only if (focused && !(focused instanceof List || focused instanceof PagedList)) { - if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) { + if (focused instanceof ObjectTree || focused instanceof DataTree) { const tree = focused; const focusedElements = tree.getFocus(); @@ -328,6 +328,26 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ tree.reveal(child); } } + } else if (focused instanceof AsyncDataTree) { + const tree = focused; + const focusedElements = tree.getFocus(); + + if (focusedElements.length === 0) { + return; + } + + const focus = focusedElements[0]; + tree.expand(focus).then(didExpand => { + if (focus && !didExpand) { + const child = tree.getFirstElementChild(focus); + + if (child) { + const fakeKeyboardEvent = new KeyboardEvent('keydown'); + tree.setFocus([child], fakeKeyboardEvent); + tree.reveal(child); + } + } + }); } else { const tree = focused; const focus = tree.getFocus(); From 3df1107cb006e11e22e17c290918ca42a1f73ec5 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Mon, 28 Jan 2019 17:40:55 +0100 Subject: [PATCH 226/274] Update problem matchers in json schema when providing extension is enabled/disabled --- src/vs/workbench/parts/tasks/common/problemMatcher.ts | 8 ++++++++ .../parts/tasks/electron-browser/jsonSchema_v2.ts | 6 +++++- .../parts/tasks/electron-browser/task.contribution.ts | 7 ++++++- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/parts/tasks/common/problemMatcher.ts b/src/vs/workbench/parts/tasks/common/problemMatcher.ts index bab1c2e486d..a9889946c2e 100644 --- a/src/vs/workbench/parts/tasks/common/problemMatcher.ts +++ b/src/vs/workbench/parts/tasks/common/problemMatcher.ts @@ -20,6 +20,7 @@ import { IStringDictionary } from 'vs/base/common/collections'; import { IMarkerData, MarkerSeverity } from 'vs/platform/markers/common/markers'; import { ExtensionsRegistry, ExtensionMessageCollector } from 'vs/workbench/services/extensions/common/extensionsRegistry'; +import { Event, Emitter } from 'vs/base/common/event'; export enum FileLocationKind { Auto, @@ -1681,12 +1682,16 @@ export interface IProblemMatcherRegistry { onReady(): Promise; get(name: string): NamedProblemMatcher; keys(): string[]; + readonly onMatcherChanged; } class ProblemMatcherRegistryImpl implements IProblemMatcherRegistry { private matchers: IStringDictionary; private readyPromise: Promise; + private _onMatchersChanged: Emitter = new Emitter(); + public get onMatcherChanged(): Event { return this._onMatchersChanged.event; } + constructor() { this.matchers = Object.create(null); @@ -1712,6 +1717,9 @@ class ProblemMatcherRegistryImpl implements IProblemMatcherRegistry { } } }); + if ((delta.removed.length > 0) || (delta.added.length > 0)) { + this._onMatchersChanged.fire(); + } } catch (error) { } let matcher = this.get('tsc-watch'); diff --git a/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.ts b/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.ts index ad8290e168e..1f39e52033a 100644 --- a/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.ts +++ b/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.ts @@ -513,7 +513,7 @@ Object.getOwnPropertyNames(definitions).forEach(key => { }); fixReferences(schema); -ProblemMatcherRegistry.onReady().then(() => { +export function updateProblemMatchers() { try { let matcherIds = ProblemMatcherRegistry.keys().map(key => '$' + key); definitions.problemMatcherType2.oneOf![0].enum = matcherIds; @@ -521,6 +521,10 @@ ProblemMatcherRegistry.onReady().then(() => { } catch (err) { console.log('Installing problem matcher ids failed'); } +} + +ProblemMatcherRegistry.onReady().then(() => { + updateProblemMatchers(); }); export default schema; 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 3d57be482f0..07a32bc0a65 100644 --- a/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts +++ b/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts @@ -2658,7 +2658,7 @@ let schema: IJSONSchema = { }; import schemaVersion1 from './jsonSchema_v1'; -import schemaVersion2 from './jsonSchema_v2'; +import schemaVersion2, { updateProblemMatchers } from './jsonSchema_v2'; schema.definitions = { ...schemaVersion1.definitions, ...schemaVersion2.definitions, @@ -2667,3 +2667,8 @@ schema.oneOf = [...schemaVersion2.oneOf, ...schemaVersion1.oneOf]; let jsonRegistry = Registry.as(jsonContributionRegistry.Extensions.JSONContribution); jsonRegistry.registerSchema(schemaId, schema); + +ProblemMatcherRegistry.onMatcherChanged(() => { + updateProblemMatchers(); + jsonRegistry.notifySchemaChanged(schemaId); +}); From d28a8e1ff771eff85d55b22f3aeb340bffe95f21 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 28 Jan 2019 04:29:48 +0000 Subject: [PATCH 227/274] Add cgmanifest for ripgrep --- cgmanifest.json | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/cgmanifest.json b/cgmanifest.json index 1cf8ac1f1fe..37d52feb36c 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -505,6 +505,19 @@ " defined by the Mozilla Public License, v. 2.0." ], "license": "MPL" + }, + { + "component": { + "type": "git", + "git": { + "name": "ripgrep", + "repositoryUrl": "https://github.com/BurntSushi/ripgrep", + "commitHash": "8a7db1a918e969b85cd933d8ed9fa5285b281ba4" + } + }, + "isOnlyProductionDependency": true, + "license": "MIT", + "version": "0.10.0" } ], "version": 1 From 5f9257140a1a238259500e5cd9c3e62bb9488a0b Mon Sep 17 00:00:00 2001 From: isidor Date: Mon, 28 Jan 2019 17:42:50 +0100 Subject: [PATCH 228/274] fixes #67228 --- .../workbench/parts/debug/electron-browser/callStackView.ts | 4 ++++ .../workbench/parts/debug/electron-browser/variablesView.ts | 4 ++++ .../parts/debug/electron-browser/watchExpressionsView.ts | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/src/vs/workbench/parts/debug/electron-browser/callStackView.ts b/src/vs/workbench/parts/debug/electron-browser/callStackView.ts index 38c16c1817c..aef0de79a27 100644 --- a/src/vs/workbench/parts/debug/electron-browser/callStackView.ts +++ b/src/vs/workbench/parts/debug/electron-browser/callStackView.ts @@ -227,6 +227,10 @@ export class CallStackView extends ViewletPanel { this.tree.layout(height, width); } + focus(): void { + this.tree.domFocus(); + } + private updateTreeSelection(): void { if (!this.tree || this.tree.visibleNodeCount === 0) { // Tree not initialized yet diff --git a/src/vs/workbench/parts/debug/electron-browser/variablesView.ts b/src/vs/workbench/parts/debug/electron-browser/variablesView.ts index 21c171270ca..9540ae417cd 100644 --- a/src/vs/workbench/parts/debug/electron-browser/variablesView.ts +++ b/src/vs/workbench/parts/debug/electron-browser/variablesView.ts @@ -121,6 +121,10 @@ export class VariablesView extends ViewletPanel { this.tree.layout(width, height); } + focus(): void { + this.tree.domFocus(); + } + private onMouseDblClick(e: ITreeMouseEvent): void { const session = this.debugService.getViewModel().focusedSession; if (e.element instanceof Variable && session.capabilities.supportsSetVariable) { diff --git a/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts b/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts index 7b344d0abbf..7e461ac86a3 100644 --- a/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts +++ b/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts @@ -117,6 +117,10 @@ export class WatchExpressionsView extends ViewletPanel { this.tree.layout(height, width); } + focus(): void { + this.tree.domFocus(); + } + private onMouseDblClick(e: ITreeMouseEvent): void { if ((e.browserEvent.target as HTMLElement).className.indexOf('twistie') >= 0) { // Ignore double click events on twistie From 0fc0db3a7fe903f825bccf97391b366da7dd8af7 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Mon, 28 Jan 2019 08:48:14 -0800 Subject: [PATCH 229/274] update padding --- .../workbench/parts/comments/electron-browser/media/review.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/comments/electron-browser/media/review.css b/src/vs/workbench/parts/comments/electron-browser/media/review.css index 57347578cb3..b8cf5584e59 100644 --- a/src/vs/workbench/parts/comments/electron-browser/media/review.css +++ b/src/vs/workbench/parts/comments/electron-browser/media/review.css @@ -137,7 +137,7 @@ } .monaco-editor .review-widget .body .review-comment .review-comment-contents .comment-reactions .action-item .action-label { - padding: 2px 5px 0px 5px; + padding: 2px 5px 2px 5px; white-space: pre; text-align: center; font-size: 14px; From 43c36fbdbd4096de0ffacb1cea7691d90f395799 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Mon, 28 Jan 2019 18:01:18 +0100 Subject: [PATCH 230/274] Collect telemetry on connection outcomes (#60773) --- package.json | 2 +- .../services/extensions/node/proxyResolver.ts | 43 +++++++++++++++++-- yarn.lock | 8 ++-- 3 files changed, 45 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index 6d516a6819a..4f78caf443e 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "vscode-chokidar": "1.6.5", "vscode-debugprotocol": "1.33.0", "vscode-nsfw": "1.1.1", - "vscode-proxy-agent": "0.2.0", + "vscode-proxy-agent": "0.3.0", "vscode-ripgrep": "^1.2.5", "vscode-sqlite3": "4.0.7", "vscode-textmate": "^4.0.1", diff --git a/src/vs/workbench/services/extensions/node/proxyResolver.ts b/src/vs/workbench/services/extensions/node/proxyResolver.ts index 4a8df829c28..9205a62dd1b 100644 --- a/src/vs/workbench/services/extensions/node/proxyResolver.ts +++ b/src/vs/workbench/services/extensions/node/proxyResolver.ts @@ -17,6 +17,13 @@ import { toErrorMessage } from 'vs/base/common/errorMessage'; import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService'; import { URI } from 'vs/base/common/uri'; +interface ConnectionResult { + proxy: string; + connection: string; + code: string; + count: number; +} + export function connectProxyResolver( extHostWorkspace: ExtHostWorkspace, configProvider: ExtHostConfigProvider, @@ -83,6 +90,7 @@ function createProxyAgents( let envCount = 0; let settingsCount = 0; let localhostCount = 0; + let results: ConnectionResult[] = []; function logEvent() { timeout = undefined; /* __GDPR__ @@ -95,14 +103,16 @@ function createProxyAgents( "cacheRolls": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "envCount": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "settingsCount": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "localhostCount": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true } + "localhostCount": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "results": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } } */ - mainThreadTelemetry.$publicLog('resolveProxy', { count, duration, errorCount, cacheCount, cacheSize: cache.size, cacheRolls, envCount, settingsCount, localhostCount }); + mainThreadTelemetry.$publicLog('resolveProxy', { count, duration, errorCount, cacheCount, cacheSize: cache.size, cacheRolls, envCount, settingsCount, localhostCount, results }); count = duration = errorCount = cacheCount = envCount = settingsCount = localhostCount = 0; + results = []; } - function resolveProxy(url: string, callback: (proxy?: string) => void) { + function resolveProxy(req: http.ClientRequest, opts: http.RequestOptions, url: string, callback: (proxy?: string) => void) { if (!timeout) { timeout = setTimeout(logEvent, 10 * 60 * 1000); } @@ -135,6 +145,7 @@ function createProxyAgents( const proxy = getCachedProxy(key); if (proxy) { cacheCount++; + collectResult(results, proxy, parsedUrl.protocol === 'https:' ? 'HTTPS' : 'HTTP', req); callback(proxy); extHostLogService.trace('ProxyResolver#resolveProxy cached', url, proxy); return; @@ -144,6 +155,7 @@ function createProxyAgents( extHostWorkspace.resolveProxy(url) // Use full URL to ensure it is an actually used one. .then(proxy => { cacheProxy(key, proxy); + collectResult(results, proxy, parsedUrl.protocol === 'https:' ? 'HTTPS' : 'HTTP', req); callback(proxy); extHostLogService.debug('ProxyResolver#resolveProxy', url, proxy); }).then(() => { @@ -163,6 +175,31 @@ function createProxyAgents( return { http: httpAgent, https: httpsAgent }; } +function collectResult(results: ConnectionResult[], resolveProxy: string, connection: string, req: http.ClientRequest) { + const proxy = resolveProxy ? String(resolveProxy).trim().split(/\s+/, 1)[0] : 'EMPTY'; + req.on('response', res => { + const code = `HTTP_${res.statusCode}`; + const result = findOrCreateResult(results, proxy, connection, code); + result.count++; + }); + req.on('error', err => { + const code = err && typeof (err).code === 'string' && (err).code || 'UNKNOWN_ERROR'; + const result = findOrCreateResult(results, proxy, connection, code); + result.count++; + }); +} + +function findOrCreateResult(results: ConnectionResult[], proxy: string, connection: string, code: string): ConnectionResult | undefined { + for (const result of results) { + if (result.proxy === proxy && result.connection === connection && result.code === code) { + return result; + } + } + const result = { proxy, connection, code, count: 0 }; + results.push(result); + return result; +} + function proxyFromConfigURL(configURL: string) { const url = (configURL || '').trim(); const i = url.indexOf('://'); diff --git a/yarn.lock b/yarn.lock index da55da48abd..82332aa7d79 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9399,10 +9399,10 @@ vscode-nsfw@1.1.1: lodash.isundefined "^3.0.1" nan "^2.10.0" -vscode-proxy-agent@0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/vscode-proxy-agent/-/vscode-proxy-agent-0.2.0.tgz#5ae6e1f8e1b8715a4058fafb6d10cd2409d72620" - integrity sha512-x6RUOUkV18iM78+6S70VjiwiHuY7qmk3CU9+u+0y4c1Y/X8/IcTJfPsngoAXrrojoKcplyLdp3YCZbj1rFNo/Q== +vscode-proxy-agent@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/vscode-proxy-agent/-/vscode-proxy-agent-0.3.0.tgz#b5c8bea5046761966e1fa71f89d9cef11c457894" + integrity sha512-R6qz8Sc0ocNfeFPOp3k6QLP/Y8HzK1yqXwfgB1f0GakVzUGMDmniRe8RLxIiCAqlxGaWMn2yqpTSNUYZ1obPsQ== dependencies: debug "3.1.0" http-proxy-agent "2.1.0" From 1972f79077609b63b42563d71fe2d60f56bc8951 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 28 Jan 2019 18:24:12 +0100 Subject: [PATCH 231/274] client: semantic selection registration --- .../client/src/cssMain.ts | 18 ++++++++++ .../client/src/htmlMain.ts | 33 +++++++++-------- .../client/src/jsonMain.ts | 36 +++++++++---------- 3 files changed, 54 insertions(+), 33 deletions(-) diff --git a/extensions/css-language-features/client/src/cssMain.ts b/extensions/css-language-features/client/src/cssMain.ts index 18f82a02d77..59bd8db7d26 100644 --- a/extensions/css-language-features/client/src/cssMain.ts +++ b/extensions/css-language-features/client/src/cssMain.ts @@ -78,6 +78,24 @@ export function activate(context: ExtensionContext) { client.onReady().then(() => { context.subscriptions.push(initCompletionProvider()); + + documentSelector.forEach(selector => { + context.subscriptions.push(languages.registerSelectionRangeProvider(selector, { + async provideSelectionRanges(document: TextDocument, position: Position): Promise { + const textDocument = client.code2ProtocolConverter.asTextDocumentIdentifier(document); + const rawRanges = await client.sendRequest('$/textDocument/selectionRange', { textDocument, position }); + if (Array.isArray(rawRanges)) { + return rawRanges.map(r => { + return { + range: client.protocol2CodeConverter.asRange(r), + kind: SelectionRangeKind.Declaration + }; + }); + } + return []; + } + })); + }); }); const selectionRangeProvider = { diff --git a/extensions/html-language-features/client/src/htmlMain.ts b/extensions/html-language-features/client/src/htmlMain.ts index 571c1d3ff47..218e05b3e32 100644 --- a/extensions/html-language-features/client/src/htmlMain.ts +++ b/extensions/html-language-features/client/src/htmlMain.ts @@ -9,7 +9,7 @@ import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); import { languages, ExtensionContext, IndentAction, Position, TextDocument, Range, CompletionItem, CompletionItemKind, SnippetString, workspace, SelectionRange, SelectionRangeKind } from 'vscode'; -import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind, RequestType, TextDocumentPositionParams, TextDocumentIdentifier } from 'vscode-languageclient'; +import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind, RequestType, TextDocumentPositionParams } from 'vscode-languageclient'; import { EMPTY_ELEMENTS } from './htmlEmptyTagsShared'; import { activateTagClosing } from './tagClosing'; import TelemetryReporter from 'vscode-extension-telemetry'; @@ -87,21 +87,24 @@ export function activate(context: ExtensionContext) { } }); toDispose.push(disposable); - }); - languages.registerSelectionRangeProvider('html', { - async provideSelectionRanges(document: TextDocument, position: Position): Promise { - const textDocument = TextDocumentIdentifier.create(document.uri.toString()); - const rawRanges: Range[] = await client.sendRequest('$/textDocument/selectionRange', { textDocument, position }); - - return rawRanges.map(r => { - const actualRange = new Range(new Position(r.start.line, r.start.character), new Position(r.end.line, r.end.character)); - return { - range: actualRange, - kind: SelectionRangeKind.Declaration - }; - }); - } + documentSelector.forEach(selector => { + context.subscriptions.push(languages.registerSelectionRangeProvider(selector, { + async provideSelectionRanges(document: TextDocument, position: Position): Promise { + const textDocument = client.code2ProtocolConverter.asTextDocumentIdentifier(document); + const rawRanges = await client.sendRequest('$/textDocument/selectionRange', { textDocument, position }); + if (Array.isArray(rawRanges)) { + return rawRanges.map(r => { + return { + range: client.protocol2CodeConverter.asRange(r), + kind: SelectionRangeKind.Declaration + }; + }); + } + return []; + } + })); + }); }); languages.setLanguageConfiguration('html', { diff --git a/extensions/json-language-features/client/src/jsonMain.ts b/extensions/json-language-features/client/src/jsonMain.ts index b1bc1f4097c..585a908afe7 100644 --- a/extensions/json-language-features/client/src/jsonMain.ts +++ b/extensions/json-language-features/client/src/jsonMain.ts @@ -194,26 +194,26 @@ export function activate(context: ExtensionContext) { client.sendNotification(SchemaAssociationNotification.type, getSchemaAssociation(context)); + documentSelector.forEach(selector => { + toDispose.push(languages.registerSelectionRangeProvider(selector, { + async provideSelectionRanges(document: TextDocument, position: Position): Promise { + const textDocument = client.code2ProtocolConverter.asTextDocumentIdentifier(document); + const rawRanges = await client.sendRequest('$/textDocument/selectionRange', { textDocument, position }); + if (Array.isArray(rawRanges)) { + return rawRanges.map(r => { + return { + range: client.protocol2CodeConverter.asRange(r), + kind: SelectionRangeKind.Declaration + }; + }); + } + return []; + } + })); + }); }); - const selectionRangeProvider = { - async provideSelectionRanges(document: TextDocument, position: Position): Promise { - const textDocument = client.code2ProtocolConverter.asTextDocumentIdentifier(document); - const rawRanges = await client.sendRequest('$/textDocument/selectionRange', { textDocument, position }); - if (Array.isArray(rawRanges)) { - return rawRanges.map(r => { - return { - range: client.protocol2CodeConverter.asRange(r), - kind: SelectionRangeKind.Declaration - }; - }); - } - return []; - } - }; - documentSelector.forEach(selector => { - languages.registerSelectionRangeProvider(selector, selectionRangeProvider); - }); + let languageConfiguration: LanguageConfiguration = { wordPattern: /("(?:[^\\\"]*(?:\\.)?)*"?)|[^\s{}\[\],:]+/, From b257ca20788c639d99790649f931f3b0fcccb331 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 28 Jan 2019 20:53:34 +0100 Subject: [PATCH 232/274] grid: views should know which orientation they are laid out --- src/vs/base/browser/ui/centered/centeredViewLayout.ts | 4 ++-- src/vs/base/browser/ui/grid/gridview.ts | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vs/base/browser/ui/centered/centeredViewLayout.ts b/src/vs/base/browser/ui/centered/centeredViewLayout.ts index 1b1710bed0c..a776ba92a6d 100644 --- a/src/vs/base/browser/ui/centered/centeredViewLayout.ts +++ b/src/vs/base/browser/ui/centered/centeredViewLayout.ts @@ -40,7 +40,7 @@ function toSplitViewView(view: IView, getHeight: () => number): ISplitViewView { get maximumSize() { return view.maximumWidth; }, get minimumSize() { return view.minimumWidth; }, onDidChange: Event.map(view.onDidChange, e => e && e.width), - layout: size => view.layout(size, getHeight()) + layout: size => view.layout(size, getHeight(), Orientation.HORIZONTAL) }; } @@ -78,7 +78,7 @@ export class CenteredViewLayout { this.resizeMargins(); } } else { - this.view.layout(width, height); + this.view.layout(width, height, Orientation.HORIZONTAL); } this.didLayout = true; } diff --git a/src/vs/base/browser/ui/grid/gridview.ts b/src/vs/base/browser/ui/grid/gridview.ts index edde60f0eb3..a3f483632d0 100644 --- a/src/vs/base/browser/ui/grid/gridview.ts +++ b/src/vs/base/browser/ui/grid/gridview.ts @@ -24,7 +24,7 @@ export interface IView { readonly onDidChange: Event<{ width: number; height: number; } | undefined>; readonly priority?: LayoutPriority; readonly snapSize?: number; - layout(width: number, height: number): void; + layout(width: number, height: number, orientation: Orientation): void; } export function orthogonal(orientation: Orientation): Orientation { @@ -480,12 +480,12 @@ class LeafNode implements ISplitView, IDisposable { layout(size: number): void { this._size = size; - return this.view.layout(this.width, this.height); + return this.view.layout(this.width, this.height, orthogonal(this.orientation)); } orthogonalLayout(size: number): void { this._orthogonalSize = size; - return this.view.layout(this.width, this.height); + return this.view.layout(this.width, this.height, orthogonal(this.orientation)); } dispose(): void { } From a920f227b1a73597717b19d9e73de3ff6b4f822c Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 28 Jan 2019 22:21:41 +0100 Subject: [PATCH 233/274] Fix #66685 --- .../markers/electron-browser/constants.ts | 1 + .../electron-browser/markers.contribution.ts | 18 ++- .../markers/electron-browser/markersPanel.ts | 32 +++-- .../electron-browser/markersPanelActions.ts | 17 ++- .../electron-browser/markersTreeViewer.ts | 123 +++++++++++------- 5 files changed, 125 insertions(+), 66 deletions(-) diff --git a/src/vs/workbench/parts/markers/electron-browser/constants.ts b/src/vs/workbench/parts/markers/electron-browser/constants.ts index a9e0b9e8f6d..8d4348b87a7 100644 --- a/src/vs/workbench/parts/markers/electron-browser/constants.ts +++ b/src/vs/workbench/parts/markers/electron-browser/constants.ts @@ -16,6 +16,7 @@ export default { MARKERS_PANEL_SHOW_SINGLELINE_MESSAGE: 'problems.action.showSinglelineMessage', MARKER_OPEN_SIDE_ACTION_ID: 'problems.action.openToSide', MARKER_SHOW_PANEL_ID: 'workbench.action.showErrorsWarnings', + MARKER_SHOW_QUICK_FIX: 'problems.action.showQuickFixes', MarkerPanelFocusContextKey: new RawContextKey('problemsViewFocus', false), MarkerFocusContextKey: new RawContextKey('problemFocus', false), diff --git a/src/vs/workbench/parts/markers/electron-browser/markers.contribution.ts b/src/vs/workbench/parts/markers/electron-browser/markers.contribution.ts index 4afdc0bf6c3..765a7908f83 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markers.contribution.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markers.contribution.ts @@ -53,6 +53,20 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ } }); +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: Constants.MARKER_SHOW_QUICK_FIX, + weight: KeybindingWeight.WorkbenchContrib, + when: Constants.MarkerFocusContextKey, + primary: KeyMod.CtrlCmd | KeyCode.US_DOT, + handler: (accessor, args: any) => { + const markersPanel = (accessor.get(IPanelService).getActivePanel()); + const focusedElement = markersPanel.getFocusElement(); + if (focusedElement instanceof Marker) { + markersPanel.showQuickFixes(focusedElement); + } + } +}); + // configuration Registry.as(Extensions.Configuration).registerConfiguration({ 'id': 'problems', @@ -161,7 +175,7 @@ registerAction({ const panelService = accessor.get(IPanelService); const panel = panelService.getActivePanel(); if (panel instanceof MarkersPanel) { - panel.markersViewState.multiline = true; + panel.markersViewModel.multiline = true; } }, title: localize('show multiline', "Show message in multiple lines"), @@ -177,7 +191,7 @@ registerAction({ const panelService = accessor.get(IPanelService); const panel = panelService.getActivePanel(); if (panel instanceof MarkersPanel) { - panel.markersViewState.multiline = false; + panel.markersViewModel.multiline = false; } }, title: localize('show singleline', "Show message in single line"), diff --git a/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts b/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts index a87ac486e8c..128c751cd08 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts @@ -14,7 +14,7 @@ import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/ import Constants from 'vs/workbench/parts/markers/electron-browser/constants'; import { Marker, ResourceMarkers, RelatedInformation, MarkersModel } from 'vs/workbench/parts/markers/electron-browser/markersModel'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { MarkersFilterActionItem, MarkersFilterAction, QuickFixAction, QuickFixActionItem, IMarkersFilterActionChangeEvent, IMarkerFilterController } from 'vs/workbench/parts/markers/electron-browser/markersPanelActions'; +import { MarkersFilterActionItem, MarkersFilterAction, IMarkersFilterActionChangeEvent, IMarkerFilterController } from 'vs/workbench/parts/markers/electron-browser/markersPanelActions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import Messages from 'vs/workbench/parts/markers/electron-browser/messages'; import { RangeHighlightDecorations } from 'vs/workbench/browser/parts/editor/rangeDecorations'; @@ -33,7 +33,7 @@ import { IExpression, getEmptyExpression } from 'vs/base/common/glob'; import { mixin, deepClone } from 'vs/base/common/objects'; import { IWorkspaceFolder, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { isAbsolute, join } from 'vs/base/common/paths'; -import { FilterData, Filter, VirtualDelegate, ResourceMarkersRenderer, MarkerRenderer, RelatedInformationRenderer, TreeElement, MarkersTreeAccessibilityProvider, MarkersViewState } from 'vs/workbench/parts/markers/electron-browser/markersTreeViewer'; +import { FilterData, Filter, VirtualDelegate, ResourceMarkersRenderer, MarkerRenderer, RelatedInformationRenderer, TreeElement, MarkersTreeAccessibilityProvider, MarkersViewModel } from 'vs/workbench/parts/markers/electron-browser/markersTreeViewer'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { Separator, ActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; @@ -89,7 +89,7 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { private cachedFilterStats: { total: number; filtered: number; } | undefined = undefined; private currentResourceGotAddedToMarkersData: boolean = false; - readonly markersViewState: MarkersViewState; + readonly markersViewModel: MarkersViewModel; private disposables: IDisposable[] = []; constructor( @@ -109,8 +109,8 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { super(Constants.MARKERS_PANEL_ID, telemetryService, themeService, storageService); this.panelFoucusContextKey = Constants.MarkerPanelFocusContextKey.bindTo(contextKeyService); this.panelState = this.getMemento(StorageScope.WORKSPACE); - this.markersViewState = new MarkersViewState(this.panelState['multiline']); - this.markersViewState.onDidChangeViewState(this.onDidChangeViewState, this, this.disposables); + this.markersViewModel = instantiationService.createInstance(MarkersViewModel, this.panelState['multiline']); + this.markersViewModel.onDidChange(this.onDidChangeViewState, this, this.disposables); this.setCurrentActiveEditor(); } @@ -178,6 +178,13 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { return this.actions; } + public showQuickFixes(marker: Marker): void { + const viewModel = this.markersViewModel.getViewModel(marker); + if (viewModel) { + viewModel.quickFixAction.run(); + } + } + public openFileAtElement(element: any, preserveFocus: boolean, sideByside: boolean, pinned: boolean): boolean { const { resource, selection, event, data } = element instanceof Marker ? { resource: element.resource, selection: element.range, event: 'problems.selectDiagnostic', data: this.getTelemetryData(element.marker) } : element instanceof RelatedInformation ? { resource: element.raw.resource, selection: element.raw, event: 'problems.selectRelatedInformation', data: this.getTelemetryData(element.marker) } : { resource: null, selection: null, event: null, data: null }; @@ -306,10 +313,10 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { this.treeLabels = this._register(this.instantiationService.createInstance(ResourceLabels, this)); - const virtualDelegate = new VirtualDelegate(this.markersViewState); + const virtualDelegate = new VirtualDelegate(this.markersViewModel); const renderers = [ this.instantiationService.createInstance(ResourceMarkersRenderer, this.treeLabels, onDidChangeRenderNodeCount.event), - this.instantiationService.createInstance(MarkerRenderer, this.markersViewState, a => this.getActionItem(a)), + this.instantiationService.createInstance(MarkerRenderer, this.markersViewModel), this.instantiationService.createInstance(RelatedInformationRenderer) ]; this.filter = new Filter(); @@ -408,11 +415,11 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { private onDidChangeModel(resources: URI[]) { for (const resource of resources) { - this.markersViewState.remove(resource); + this.markersViewModel.remove(resource); const resourceMarkers = this.markersWorkbenchService.markersModel.getResourceMarkers(resource); if (resourceMarkers) { for (const marker of resourceMarkers.markers) { - this.markersViewState.add(marker); + this.markersViewModel.add(marker); } } } @@ -657,9 +664,6 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { this.filterInputActionItem = this.instantiationService.createInstance(MarkersFilterActionItem, this.filterAction, this); return this.filterInputActionItem; } - if (action.id === QuickFixAction.ID) { - return this.instantiationService.createInstance(QuickFixActionItem, action); - } return super.getActionItem(action); } @@ -701,7 +705,7 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { this.panelState['filter'] = this.filterAction.filterText; this.panelState['filterHistory'] = this.filterAction.filterHistory; this.panelState['useFilesExclude'] = this.filterAction.useFilesExclude; - this.panelState['multiline'] = this.markersViewState.multiline; + this.panelState['multiline'] = this.markersViewModel.multiline; super.saveState(); } @@ -709,7 +713,7 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { public dispose(): void { super.dispose(); this.tree.dispose(); - this.markersViewState.dispose(); + this.markersViewModel.dispose(); this.disposables = dispose(this.disposables); } } \ No newline at end of file diff --git a/src/vs/workbench/parts/markers/electron-browser/markersPanelActions.ts b/src/vs/workbench/parts/markers/electron-browser/markersPanelActions.ts index b81abdf184c..278570fc877 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markersPanelActions.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markersPanelActions.ts @@ -29,7 +29,7 @@ import { Marker } from 'vs/workbench/parts/markers/electron-browser/markersModel import { IModelService } from 'vs/editor/common/services/modelService'; import { isEqual } from 'vs/base/common/resources'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { Event } from 'vs/base/common/event'; +import { Event, Emitter } from 'vs/base/common/event'; import { FilterOptions } from 'vs/workbench/parts/markers/electron-browser/markersFilterOptions'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -297,12 +297,16 @@ export class QuickFixAction extends Action { private updated: boolean = false; private disposables: IDisposable[] = []; + private _onShowQuickFixes: Emitter = new Emitter(); + readonly onShowQuickFixes: Event = this._onShowQuickFixes.event; + constructor( readonly marker: Marker, @IModelService modelService: IModelService, @IMarkersWorkbenchService private readonly markerWorkbenchService: IMarkersWorkbenchService, ) { super(QuickFixAction.ID, Messages.MARKERS_PANEL_ACTION_TOOLTIP_QUICKFIX, 'markers-panel-action-quickfix', false); + this.disposables.push(this._onShowQuickFixes); if (modelService.getModel(this.marker.resource)) { this.update(); } else { @@ -314,6 +318,11 @@ export class QuickFixAction extends Action { } } + run(): Promise { + this._onShowQuickFixes.fire(); + return Promise.resolve(); + } + private update(): void { if (!this.updated) { this.markerWorkbenchService.hasQuickFixes(this.marker).then(hasFixes => this.enabled = hasFixes); @@ -338,13 +347,17 @@ export class QuickFixActionItem extends ActionItem { public onClick(event: DOM.EventLike): void { DOM.EventHelper.stop(event, true); + this.showQuickFixes(); + } + + public showQuickFixes(): void { if (!this.element) { return; } const elementPosition = DOM.getDomNodePagePosition(this.element); this.markerWorkbenchService.getQuickFixActions((this.getAction()).marker).then(actions => { this.contextMenuService.showContextMenu({ - getAnchor: () => ({ x: elementPosition.left + 10, y: elementPosition.top + elementPosition.height }), + getAnchor: () => ({ x: elementPosition.left + 10, y: elementPosition.top + elementPosition.height + 4 }), getActions: () => actions }); }); diff --git a/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts b/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts index c18c2778053..a284d526c5e 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts @@ -16,8 +16,8 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { attachBadgeStyler } from 'vs/platform/theme/common/styler'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IDisposable, dispose, Disposable, toDisposable } from 'vs/base/common/lifecycle'; -import { ActionBar, IActionItemProvider } from 'vs/base/browser/ui/actionbar/actionbar'; -import { QuickFixAction } from 'vs/workbench/parts/markers/electron-browser/markersPanelActions'; +import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { QuickFixAction, QuickFixActionItem } from 'vs/workbench/parts/markers/electron-browser/markersPanelActions'; import { ILabelService } from 'vs/platform/label/common/label'; import { dirname } from 'vs/base/common/resources'; import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; @@ -76,12 +76,12 @@ const enum TemplateId { export class VirtualDelegate implements IListVirtualDelegate { - constructor(private readonly markersViewState: MarkersViewState) { } + constructor(private readonly markersViewState: MarkersViewModel) { } getHeight(element: TreeElement): number { if (element instanceof Marker) { - const viewState = this.markersViewState.getViewState(element); - const noOfLines = !viewState || viewState.multiline ? element.lines.length : 1; + const viewModel = this.markersViewState.getViewModel(element); + const noOfLines = !viewModel || viewModel.multiline ? element.lines.length : 1; return noOfLines * 22; } return 22; @@ -201,8 +201,7 @@ export class FileResourceMarkersRenderer extends ResourceMarkersRenderer { export class MarkerRenderer implements ITreeRenderer { constructor( - private readonly markersViewState: MarkersViewState, - private actionItemProvider: IActionItemProvider, + private readonly markersViewState: MarkersViewModel, @IInstantiationService protected instantiationService: IInstantiationService ) { } @@ -210,7 +209,7 @@ export class MarkerRenderer implements ITreeRenderer action.id === QuickFixAction.ID ? instantiationService.createInstance(QuickFixActionItem, action) : null + })); this.icon = dom.append(parent, dom.$('.icon')); - this.multilineActionbar = this._register(new ActionBar(dom.append(parent, dom.$('.multiline-actions')), { actionItemProvider })); + this.multilineActionbar = this._register(new ActionBar(dom.append(parent, dom.$('.multiline-actions')))); this.messageAndDetailsContainer = dom.append(parent, dom.$('.marker-message-details')); this._register(toDisposable(() => this.disposables = dispose(this.disposables))); } @@ -254,38 +254,47 @@ class MarkerWidget extends Disposable { } dom.clearNode(this.messageAndDetailsContainer); - this.renderQuickfixActionbar(element); this.icon.className = 'marker-icon ' + MarkerWidget.iconClassNameFor(element.marker); + this.renderQuickfixActionbar(element); this.renderMultilineActionbar(element); this.renderMessageAndDetails(element, filterData); } private renderQuickfixActionbar(marker: Marker): void { - const quickFixAction = this.instantiationService.createInstance(QuickFixAction, marker); - this.actionBar.push([quickFixAction], { icon: true, label: false }); - dom.toggleClass(this.icon, 'quickFix', quickFixAction.enabled); - quickFixAction.onDidChange(({ enabled }) => { - if (!isUndefinedOrNull(enabled)) { - dom.toggleClass(this.icon, 'quickFix', enabled); - } - }, this, this.disposables); + const viewModel = this.markersViewModel.getViewModel(marker); + if (viewModel) { + const quickFixAction = viewModel.quickFixAction; + this.actionBar.push([quickFixAction], { icon: true, label: false }); + dom.toggleClass(this.icon, 'quickFix', quickFixAction.enabled); + quickFixAction.onDidChange(({ enabled }) => { + if (!isUndefinedOrNull(enabled)) { + dom.toggleClass(this.icon, 'quickFix', enabled); + } + }, this, this.disposables); + quickFixAction.onShowQuickFixes(() => { + const quickFixActionItem = this.actionBar.items[0]; + if (quickFixActionItem) { + quickFixActionItem.showQuickFixes(); + } + }, this, this.disposables); + } } private renderMultilineActionbar(marker: Marker): void { - const viewState = this.markersViewState.getViewState(marker); - const multiline = viewState && viewState.multiline; + const viewModel = this.markersViewModel.getViewModel(marker); + const multiline = viewModel && viewModel.multiline; const action = new Action('problems.action.toggleMultiline'); - action.enabled = viewState && marker.lines.length > 1; + action.enabled = viewModel && marker.lines.length > 1; action.tooltip = multiline ? localize('single line', "Show message in single line") : localize('multi line', "Show message in multiple lines"); action.class = multiline ? 'octicon octicon-chevron-up' : 'octicon octicon-chevron-down'; - action.run = () => { if (viewState) { viewState.multiline = !viewState.multiline; } return Promise.resolve(); }; + action.run = () => { if (viewModel) { viewModel.multiline = !viewModel.multiline; } return Promise.resolve(); }; this.multilineActionbar.push([action], { icon: true, label: false }); } private renderMessageAndDetails(element: Marker, filterData: MarkerFilterData) { const { marker, lines } = element; - const viewState = this.markersViewState.getViewState(element); + const viewState = this.markersViewModel.getViewModel(element); const multiline = !viewState || viewState.multiline; const lineMatches = filterData && filterData.lineMatches || []; const messageContainer = dom.append(this.messageAndDetailsContainer, dom.$('.marker-message')); @@ -461,10 +470,17 @@ export class Filter implements ITreeFilter { } } -export class MarkerViewState extends Disposable { +export class MarkerViewModel extends Disposable { - private readonly _onDidChangeViewState: Emitter = this._register(new Emitter()); - readonly onDidChangeViewState: Event = this._onDidChangeViewState.event; + private readonly _onDidChange: Emitter = this._register(new Emitter()); + readonly onDidChange: Event = this._onDidChange.event; + + constructor( + private readonly marker: Marker, + @IInstantiationService private instantiationService: IInstantiationService + ) { + super(); + } private _multiline: boolean = true; get multiline(): boolean { @@ -474,37 +490,48 @@ export class MarkerViewState extends Disposable { set multiline(value: boolean) { if (this._multiline !== value) { this._multiline = value; - this._onDidChangeViewState.fire(); + this._onDidChange.fire(); } } + + private _quickFixAction: QuickFixAction; + get quickFixAction(): QuickFixAction { + if (!this._quickFixAction) { + this._quickFixAction = this._register(this.instantiationService.createInstance(QuickFixAction, this.marker)); + } + return this._quickFixAction; + } } -export class MarkersViewState extends Disposable { +export class MarkersViewModel extends Disposable { - private readonly _onDidChangeViewState: Emitter = this._register(new Emitter()); - readonly onDidChangeViewState: Event = this._onDidChangeViewState.event; + private readonly _onDidChange: Emitter = this._register(new Emitter()); + readonly onDidChange: Event = this._onDidChange.event; - private readonly markersViewStates: Map = new Map(); + private readonly markersViewStates: Map = new Map(); private readonly markersPerResource: Map = new Map(); private bulkUpdate: boolean = false; - constructor(multiline: boolean = true) { + constructor( + multiline: boolean = true, + @IInstantiationService private instantiationService: IInstantiationService + ) { super(); this._multiline = multiline; } add(marker: Marker): void { if (!this.markersViewStates.has(marker.hash)) { - const disposables: IDisposable[] = []; - const viewState = new MarkerViewState(); - viewState.multiline = this.multiline; - viewState.onDidChangeViewState(() => { + const viewModel = this.instantiationService.createInstance(MarkerViewModel, marker); + const disposables: IDisposable[] = [viewModel]; + viewModel.multiline = this.multiline; + viewModel.onDidChange(() => { if (!this.bulkUpdate) { - this._onDidChangeViewState.fire(marker); + this._onDidChange.fire(marker); } }, this, disposables); - this.markersViewStates.set(marker.hash, { viewState, disposables }); + this.markersViewStates.set(marker.hash, { viewModel, disposables }); const markers = this.markersPerResource.get(marker.resource.toString()) || []; markers.push(marker); @@ -524,9 +551,9 @@ export class MarkersViewState extends Disposable { this.markersPerResource.delete(resource.toString()); } - getViewState(marker: Marker): MarkerViewState | null { + getViewModel(marker: Marker): MarkerViewModel | null { const value = this.markersViewStates.get(marker.hash); - return value ? value.viewState : null; + return value ? value.viewModel : null; } private _multiline: boolean = true; @@ -541,15 +568,15 @@ export class MarkersViewState extends Disposable { changed = true; } this.bulkUpdate = true; - this.markersViewStates.forEach(({ viewState }) => { - if (viewState.multiline !== value) { - viewState.multiline = value; + this.markersViewStates.forEach(({ viewModel }) => { + if (viewModel.multiline !== value) { + viewModel.multiline = value; changed = true; } }); this.bulkUpdate = false; if (changed) { - this._onDidChangeViewState.fire(undefined); + this._onDidChange.fire(undefined); } } From 81e2ef1be1d6e0cd60ede4fd82c2a8a22ae40111 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Mon, 28 Jan 2019 13:29:36 -0800 Subject: [PATCH 234/274] Update services --- .../css-language-features/server/package.json | 2 +- .../css-language-features/server/yarn.lock | 8 ++++---- .../html-language-features/server/package.json | 4 ++-- .../html-language-features/server/yarn.lock | 16 ++++++++-------- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/extensions/css-language-features/server/package.json b/extensions/css-language-features/server/package.json index 5fe60307113..aebf551a2e5 100644 --- a/extensions/css-language-features/server/package.json +++ b/extensions/css-language-features/server/package.json @@ -9,7 +9,7 @@ }, "main": "./out/cssServerMain", "dependencies": { - "vscode-css-languageservice": "^3.0.13-next.9", + "vscode-css-languageservice": "^3.0.13-next.10", "vscode-languageserver": "^5.1.0" }, "devDependencies": { diff --git a/extensions/css-language-features/server/yarn.lock b/extensions/css-language-features/server/yarn.lock index 0c35b246af8..665dd086b76 100644 --- a/extensions/css-language-features/server/yarn.lock +++ b/extensions/css-language-features/server/yarn.lock @@ -229,10 +229,10 @@ supports-color@5.4.0: dependencies: has-flag "^3.0.0" -vscode-css-languageservice@^3.0.13-next.9: - version "3.0.13-next.9" - resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-3.0.13-next.9.tgz#1f3ba55fb7444d8eab2c20df60ea5a95ae450cce" - integrity sha512-ooq2KOge+fF7K3mk43C+3/a7roVBDgHUSWfPiDrhOcT7iHAFSmyG/I7yZpH29mXincIHo22hGCoWzEerJF1otw== +vscode-css-languageservice@^3.0.13-next.10: + version "3.0.13-next.10" + resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-3.0.13-next.10.tgz#f5822e832b06e1e91ec96c528bab83bb07251c35" + integrity sha512-zKwzo3GVhrAllYDM4afL8q1XCHixsI8tP3SyLrWGzp0Nc9P+bbjKQeC26VcaOb0dtkgfpB/vfBPf+4yOs4s/pw== dependencies: vscode-languageserver-types "^3.13.0" vscode-nls "^4.0.0" diff --git a/extensions/html-language-features/server/package.json b/extensions/html-language-features/server/package.json index eb1eae43671..170deb801b2 100644 --- a/extensions/html-language-features/server/package.json +++ b/extensions/html-language-features/server/package.json @@ -9,8 +9,8 @@ }, "main": "./out/htmlServerMain", "dependencies": { - "vscode-css-languageservice": "^3.0.13-next.9", - "vscode-html-languageservice": "^2.1.11-next.6", + "vscode-css-languageservice": "^3.0.13-next.10", + "vscode-html-languageservice": "^2.1.11-next.7", "vscode-languageserver": "^5.1.0", "vscode-languageserver-types": "^3.13.0", "vscode-nls": "^4.0.0", diff --git a/extensions/html-language-features/server/yarn.lock b/extensions/html-language-features/server/yarn.lock index 3455b53ad43..bb1b921c1c6 100644 --- a/extensions/html-language-features/server/yarn.lock +++ b/extensions/html-language-features/server/yarn.lock @@ -229,18 +229,18 @@ supports-color@5.4.0: dependencies: has-flag "^3.0.0" -vscode-css-languageservice@^3.0.13-next.9: - version "3.0.13-next.9" - resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-3.0.13-next.9.tgz#1f3ba55fb7444d8eab2c20df60ea5a95ae450cce" - integrity sha512-ooq2KOge+fF7K3mk43C+3/a7roVBDgHUSWfPiDrhOcT7iHAFSmyG/I7yZpH29mXincIHo22hGCoWzEerJF1otw== +vscode-css-languageservice@^3.0.13-next.10: + version "3.0.13-next.10" + resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-3.0.13-next.10.tgz#f5822e832b06e1e91ec96c528bab83bb07251c35" + integrity sha512-zKwzo3GVhrAllYDM4afL8q1XCHixsI8tP3SyLrWGzp0Nc9P+bbjKQeC26VcaOb0dtkgfpB/vfBPf+4yOs4s/pw== dependencies: vscode-languageserver-types "^3.13.0" vscode-nls "^4.0.0" -vscode-html-languageservice@^2.1.11-next.6: - version "2.1.11-next.6" - resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-2.1.11-next.6.tgz#66eb7a10d84ac91d5985b45cfa704ce05fe3e638" - integrity sha512-BCShNFqflm1XpjMJrxAz4tWSAdA1cqJi2XrI3/eHzqcHzw4kO/l3ApaZPdci77CAcOdWKyDmmzSau0UwR+CkYQ== +vscode-html-languageservice@^2.1.11-next.7: + version "2.1.11-next.7" + resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-2.1.11-next.7.tgz#4de88828ffa8b133b94a00de31512856f1cd1998" + integrity sha512-k6xFtjw7mk7MIokNxPV+BTLLUTK9OwLxeIgJPibDi2v5b078FNgpD7Yb/h1sZ3ZHVTbd2ARfe4EY6QOTSQ1eaw== dependencies: vscode-languageserver-types "^3.13.0" vscode-nls "^4.0.0" From 36100deb715cc3ebac7310326c10dc5a408ef0fc Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Mon, 28 Jan 2019 08:50:38 -0800 Subject: [PATCH 235/274] render reactions only when it's more than one. --- src/vs/workbench/parts/comments/electron-browser/commentNode.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/comments/electron-browser/commentNode.ts b/src/vs/workbench/parts/comments/electron-browser/commentNode.ts index d81030836ec..894c8a31fb2 100644 --- a/src/vs/workbench/parts/comments/electron-browser/commentNode.ts +++ b/src/vs/workbench/parts/comments/electron-browser/commentNode.ts @@ -92,7 +92,7 @@ export class CommentNode extends Disposable { this._md = this.markdownRenderer.render(comment.body).element; this._body.appendChild(this._md); - if (this.comment.commentReactions) { + if (this.comment.commentReactions && this.comment.commentReactions.length) { this.createReactions(commentDetailsContainer); } From 527513513ae1c49e63dce9828a4786fba58e8183 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Mon, 28 Jan 2019 13:35:11 -0800 Subject: [PATCH 236/274] rerender comment reactions when thread update --- .../comments/electron-browser/commentNode.ts | 36 +++++++++++++------ 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/parts/comments/electron-browser/commentNode.ts b/src/vs/workbench/parts/comments/electron-browser/commentNode.ts index 894c8a31fb2..5d1c0b3b434 100644 --- a/src/vs/workbench/parts/comments/electron-browser/commentNode.ts +++ b/src/vs/workbench/parts/comments/electron-browser/commentNode.ts @@ -44,6 +44,9 @@ export class CommentNode extends Disposable { private _editAction: Action; private _commentEditContainer: HTMLElement; + private _commentDetailsContainer: HTMLElement; + private _reactionsActionBar?: ActionBar; + private _actionsContainer?: HTMLElement; private _commentEditor: SimpleCommentEditor; private _commentEditorModel: ITextModel; private _updateCommentButton: Button; @@ -84,16 +87,16 @@ export class CommentNode extends Disposable { img.src = comment.userIconPath.toString(); img.onerror = _ => img.remove(); } - const commentDetailsContainer = dom.append(this._domNode, dom.$('.review-comment-contents')); + this._commentDetailsContainer = dom.append(this._domNode, dom.$('.review-comment-contents')); - this.createHeader(commentDetailsContainer); + this.createHeader(this._commentDetailsContainer); - this._body = dom.append(commentDetailsContainer, dom.$('div.comment-body')); + this._body = dom.append(this._commentDetailsContainer, dom.$('div.comment-body')); this._md = this.markdownRenderer.render(comment.body).element; this._body.appendChild(this._md); if (this.comment.commentReactions && this.comment.commentReactions.length) { - this.createReactions(commentDetailsContainer); + this.createReactions(this._commentDetailsContainer); } this._domNode.setAttribute('aria-label', `${comment.userName}, ${comment.body.value}`); @@ -139,7 +142,7 @@ export class CommentNode extends Disposable { let reactionActions = []; let reactionGroup = this.commentService.getReactionGroup(this.owner); - if (reactionGroup) { + if (reactionGroup && reactionGroup.length) { reactionActions = reactionGroup.map((reaction) => { return new Action(`reaction.command.${reaction.label}`, `${reaction.label}`, '', true, async () => { try { @@ -172,9 +175,9 @@ export class CommentNode extends Disposable { } private createReactions(commentDetailsContainer: HTMLElement): void { - const actionsContainer = dom.append(commentDetailsContainer, dom.$('div.comment-reactions')); - const actionBar = new ActionBar(actionsContainer, {}); - this._toDispose.push(actionBar); + this._actionsContainer = dom.append(commentDetailsContainer, dom.$('div.comment-reactions')); + this._reactionsActionBar = new ActionBar(this._actionsContainer, {}); + this._toDispose.push(this._reactionsActionBar); let reactionActions = this.comment.commentReactions.map(reaction => { return new Action(`reaction.${reaction.label}`, `${reaction.label}`, reaction.hasReacted ? 'active' : '', true, async () => { @@ -201,7 +204,7 @@ export class CommentNode extends Disposable { }); }); - reactionActions.forEach(action => actionBar.push(action, { label: true, icon: true })); + reactionActions.forEach(action => this._reactionsActionBar.push(action, { label: true, icon: true })); } private createCommentEditor(): void { @@ -354,6 +357,8 @@ export class CommentNode extends Disposable { } update(newComment: modes.Comment) { + this.comment = newComment; + if (newComment.body !== this.comment.body) { this._body.removeChild(this._md); this._md = this.markdownRenderer.render(newComment.body).element; @@ -366,7 +371,18 @@ export class CommentNode extends Disposable { this._isPendingLabel.innerText = ''; } - this.comment = newComment; + // update comment reactions + if (this._actionsContainer) { + this._actionsContainer.remove(); + } + + if (this._reactionsActionBar) { + this._reactionsActionBar.clear(); + } + + if (this.comment.commentReactions && this.comment.commentReactions.length) { + this.createReactions(this._commentDetailsContainer); + } } focus() { From cd4a6cebe707abe467dfb7a52fba549732241b6a Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 28 Jan 2019 22:39:39 +0100 Subject: [PATCH 237/274] Fix #67105 --- .../parts/extensions/electron-browser/media/extensionEditor.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/extensions/electron-browser/media/extensionEditor.css b/src/vs/workbench/parts/extensions/electron-browser/media/extensionEditor.css index f9b18fc7a83..a5446fb5d46 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/media/extensionEditor.css +++ b/src/vs/workbench/parts/extensions/electron-browser/media/extensionEditor.css @@ -131,7 +131,7 @@ .extension-editor > .header > .details > .actions > .monaco-action-bar > .actions-container > .action-item > .action-label { font-weight: 600; - margin: 4px; + margin: 4px 8px 4px 0px; padding: 1px 6px; } From ccb5a625c2412b36baeb14d8cac84f7053f298eb Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 25 Jan 2019 16:17:38 -0800 Subject: [PATCH 238/274] Mark extract constant as a preferred refactoring https://github.com/Microsoft/TypeScript/issues/29587 --- .../src/features/refactor.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/extensions/typescript-language-features/src/features/refactor.ts b/extensions/typescript-language-features/src/features/refactor.ts index 5f28e8e398b..bad25c983c4 100644 --- a/extensions/typescript-language-features/src/features/refactor.ts +++ b/extensions/typescript-language-features/src/features/refactor.ts @@ -188,6 +188,7 @@ class TypeScriptRefactorProvider implements vscode.CodeActionProvider { command: ApplyRefactoringCommand.ID, arguments: [document, file, info.name, action.name, rangeOrSelection], }; + codeAction.isPreferred = TypeScriptRefactorProvider.isPreferred(action); return codeAction; } @@ -209,6 +210,15 @@ class TypeScriptRefactorProvider implements vscode.CodeActionProvider { } return vscode.CodeActionKind.Refactor; } + + private static isPreferred( + action: Proto.RefactorActionInfo + ): boolean { + if (action.name.startsWith('constant_')) { + return action.name.endsWith('scope_0'); + } + return false; + } } export function register( From ef25b181eac10cd69e522542b1321c435e438bc9 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 25 Jan 2019 17:06:14 -0800 Subject: [PATCH 239/274] Move ServerResponse into namespace --- .../src/commands/goToProjectConfiguration.ts | 2 +- .../src/features/rename.ts | 2 +- .../src/test/cachedResponse.test.ts | 20 +++++++------- .../src/tsServer/cachedResponse.ts | 8 +++--- .../src/tsServer/callbackMap.ts | 12 ++++----- .../src/tsServer/server.ts | 14 +++++----- .../src/typescriptService.ts | 27 ++++++++++--------- .../src/typescriptServiceClient.ts | 12 ++++----- 8 files changed, 50 insertions(+), 47 deletions(-) diff --git a/extensions/typescript-language-features/src/commands/goToProjectConfiguration.ts b/extensions/typescript-language-features/src/commands/goToProjectConfiguration.ts index ee819648000..9273683ba4c 100644 --- a/extensions/typescript-language-features/src/commands/goToProjectConfiguration.ts +++ b/extensions/typescript-language-features/src/commands/goToProjectConfiguration.ts @@ -69,7 +69,7 @@ async function goToProjectConfig( return; } - let res: ServerResponse | undefined; + let res: ServerResponse.Response | undefined; try { res = await client.execute('projectInfo', { file, needFileNameList: false }, nulToken); } catch { diff --git a/extensions/typescript-language-features/src/features/rename.ts b/extensions/typescript-language-features/src/features/rename.ts index 55e6c819a08..aff2c508c53 100644 --- a/extensions/typescript-language-features/src/features/rename.ts +++ b/extensions/typescript-language-features/src/features/rename.ts @@ -79,7 +79,7 @@ class TypeScriptRenameProvider implements vscode.RenameProvider { document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken - ): Promise | undefined> { + ): Promise | undefined> { const file = this.client.toOpenedFilePath(document); if (!file) { return undefined; diff --git a/extensions/typescript-language-features/src/test/cachedResponse.test.ts b/extensions/typescript-language-features/src/test/cachedResponse.test.ts index de1003f284f..919d5d4d03c 100644 --- a/extensions/typescript-language-features/src/test/cachedResponse.test.ts +++ b/extensions/typescript-language-features/src/test/cachedResponse.test.ts @@ -8,7 +8,7 @@ import 'mocha'; import * as vscode from 'vscode'; import * as Proto from '../protocol'; import { CachedResponse } from '../tsServer/cachedResponse'; -import { ServerResponse, CancelledResponse } from '../typescriptService'; +import { ServerResponse } from '../typescriptService'; suite('CachedResponse', () => { test('should cache simple response for same document', async () => { @@ -36,12 +36,12 @@ suite('CachedResponse', () => { const doc = await createTextDocument(); const response = new CachedResponse(); - const cancelledResponder = createEventualResponder(); + const cancelledResponder = createEventualResponder(); const result1 = response.execute(doc, () => cancelledResponder.promise); const result2 = response.execute(doc, respondWith('test-0')); const result3 = response.execute(doc, respondWith('test-1')); - cancelledResponder.resolve(new CancelledResponse('cancelled')); + cancelledResponder.resolve(new ServerResponse.Cancelled('cancelled')); assert.strictEqual((await result1).type, 'cancelled'); assertResult(await result2, 'test-0'); @@ -52,12 +52,12 @@ suite('CachedResponse', () => { const doc = await createTextDocument(); const response = new CachedResponse(); - const cancelledResponder = createEventualResponder(); + const cancelledResponder = createEventualResponder(); const result1 = response.execute(doc, respondWith('test-0')); const result2 = response.execute(doc, () => cancelledResponder.promise); const result3 = response.execute(doc, respondWith('test-1')); - cancelledResponder.resolve(new CancelledResponse('cancelled')); + cancelledResponder.resolve(new ServerResponse.Cancelled('cancelled')); assertResult(await result1, 'test-0'); assertResult(await result2, 'test-0'); @@ -69,8 +69,8 @@ suite('CachedResponse', () => { const doc2 = await createTextDocument(); const response = new CachedResponse(); - const cancelledResponder = createEventualResponder(); - const cancelledResponder2 = createEventualResponder(); + const cancelledResponder = createEventualResponder(); + const cancelledResponder2 = createEventualResponder(); const result1 = response.execute(doc1, () => cancelledResponder.promise); const result2 = response.execute(doc1, respondWith('test-0')); @@ -79,8 +79,8 @@ suite('CachedResponse', () => { const result5 = response.execute(doc2, respondWith('test-2')); const result6 = response.execute(doc1, respondWith('test-3')); - cancelledResponder.resolve(new CancelledResponse('cancelled')); - cancelledResponder2.resolve(new CancelledResponse('cancelled')); + cancelledResponder.resolve(new ServerResponse.Cancelled('cancelled')); + cancelledResponder2.resolve(new ServerResponse.Cancelled('cancelled')); assert.strictEqual((await result1).type, 'cancelled'); assertResult(await result2, 'test-0'); @@ -99,7 +99,7 @@ function createTextDocument() { return vscode.workspace.openTextDocument({ language: 'javascript', content: '' }); } -function assertResult(result: ServerResponse, command: string) { +function assertResult(result: ServerResponse.Response, command: string) { if (result.type === 'response') { assert.strictEqual(result.command, command); } else { diff --git a/extensions/typescript-language-features/src/tsServer/cachedResponse.ts b/extensions/typescript-language-features/src/tsServer/cachedResponse.ts index ea8414cf8a5..2fb9b542281 100644 --- a/extensions/typescript-language-features/src/tsServer/cachedResponse.ts +++ b/extensions/typescript-language-features/src/tsServer/cachedResponse.ts @@ -7,13 +7,13 @@ import * as vscode from 'vscode'; import * as Proto from '../protocol'; import { ServerResponse } from '../typescriptService'; -type Resolve = () => Promise>; +type Resolve = () => Promise>; /** * Caches a class of TS Server request based on document. */ export class CachedResponse { - private response?: Promise>; + private response?: Promise>; private version: number = -1; private document: string = ''; @@ -25,7 +25,7 @@ export class CachedResponse { public execute( document: vscode.TextDocument, resolve: Resolve - ): Promise> { + ): Promise> { if (this.response && this.matches(document)) { // Chain so that on cancellation we fall back to the next resolve return this.response = this.response.then(result => result.type === 'cancelled' ? resolve() : result); @@ -40,7 +40,7 @@ export class CachedResponse { private async reset( document: vscode.TextDocument, resolve: Resolve - ): Promise> { + ): Promise> { this.version = document.version; this.document = document.uri.toString(); return this.response = resolve(); diff --git a/extensions/typescript-language-features/src/tsServer/callbackMap.ts b/extensions/typescript-language-features/src/tsServer/callbackMap.ts index 897a587b924..7f96de57159 100644 --- a/extensions/typescript-language-features/src/tsServer/callbackMap.ts +++ b/extensions/typescript-language-features/src/tsServer/callbackMap.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as Proto from '../protocol'; -import { CancelledResponse, ServerResponse } from '../typescriptService'; +import { ServerResponse } from '../typescriptService'; export interface CallbackItem { readonly onSuccess: (value: R) => void; @@ -14,11 +14,11 @@ export interface CallbackItem { } export class CallbackMap { - private readonly _callbacks = new Map | undefined>>(); - private readonly _asyncCallbacks = new Map | undefined>>(); + private readonly _callbacks = new Map | undefined>>(); + private readonly _asyncCallbacks = new Map | undefined>>(); public destroy(cause: string): void { - const cancellation = new CancelledResponse(cause); + const cancellation = new ServerResponse.Cancelled(cause); for (const callback of this._callbacks.values()) { callback.onSuccess(cancellation); } @@ -29,7 +29,7 @@ export class CallbackMap { this._asyncCallbacks.clear(); } - public add(seq: number, callback: CallbackItem | undefined>, isAsync: boolean) { + public add(seq: number, callback: CallbackItem | undefined>, isAsync: boolean) { if (isAsync) { this._asyncCallbacks.set(seq, callback); } else { @@ -37,7 +37,7 @@ export class CallbackMap { } } - public fetch(seq: number): CallbackItem | undefined> | undefined { + public fetch(seq: number): CallbackItem | undefined> | undefined { const callback = this._callbacks.get(seq) || this._asyncCallbacks.get(seq); this.delete(seq); return callback; diff --git a/extensions/typescript-language-features/src/tsServer/server.ts b/extensions/typescript-language-features/src/tsServer/server.ts index b657449ee32..6c237aa538d 100644 --- a/extensions/typescript-language-features/src/tsServer/server.ts +++ b/extensions/typescript-language-features/src/tsServer/server.ts @@ -8,7 +8,7 @@ import * as fs from 'fs'; import * as path from 'path'; import * as vscode from 'vscode'; import * as Proto from '../protocol'; -import { CancelledResponse, NoContentResponse, ServerResponse } from '../typescriptService'; +import { ServerResponse } from '../typescriptService'; import API from '../utils/api'; import { TsServerLogLevel, TypeScriptServiceConfiguration } from '../utils/configuration'; import { Disposable } from '../utils/dispose'; @@ -299,7 +299,7 @@ export class TypeScriptServer extends Disposable { } finally { const callback = this.fetchCallback(seq); if (callback) { - callback.onSuccess(new CancelledResponse(`Cancelled request ${seq} - ${command}`)); + callback.onSuccess(new ServerResponse.Cancelled(`Cancelled request ${seq} - ${command}`)); } } } @@ -315,15 +315,15 @@ export class TypeScriptServer extends Disposable { callback.onSuccess(response); } else if (response.message === 'No content available.') { // Special case where response itself is successful but there is not any data to return. - callback.onSuccess(NoContentResponse); + callback.onSuccess(ServerResponse.NoContent); } else { callback.onError(new TypeScriptServerError(this._version, response)); } } public executeImpl(command: string, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: false, lowPriority?: boolean }): undefined; - public executeImpl(command: string, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise>; - public executeImpl(command: string, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise> | undefined { + public executeImpl(command: string, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise>; + public executeImpl(command: string, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise> | undefined { const request = this._requestQueue.createRequest(command, args); const requestInfo: RequestItem = { request, @@ -331,9 +331,9 @@ export class TypeScriptServer extends Disposable { isAsync: executeInfo.isAsync, queueingType: getQueueingType(command, executeInfo.lowPriority) }; - let result: Promise> | undefined; + let result: Promise> | undefined; if (executeInfo.expectsResult) { - result = new Promise>((resolve, reject) => { + result = new Promise>((resolve, reject) => { this._callbacks.add(request.seq, { onSuccess: resolve, onError: reject, startTime: Date.now(), isAsync: executeInfo.isAsync }, executeInfo.isAsync); if (executeInfo.token) { diff --git a/extensions/typescript-language-features/src/typescriptService.ts b/extensions/typescript-language-features/src/typescriptService.ts index 8f004c0e5eb..90ba448e9ed 100644 --- a/extensions/typescript-language-features/src/typescriptService.ts +++ b/extensions/typescript-language-features/src/typescriptService.ts @@ -11,19 +11,22 @@ import { TypeScriptServiceConfiguration } from './utils/configuration'; import Logger from './utils/logger'; import { PluginManager } from './utils/plugins'; -export class CancelledResponse { - public readonly type: 'cancelled' = 'cancelled'; +export namespace ServerResponse { - constructor( - public readonly reason: string - ) { } + export class Cancelled { + public readonly type = 'cancelled'; + + constructor( + public readonly reason: string + ) { } + } + + export const NoContent = new class { readonly type = 'noContent'; }; + export const LanguageServiceDisabled = new class { readonly type = 'languageServiceDisabled'; }; + + export type Response = T | Cancelled | typeof NoContent | typeof LanguageServiceDisabled; } -export const NoContentResponse = new class { readonly type = 'noContent'; }; -export const LanguageServiceDisabledContentResponse = new class { readonly type = 'languageServiceDisabled'; }; - -export type ServerResponse = T | CancelledResponse | typeof NoContentResponse | typeof LanguageServiceDisabledContentResponse; - export interface TypeScriptRequestTypes { 'applyCodeActionCommand': [Proto.ApplyCodeActionCommandRequestArgs, Proto.ApplyCodeActionCommandResponse]; 'completionEntryDetails': [Proto.CompletionDetailsRequestArgs, Proto.CompletionDetailsResponse]; @@ -103,7 +106,7 @@ export interface ITypeScriptServiceClient { args: TypeScriptRequestTypes[K][0], token: vscode.CancellationToken, lowPriority?: boolean - ): Promise>; + ): Promise>; executeWithoutWaitingForResponse(command: 'open', args: Proto.OpenRequestArgs): void; executeWithoutWaitingForResponse(command: 'close', args: Proto.FileRequestArgs): void; @@ -111,7 +114,7 @@ export interface ITypeScriptServiceClient { executeWithoutWaitingForResponse(command: 'compilerOptionsForInferredProjects', args: Proto.SetCompilerOptionsForInferredProjectsArgs): void; executeWithoutWaitingForResponse(command: 'reloadProjects', args: null): void; - executeAsync(command: 'geterr', args: Proto.GeterrRequestArgs, token: vscode.CancellationToken): Promise>; + executeAsync(command: 'geterr', args: Proto.GeterrRequestArgs, token: vscode.CancellationToken): Promise>; /** * Cancel on going geterr requests and re-queue them after `f` has been evaluated. diff --git a/extensions/typescript-language-features/src/typescriptServiceClient.ts b/extensions/typescript-language-features/src/typescriptServiceClient.ts index 799f24864c1..91353cd1055 100644 --- a/extensions/typescript-language-features/src/typescriptServiceClient.ts +++ b/extensions/typescript-language-features/src/typescriptServiceClient.ts @@ -11,7 +11,7 @@ import BufferSyncSupport from './features/bufferSyncSupport'; import { DiagnosticKind, DiagnosticsManager } from './features/diagnostics'; import * as Proto from './protocol'; import { TypeScriptServer, TypeScriptServerSpawner } from './tsServer/server'; -import { ITypeScriptServiceClient, ServerResponse, LanguageServiceDisabledContentResponse } from './typescriptService'; +import { ITypeScriptServiceClient, ServerResponse } from './typescriptService'; import API from './utils/api'; import { TsServerLogLevel, TypeScriptServiceConfiguration } from './utils/configuration'; import { Disposable } from './utils/dispose'; @@ -590,7 +590,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType return undefined; } - public execute(command: string, args: any, token: vscode.CancellationToken, lowPriority?: boolean): Promise> { + public execute(command: string, args: any, token: vscode.CancellationToken, lowPriority?: boolean): Promise> { return this.executeImpl(command, args, { isAsync: false, token, @@ -607,7 +607,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType }); } - public executeAsync(command: string, args: Proto.GeterrRequestArgs, token: vscode.CancellationToken): Promise> { + public executeAsync(command: string, args: Proto.GeterrRequestArgs, token: vscode.CancellationToken): Promise> { return this.executeImpl(command, args, { isAsync: true, token, @@ -616,8 +616,8 @@ export default class TypeScriptServiceClient extends Disposable implements IType } private executeImpl(command: string, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: false, lowPriority?: boolean }): undefined; - private executeImpl(command: string, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise>; - private executeImpl(command: string, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise> | undefined { + private executeImpl(command: string, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise>; + private executeImpl(command: string, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise> | undefined { const runningServerState = this.service(); if (!runningServerState.langaugeServiceEnabled) { @@ -634,7 +634,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType 'reloadProjects', ]; if (nonSemanticCommands.indexOf(command) === -1) { - return Promise.resolve(LanguageServiceDisabledContentResponse); + return Promise.resolve(ServerResponse.LanguageServiceDisabled); } } From a70b1317a183999d0c8205fcacdb22518014bbce Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 25 Jan 2019 17:10:10 -0800 Subject: [PATCH 240/274] Removing languageServiceEnabled check After further discussion with the TS team, we determined that we do not need to stop sending semantic commands when the language service disables itself --- .../src/typescriptService.ts | 3 +-- .../src/typescriptServiceClient.ts | 19 ------------------- 2 files changed, 1 insertion(+), 21 deletions(-) diff --git a/extensions/typescript-language-features/src/typescriptService.ts b/extensions/typescript-language-features/src/typescriptService.ts index 90ba448e9ed..14a4d895800 100644 --- a/extensions/typescript-language-features/src/typescriptService.ts +++ b/extensions/typescript-language-features/src/typescriptService.ts @@ -22,9 +22,8 @@ export namespace ServerResponse { } export const NoContent = new class { readonly type = 'noContent'; }; - export const LanguageServiceDisabled = new class { readonly type = 'languageServiceDisabled'; }; - export type Response = T | Cancelled | typeof NoContent | typeof LanguageServiceDisabled; + export type Response = T | Cancelled | typeof NoContent; } export interface TypeScriptRequestTypes { diff --git a/extensions/typescript-language-features/src/typescriptServiceClient.ts b/extensions/typescript-language-features/src/typescriptServiceClient.ts index 91353cd1055..4328948b23e 100644 --- a/extensions/typescript-language-features/src/typescriptServiceClient.ts +++ b/extensions/typescript-language-features/src/typescriptServiceClient.ts @@ -619,25 +619,6 @@ export default class TypeScriptServiceClient extends Disposable implements IType private executeImpl(command: string, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise>; private executeImpl(command: string, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise> | undefined { const runningServerState = this.service(); - - if (!runningServerState.langaugeServiceEnabled) { - const nonSemanticCommands: string[] = [ - 'change', - 'close', - 'compilerOptionsForInferredProjects', - 'configure', - 'format', - 'formatonkey', - 'getOutliningSpans', - 'open', - 'projectInfo', - 'reloadProjects', - ]; - if (nonSemanticCommands.indexOf(command) === -1) { - return Promise.resolve(ServerResponse.LanguageServiceDisabled); - } - } - return runningServerState.server.executeImpl(command, args, executeInfo); } From 70f4460005960451fbbd89cb2ce661bfc7912a77 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 25 Jan 2019 17:26:19 -0800 Subject: [PATCH 241/274] Extract getCodeActionProviders --- .../editor/contrib/codeAction/codeAction.ts | 51 ++++++++++--------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/src/vs/editor/contrib/codeAction/codeAction.ts b/src/vs/editor/contrib/codeAction/codeAction.ts index 5282fbb9dc7..e6015b5d02b 100644 --- a/src/vs/editor/contrib/codeAction/codeAction.ts +++ b/src/vs/editor/contrib/codeAction/codeAction.ts @@ -13,7 +13,7 @@ import { Selection } from 'vs/editor/common/core/selection'; import { ITextModel } from 'vs/editor/common/model'; import { CodeAction, CodeActionContext, CodeActionProviderRegistry, CodeActionTrigger as CodeActionTriggerKind } from 'vs/editor/common/modes'; import { IModelService } from 'vs/editor/common/services/modelService'; -import { CodeActionKind, CodeActionTrigger, filtersAction, mayIncludeActionsOfKind } from './codeActionTrigger'; +import { CodeActionKind, CodeActionTrigger, filtersAction, mayIncludeActionsOfKind, CodeActionFilter } from './codeActionTrigger'; export function getCodeActions( model: ITextModel, @@ -32,35 +32,40 @@ export function getCodeActions( rangeOrSelection = model.getFullModelRange(); } - const promises = CodeActionProviderRegistry.all(model) - // Avoid calling providers that we know will not return code actions of interest + const promises = getCodeActionProviders(model, filter).map(provider => { + return Promise.resolve(provider.provideCodeActions(model, rangeOrSelection, codeActionContext, token)).then(providedCodeActions => { + if (!Array.isArray(providedCodeActions)) { + return []; + } + return providedCodeActions.filter(action => action && filtersAction(filter, action)); + }, (err): CodeAction[] => { + if (isPromiseCanceledError(err)) { + throw err; + } + + onUnexpectedExternalError(err); + return []; + }); + }); + + return Promise.all(promises) + .then(flatten) + .then(allCodeActions => mergeSort(allCodeActions, codeActionsComparator)); +} + +function getCodeActionProviders( + model: ITextModel, + filter: CodeActionFilter +) { + return CodeActionProviderRegistry.all(model) + // Don't include providers that we know will not return code actions of interest .filter(provider => { if (!provider.providedCodeActionKinds) { // We don't know what type of actions this provider will return. return true; } - return provider.providedCodeActionKinds.some(kind => mayIncludeActionsOfKind(filter, new CodeActionKind(kind))); - }) - .map(support => { - return Promise.resolve(support.provideCodeActions(model, rangeOrSelection, codeActionContext, token)).then(providedCodeActions => { - if (!Array.isArray(providedCodeActions)) { - return []; - } - return providedCodeActions.filter(action => action && filtersAction(filter, action)); - }, (err): CodeAction[] => { - if (isPromiseCanceledError(err)) { - throw err; - } - - onUnexpectedExternalError(err); - return []; - }); }); - - return Promise.all(promises) - .then(flatten) - .then(allCodeActions => mergeSort(allCodeActions, codeActionsComparator)); } function codeActionsComparator(a: CodeAction, b: CodeAction): number { From 61efc1d7d19a8a81ecc4a40a9c073efb0021ce4e Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 28 Jan 2019 13:40:00 -0800 Subject: [PATCH 242/274] Add webview specific copy/paste, undo/redo commands Fixes #65452 Adds copy/paste, undo/redo commands that are specific to the webview. This is very similar to the approach we already use to implement 'select all' --- .../electron-browser/baseWebviewEditor.ts | 38 ++++++++---- .../electron-browser/webview.contribution.ts | 60 ++++++++++++++++--- .../electron-browser/webviewCommands.ts | 55 +++++++++++++++++ .../electron-browser/webviewElement.ts | 20 +++++++ 4 files changed, 156 insertions(+), 17 deletions(-) diff --git a/src/vs/workbench/parts/webview/electron-browser/baseWebviewEditor.ts b/src/vs/workbench/parts/webview/electron-browser/baseWebviewEditor.ts index 7d3a3a77fbc..0851ec06807 100644 --- a/src/vs/workbench/parts/webview/electron-browser/baseWebviewEditor.ts +++ b/src/vs/workbench/parts/webview/electron-browser/baseWebviewEditor.ts @@ -55,26 +55,44 @@ export abstract class BaseWebviewEditor extends BaseEditor { } public reload() { - if (this._webview) { - this._webview.reload(); - } + this.withWebviewElement(webview => webview.reload()); } public layout(dimension: Dimension): void { - if (this._webview) { - this._webview.layout(); - } + this.withWebviewElement(webview => webview.layout()); } public focus(): void { - if (this._webview) { - this._webview.focus(); - } + this.withWebviewElement(webview => webview.focus()); } public selectAll(): void { + this.withWebviewElement(webview => webview.selectAll()); + } + + public copy(): void { + this.withWebviewElement(webview => webview.copy()); + } + + public paste(): void { + this.withWebviewElement(webview => webview.paste()); + } + + public cut(): void { + this.withWebviewElement(webview => webview.cut()); + } + + public undo(): void { + this.withWebviewElement(webview => webview.undo()); + } + + public redo(): void { + this.withWebviewElement(webview => webview.redo()); + } + + private withWebviewElement(f: (element: WebviewElement) => void): void { if (this._webview) { - this._webview.selectAll(); + f(this._webview); } } } diff --git a/src/vs/workbench/parts/webview/electron-browser/webview.contribution.ts b/src/vs/workbench/parts/webview/electron-browser/webview.contribution.ts index 8adfb937562..7a686405379 100644 --- a/src/vs/workbench/parts/webview/electron-browser/webview.contribution.ts +++ b/src/vs/workbench/parts/webview/electron-browser/webview.contribution.ts @@ -16,7 +16,7 @@ import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/wor import { Extensions as EditorInputExtensions, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor'; import { WebviewEditorInputFactory } from 'vs/workbench/parts/webview/electron-browser/webviewEditorInputFactory'; import { KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE } from './baseWebviewEditor'; -import { HideWebViewEditorFindCommand, OpenWebviewDeveloperToolsAction, ReloadWebviewAction, ShowWebViewEditorFindWidgetCommand, SelectAllWebviewEditorCommand } from './webviewCommands'; +import { HideWebViewEditorFindCommand, OpenWebviewDeveloperToolsAction, ReloadWebviewAction, ShowWebViewEditorFindWidgetCommand, SelectAllWebviewEditorCommand, CopyWebviewEditorCommand, PasteWebviewEditorCommand, CutWebviewEditorCommand, UndoWebviewEditorCommand, RedoWebviewEditorCommand } from './webviewCommands'; import { WebviewEditor } from './webviewEditor'; import { WebviewEditorInput } from './webviewEditorInput'; import { IWebviewEditorService, WebviewEditorService } from './webviewEditorService'; @@ -52,7 +52,7 @@ export function registerWebViewCommands(editorId: string): void { }); showNextFindWidgetCommand.register(); - const hideCommand = new HideWebViewEditorFindCommand({ + (new HideWebViewEditorFindCommand({ id: HideWebViewEditorFindCommand.ID, precondition: ContextKeyExpr.and( contextKeyExpr, @@ -61,18 +61,64 @@ export function registerWebViewCommands(editorId: string): void { primary: KeyCode.Escape, weight: KeybindingWeight.EditorContrib } - }); - hideCommand.register(); + })).register(); - const selectAllCommand = new SelectAllWebviewEditorCommand({ + (new SelectAllWebviewEditorCommand({ id: SelectAllWebviewEditorCommand.ID, precondition: ContextKeyExpr.and(contextKeyExpr, ContextKeyExpr.not(InputFocusedContextKey)), kbOpts: { primary: KeyMod.CtrlCmd | KeyCode.KEY_A, weight: KeybindingWeight.EditorContrib } - }); - selectAllCommand.register(); + })).register(); + + (new CopyWebviewEditorCommand({ + id: CopyWebviewEditorCommand.ID, + precondition: ContextKeyExpr.and(contextKeyExpr, ContextKeyExpr.not(InputFocusedContextKey)), + kbOpts: { + primary: KeyMod.CtrlCmd | KeyCode.KEY_C, + weight: KeybindingWeight.EditorContrib + } + })).register(); + + (new PasteWebviewEditorCommand({ + id: PasteWebviewEditorCommand.ID, + precondition: ContextKeyExpr.and(contextKeyExpr, ContextKeyExpr.not(InputFocusedContextKey)), + kbOpts: { + primary: KeyMod.CtrlCmd | KeyCode.KEY_V, + weight: KeybindingWeight.EditorContrib + } + })).register(); + + + (new CutWebviewEditorCommand({ + id: CutWebviewEditorCommand.ID, + precondition: ContextKeyExpr.and(contextKeyExpr, ContextKeyExpr.not(InputFocusedContextKey)), + kbOpts: { + primary: KeyMod.CtrlCmd | KeyCode.KEY_X, + weight: KeybindingWeight.EditorContrib + } + })).register(); + + (new UndoWebviewEditorCommand({ + id: UndoWebviewEditorCommand.ID, + precondition: ContextKeyExpr.and(contextKeyExpr, ContextKeyExpr.not(InputFocusedContextKey)), + kbOpts: { + primary: KeyMod.CtrlCmd | KeyCode.KEY_Z, + weight: KeybindingWeight.EditorContrib + } + })).register(); + + (new RedoWebviewEditorCommand({ + id: RedoWebviewEditorCommand.ID, + precondition: ContextKeyExpr.and(contextKeyExpr, ContextKeyExpr.not(InputFocusedContextKey)), + kbOpts: { + primary: KeyMod.CtrlCmd | KeyCode.KEY_Y, + secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Z], + mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Z }, + weight: KeybindingWeight.EditorContrib + } + })).register(); } registerWebViewCommands(WebviewEditor.ID); diff --git a/src/vs/workbench/parts/webview/electron-browser/webviewCommands.ts b/src/vs/workbench/parts/webview/electron-browser/webviewCommands.ts index 4f42f3bf403..b91a3fa1224 100644 --- a/src/vs/workbench/parts/webview/electron-browser/webviewCommands.ts +++ b/src/vs/workbench/parts/webview/electron-browser/webviewCommands.ts @@ -43,6 +43,61 @@ export class SelectAllWebviewEditorCommand extends Command { } } +export class CopyWebviewEditorCommand extends Command { + public static readonly ID = 'editor.action.webvieweditor.copy'; + + public runCommand(accessor: ServicesAccessor, args: any): void { + const webViewEditor = getActiveWebviewEditor(accessor); + if (webViewEditor) { + webViewEditor.copy(); + } + } +} + +export class PasteWebviewEditorCommand extends Command { + public static readonly ID = 'editor.action.webvieweditor.paste'; + + public runCommand(accessor: ServicesAccessor, args: any): void { + const webViewEditor = getActiveWebviewEditor(accessor); + if (webViewEditor) { + webViewEditor.paste(); + } + } +} + +export class CutWebviewEditorCommand extends Command { + public static readonly ID = 'editor.action.webvieweditor.cut'; + + public runCommand(accessor: ServicesAccessor, args: any): void { + const webViewEditor = getActiveWebviewEditor(accessor); + if (webViewEditor) { + webViewEditor.cut(); + } + } +} + +export class UndoWebviewEditorCommand extends Command { + public static readonly ID = 'editor.action.webvieweditor.undo'; + + public runCommand(accessor: ServicesAccessor, args: any): void { + const webViewEditor = getActiveWebviewEditor(accessor); + if (webViewEditor) { + webViewEditor.undo(); + } + } +} + +export class RedoWebviewEditorCommand extends Command { + public static readonly ID = 'editor.action.webvieweditor.redo'; + + public runCommand(accessor: ServicesAccessor, args: any): void { + const webViewEditor = getActiveWebviewEditor(accessor); + if (webViewEditor) { + webViewEditor.redo(); + } + } +} + export class OpenWebviewDeveloperToolsAction extends Action { static readonly ID = 'workbench.action.webview.openDeveloperTools'; static readonly LABEL = nls.localize('openToolsLabel', "Open Webview Developer Tools"); diff --git a/src/vs/workbench/parts/webview/electron-browser/webviewElement.ts b/src/vs/workbench/parts/webview/electron-browser/webviewElement.ts index b6a9ea29e72..57af438d221 100644 --- a/src/vs/workbench/parts/webview/electron-browser/webviewElement.ts +++ b/src/vs/workbench/parts/webview/electron-browser/webviewElement.ts @@ -574,6 +574,26 @@ export class WebviewElement extends Disposable { public selectAll() { this._webview.selectAll(); } + + public copy() { + this._webview.copy(); + } + + public paste() { + this._webview.paste(); + } + + public cut() { + this._webview.cut(); + } + + public undo() { + this._webview.undo(); + } + + public redo() { + this._webview.redo(); + } } From 2c1040da1f086a1c1feafc8bade2bae8b246df31 Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Mon, 28 Jan 2019 13:51:06 -0800 Subject: [PATCH 243/274] Update Octicons and license attributions --- cgmanifest.json | 14 +++++++++++++- .../ui/octiconLabel/octicons/octicons.css | 5 +++-- .../ui/octiconLabel/octicons/octicons.svg | 3 +++ .../ui/octiconLabel/octicons/octicons.ttf | Bin 36668 -> 36832 bytes 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/cgmanifest.json b/cgmanifest.json index 37d52feb36c..b7e74e39456 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -111,12 +111,24 @@ "git": { "name": "vscode-octicons-font", "repositoryUrl": "https://github.com/Microsoft/vscode-octicons-font", - "commitHash": "174697a8d28a65dc6600c44d239bd75f7d157c71" + "commitHash": "5095860bb929919670646e2dfa0ee47d9b93bcb9" } }, "license": "MIT", "version": "1.0.0" }, + { + "component": { + "type": "git", + "git": { + "name": "octicons", + "repositoryUrl": "https://github.com/primer/octicons", + "commitHash": "d120bf97bc9a12fb415f69fedaf31fe58427ca56" + } + }, + "license": "MIT", + "version": "8.3.0" + }, { "component": { "type": "npm", diff --git a/src/vs/base/browser/ui/octiconLabel/octicons/octicons.css b/src/vs/base/browser/ui/octiconLabel/octicons/octicons.css index f4744af5bb4..d24cc0b5258 100644 --- a/src/vs/base/browser/ui/octiconLabel/octicons/octicons.css +++ b/src/vs/base/browser/ui/octiconLabel/octicons/octicons.css @@ -1,7 +1,7 @@ @font-face { font-family: "octicons"; - src: url("./octicons.ttf?7b4c09277b7efac707b034fae8e52ed0") format("truetype"), -url("./octicons.svg?7b4c09277b7efac707b034fae8e52ed0#octicons") format("svg"); + src: url("./octicons.ttf?4cd2299755e93a2430ba5703f4476584") format("truetype"), +url("./octicons.svg?4cd2299755e93a2430ba5703f4476584#octicons") format("svg"); } .octicon, .mega-octicon { @@ -240,3 +240,4 @@ url("./octicons.svg?7b4c09277b7efac707b034fae8e52ed0#octicons") format("svg"); .octicon-fold-up:before { content: "\f105" } .octicon-github-action:before { content: "\f106" } .octicon-play:before { content: "\f107" } +.octicon-request-changes:before { content: "\f108" } diff --git a/src/vs/base/browser/ui/octiconLabel/octicons/octicons.svg b/src/vs/base/browser/ui/octiconLabel/octicons/octicons.svg index 4309475d790..4581795255c 100644 --- a/src/vs/base/browser/ui/octiconLabel/octicons/octicons.svg +++ b/src/vs/base/browser/ui/octiconLabel/octicons/octicons.svg @@ -445,6 +445,9 @@ + diff --git a/src/vs/base/browser/ui/octiconLabel/octicons/octicons.ttf b/src/vs/base/browser/ui/octiconLabel/octicons/octicons.ttf index 747e34216cac83a6e77e6b7971a532a5c47016e2..12b561cd47a71c8e3acfd42b45752b42082666e8 100644 GIT binary patch delta 1251 zcmXw%e@s(X6vw~!wFPWxOZgQj=q%4xX0+|vzS77nnVZuHi;Ft9fRR)z;Gj^T7`9o_ zkts7^Y3R}t_9Re#OQ0?HX6`Ce7+Ta{mGzrGEm-9SQhag6wBjPIeaM zudVY1{AYTN-=+fZrO<`UV0}|aHM@fYr}BZMiI1klqDe5yCF&*KRjz;j4`>t%IH>|&PfpBURv>clY9+pU?q`GHY9Mna%6SxQ#TZuP&~CZ(ypcl=Rzqg z^1qd3EGe>AWnh|87Gy!Hum`t!Fk$_kA#BDLwBi7Yu@kSL31!%iQVhV1b}H44mvI`S z=)=eO1Z!{(Z^Mck$V33O&>#UysBj$Hu^ZQ5!87RMiwQY7W4MY+wBZu+a2VC7!uuG9 zh%@+0xn_)Uc(^LQ3DU2MGx$da0IW@LlWuzImkjbuA>A+ z@S-2DB7z3IgES1I9_z6UZ{P}!Vk-=I7poCM1x9cg8;}ewCUBM%RKSfF@e+2W-~>1} z;XFRW=lB9QBh!f{W{USazGyZqei>2kOugz?HpG`!b+7?`T5V(9d{QlL83m#a z8Bz*SrwnO^s7r>_LsXI>9T9cQ;2bwvW>_0!7+oaFKg;A{*ofb%m^l z_vzZ$b)KiUW%pA&UY#Iy5uKDlgukh`v0>h$FJxEvtiD`3LXkNcT;^L-Y}yGr{UU?2 z{8-Aq-M`To$03~&J1RqpBNmn+?GZaBLtQ|uRfhV3*l`)^3}Po_aFZJi;Yhb(9osc% zg>|#_fVJQH$mX@p*z@gm_I~?M&+T_)Ib4o|j#0-WXQ^|{Iqyn$gjc<%rt^9xZF;YBQwL0o*anIUF~k|;SPXPQYenT?8?Y_r+8ym9W1(Ok3?W{;N-hr{0Zw>-@} z!k_WzlTM>~ZqA%7G(PDFhlPeIIbQ4~nIbcRhgobgGJdj=3G7ZJzx@YQioVg`(Clvt SZK|mD)m8bMBL95&0{ag?GElhy delta 1109 zcmXw&YfzL`7=WL%T-{|C5Hehm)J?QTcY$TsoyZ&u(UESGMI=qILtH8@i)>}3p`1%bxqV0){-8s3wJ#DN2oj&&0#nJa8qZ~3M?`N2Q2#LP&BspsDP z`2TV4_qk&8OpH(76EQ`z@Z_>Wl!NvAFX=2iexf89D(JULb6Kk5$ z|HhW7givW*sv)JBo@AUR-YO)GQ#7)I4U}@09Xw1b@35F4m7L);_mhN`OLU9a7`!|}Ay2TI z)f}gvK|)*zeUY$KEgg$Wm*)y(R~d*svil6gW6HeW)X5Lc`&F;*FuT-YeckL>I}G7G z1CdAAZ6F#6=NpJn!X5)rOW13`F}=j%Q0H}_rB#jUn-U+?zgh;Z=cHa{z=-Zma;PEw zY0}2!7bPd37DW^jmOm|u$R=E4K$p(34yvPi%37c<>vWsLdO|8~1`Oyzn?v>KT3dlS zqWf*FYD}+8b}j6Y`XvKVOZch*<2sP+Qm1rpa)COle@HI3o{`G;2AtNLQe4((-;V}# z>pdwYMG?6UQA9G4od%+t$lC@Yo=A&<908GC267lgb{lX(uS(q+8cfYr#n)H=?aXpk zI7gkcu6?eV9A{2NPG@dr?o{p{dCT(Z@ Date: Mon, 28 Jan 2019 13:55:55 -0800 Subject: [PATCH 244/274] Fix CSS imports --- extensions/css-language-features/.vscode/launch.json | 6 ++---- .../css-language-features/server/src/customData.ts | 9 ++++++--- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/extensions/css-language-features/.vscode/launch.json b/extensions/css-language-features/.vscode/launch.json index d6393141c5d..4f7166e6dce 100644 --- a/extensions/css-language-features/.vscode/launch.json +++ b/extensions/css-language-features/.vscode/launch.json @@ -23,8 +23,7 @@ "outFiles": [ "${workspaceFolder}/client/out/**/*.js" ], - "smartStep": true, - "preLaunchTask": "npm: compile" + "smartStep": true }, { "name": "Launch Tests", @@ -39,8 +38,7 @@ "sourceMaps": true, "outFiles": [ "${workspaceFolder}/client/out/test/**/*.js" - ], - "preLaunchTask": "npm: compile" + ] }, { "name": "Attach Language Server", diff --git a/extensions/css-language-features/server/src/customData.ts b/extensions/css-language-features/server/src/customData.ts index 3b253648d42..b5cbdc27c7a 100644 --- a/extensions/css-language-features/server/src/customData.ts +++ b/extensions/css-language-features/server/src/customData.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CSSData, ICSSDataProvider } from 'vscode-css-languageservice'; +import { CSSDataV1, ICSSDataProvider } from 'vscode-css-languageservice'; import * as fs from 'fs'; export function getDataProviders(dataPaths: string[]): ICSSDataProvider[] { @@ -29,16 +29,19 @@ export function getDataProviders(dataPaths: string[]): ICSSDataProvider[] { return providers; } -function parseCSSData(source: string): CSSData { +function parseCSSData(source: string): CSSDataV1 { let rawData: any; try { rawData = JSON.parse(source); } catch (err) { - return {}; + return { + version: 1 + }; } return { + version: 1, properties: rawData.properties || [], atDirectives: rawData.atdirectives || [], pseudoClasses: rawData.pseudoclasses || [], From 85119f3faf4f0dedbd5185ddca717418db734da7 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 28 Jan 2019 22:59:25 +0100 Subject: [PATCH 245/274] Language features not supported for ".pyi" python type files. Fixes #66652 --- extensions/python/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/python/package.json b/extensions/python/package.json index 48151bd6245..c81942af764 100644 --- a/extensions/python/package.json +++ b/extensions/python/package.json @@ -10,7 +10,7 @@ "contributes": { "languages": [{ "id": "python", - "extensions": [ ".py", ".rpy", ".pyw", ".cpy", ".gyp", ".gypi", ".snakefile", ".smk"], + "extensions": [ ".py", ".rpy", ".pyw", ".cpy", ".gyp", ".gypi", ".snakefile", ".smk", ".pyi"], "aliases": [ "Python", "py" ], "firstLine": "^#!\\s*/.*\\bpython[0-9.-]*\\b", "configuration": "./language-configuration.json" From 7640e80f0b34af5892b029f93f76dd6376376eaf Mon Sep 17 00:00:00 2001 From: SteVen Batten <6561887+sbatten@users.noreply.github.com> Date: Mon, 28 Jan 2019 14:30:45 -0800 Subject: [PATCH 246/274] Sbatten/hideable views (#67291) Use hideable views for workbench parts --- src/vs/base/browser/ui/grid/grid.ts | 63 ++++- .../workbench/electron-browser/workbench.ts | 246 ++++++++---------- 2 files changed, 164 insertions(+), 145 deletions(-) diff --git a/src/vs/base/browser/ui/grid/grid.ts b/src/vs/base/browser/ui/grid/grid.ts index 39bbdeb9181..5bbe5d9c30f 100644 --- a/src/vs/base/browser/ui/grid/grid.ts +++ b/src/vs/base/browser/ui/grid/grid.ts @@ -8,7 +8,9 @@ import { Orientation } from 'vs/base/browser/ui/sash/sash'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { tail2 as tail, equals } from 'vs/base/common/arrays'; import { orthogonal, IView, GridView, Sizing as GridViewSizing, Box, IGridViewStyles } from './gridview'; -import { Event } from 'vs/base/common/event'; +import { Event, Emitter } from 'vs/base/common/event'; +import { $ } from 'vs/base/browser/dom'; +import { LayoutPriority } from 'vs/base/browser/ui/splitview/splitview'; export { Orientation } from './gridview'; @@ -647,3 +649,62 @@ export function createSerializedGrid(gridDescriptor: GridDescriptor): ISerialize height: height || 1 }; } + +export class View implements IView { + + readonly element = $('.grid-view-view'); + + private visible = false; + private width: number | undefined; + private height: number | undefined; + private orientation: Orientation = Orientation.HORIZONTAL; + + get minimumWidth(): number { return this.visible ? this.view.minimumWidth : 0; } + get maximumWidth(): number { return this.visible ? this.view.maximumWidth : (this.orientation === Orientation.HORIZONTAL ? 0 : Number.POSITIVE_INFINITY); } + get minimumHeight(): number { return this.visible ? this.view.minimumHeight : 0; } + get maximumHeight(): number { return this.visible ? this.view.maximumHeight : (this.orientation === Orientation.VERTICAL ? 0 : Number.POSITIVE_INFINITY); } + + private onDidChangeVisibility = new Emitter<{ width: number; height: number; } | undefined>(); + readonly onDidChange: Event<{ width: number; height: number; } | undefined>; + + get priority(): LayoutPriority | undefined { return this.view.priority; } + get snapSize(): number | undefined { return this.visible ? this.view.snapSize : undefined; } + + constructor(private view: IView) { + this.show(); + this.onDidChange = Event.any(this.onDidChangeVisibility.event, Event.filter(view.onDidChange, () => this.visible)); + } + + show(): void { + if (this.visible) { + return; + } + + this.visible = true; + + this.element.appendChild(this.view.element); + this.onDidChangeVisibility.fire(typeof this.width === 'number' ? { width: this.width, height: this.height! } : undefined); + } + + hide(): void { + if (!this.visible) { + return; + } + + this.visible = false; + + this.element.removeChild(this.view.element); + this.onDidChangeVisibility.fire(undefined); + } + + layout(width: number, height: number, orientation: Orientation): void { + if (!this.visible) { + return; + } + + this.view.layout(width, height, orientation); + this.width = width; + this.height = height; + this.orientation = orientation; + } +} \ No newline at end of file diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts index 929dda515de..7cc7eab56b1 100644 --- a/src/vs/workbench/electron-browser/workbench.ts +++ b/src/vs/workbench/electron-browser/workbench.ts @@ -113,7 +113,7 @@ import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/work import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; import { FileDialogService } from 'vs/workbench/services/dialogs/electron-browser/dialogService'; import { LogStorageAction } from 'vs/platform/storage/node/storageService'; -import { Sizing, Direction, SerializableGrid, ISerializedGrid } from 'vs/base/browser/ui/grid/grid'; +import { Sizing, Direction, Grid, View } from 'vs/base/browser/ui/grid/grid'; import { IEditor } from 'vs/editor/common/editorCommon'; import { WorkbenchLayout } from 'vs/workbench/browser/layout'; @@ -143,8 +143,6 @@ type FontAliasingOption = 'default' | 'antialiased' | 'none' | 'auto'; const fontAliasingValues: FontAliasingOption[] = ['antialiased', 'none', 'auto']; -type WorkbenchView = StatusbarPart | TitlebarPart | SidebarPart | EditorPart | ActivitybarPart | PanelPart; - const Identifiers = { WORKBENCH_CONTAINER: 'workbench.main.container', TITLEBAR_PART: 'workbench.parts.titlebar', @@ -180,7 +178,6 @@ interface IWorkbenchUIState { export class Workbench extends Disposable implements IPartService { - private static readonly workbenchGridUIStateStorageKey = 'workbench.layout.state'; private static readonly sidebarHiddenStorageKey = 'workbench.sidebar.hidden'; private static readonly menubarVisibilityConfigurationKey = 'window.menuBarVisibility'; private static readonly panelHiddenStorageKey = 'workbench.panel.hidden'; @@ -211,7 +208,7 @@ export class Workbench extends Disposable implements IPartService { private fileService: IFileService; private quickInput: QuickInputService; - private workbenchGrid: SerializableGrid | WorkbenchLayout; + private workbenchGrid: Grid | WorkbenchLayout; private titlebarPart: TitlebarPart; private activitybarPart: ActivitybarPart; @@ -219,6 +216,14 @@ export class Workbench extends Disposable implements IPartService { private panelPart: PanelPart; private editorPart: EditorPart; private statusbarPart: StatusbarPart; + + private titlebarPartView: View; + private activitybarPartView: View; + private sidebarPartView: View; + private panelPartView: View; + private editorPartView: View; + private statusbarPartView: View; + private quickOpen: QuickOpenController; private notificationsCenter: NotificationsCenter; private notificationsToasts: NotificationsToasts; @@ -961,14 +966,14 @@ export class Workbench extends Disposable implements IPartService { } private saveLastPanelDimension(): void { - if (!(this.workbenchGrid instanceof SerializableGrid)) { + if (!(this.workbenchGrid instanceof Grid)) { return; } if (this.panelPosition === Position.BOTTOM) { - this.uiState.lastPanelHeight = this.workbenchGrid.getViewSize(this.panelPart); + this.uiState.lastPanelHeight = this.workbenchGrid.getViewSize(this.panelPartView); } else { - this.uiState.lastPanelWidth = this.workbenchGrid.getViewSize(this.panelPart); + this.uiState.lastPanelWidth = this.workbenchGrid.getViewSize(this.panelPartView); } } @@ -988,7 +993,7 @@ export class Workbench extends Disposable implements IPartService { // Layout if (!skipLayout) { - if (this.workbenchGrid instanceof SerializableGrid) { + if (this.workbenchGrid instanceof Grid) { this.layout(); } else { this.workbenchGrid.layout(); @@ -1010,27 +1015,15 @@ export class Workbench extends Disposable implements IPartService { private createWorkbenchLayout(): void { if (this.configurationService.getValue('workbench.useExperimentalGridLayout')) { - const serializedWorkbenchGridString = this.storageService.get(Workbench.workbenchGridUIStateStorageKey, StorageScope.GLOBAL, undefined); + // Create view wrappers for all parts + this.titlebarPartView = new View(this.titlebarPart); + this.sidebarPartView = new View(this.sidebarPart); + this.activitybarPartView = new View(this.activitybarPart); + this.editorPartView = new View(this.editorPart); + this.panelPartView = new View(this.panelPart); + this.statusbarPartView = new View(this.statusbarPart); - if (serializedWorkbenchGridString) { - const serializedWorkbenchGrid = JSON.parse(serializedWorkbenchGridString) as ISerializedGrid; - this.workbenchGrid = SerializableGrid.deserialize(serializedWorkbenchGrid, { - fromJSON: (serializedView: { type: Parts }): WorkbenchView => { - switch (serializedView.type) { - case Parts.ACTIVITYBAR_PART: return this.activitybarPart; - case Parts.EDITOR_PART: return this.editorPart; - case Parts.PANEL_PART: return this.panelPart; - case Parts.SIDEBAR_PART: return this.sidebarPart; - case Parts.STATUSBAR_PART: return this.statusbarPart; - case Parts.TITLEBAR_PART: return this.titlebarPart; - } - - return null; - } - }); - } else { - this.workbenchGrid = new SerializableGrid(this.editorPart, { proportionalLayout: false }); - } + this.workbenchGrid = new Grid(this.editorPartView, { proportionalLayout: false }); this.workbench.prepend(this.workbenchGrid.element); this.layout(); @@ -1196,10 +1189,6 @@ export class Workbench extends Disposable implements IPartService { this.toggleZenMode(true); } } - - if (this.workbenchGrid instanceof SerializableGrid) { - this.storageService.store(Workbench.workbenchGridUIStateStorageKey, JSON.stringify(this.workbenchGrid.serialize()), StorageScope.GLOBAL); - } } dispose(): void { @@ -1287,8 +1276,8 @@ export class Workbench extends Disposable implements IPartService { getTitleBarOffset(): number { let offset = 0; if (this.isVisible(Parts.TITLEBAR_PART)) { - if (this.workbenchGrid instanceof SerializableGrid) { - offset = this.gridHasView(this.titlebarPart) ? this.workbenchGrid.getViewSize2(this.titlebarPart).height : 0; + if (this.workbenchGrid instanceof Grid) { + offset = this.gridHasView(this.titlebarPartView) ? this.workbenchGrid.getViewSize2(this.titlebarPartView).height : 0; } else { offset = this.workbenchGrid.partLayoutInfo.titlebar.height; } @@ -1390,8 +1379,8 @@ export class Workbench extends Disposable implements IPartService { } } - private gridHasView(view: WorkbenchView): boolean { - if (!(this.workbenchGrid instanceof SerializableGrid)) { + private gridHasView(view: View): boolean { + if (!(this.workbenchGrid instanceof Grid)) { return false; } @@ -1404,123 +1393,90 @@ export class Workbench extends Disposable implements IPartService { } private updateGrid(): void { - if (!(this.workbenchGrid instanceof SerializableGrid)) { + if (!(this.workbenchGrid instanceof Grid)) { return; } - let panelInGrid = this.gridHasView(this.panelPart); - let sidebarInGrid = this.gridHasView(this.sidebarPart); - let activityBarInGrid = this.gridHasView(this.activitybarPart); - let statusBarInGrid = this.gridHasView(this.statusbarPart); - let titlebarInGrid = this.gridHasView(this.titlebarPart); + let panelInGrid = this.gridHasView(this.panelPartView); + let sidebarInGrid = this.gridHasView(this.sidebarPartView); + let activityBarInGrid = this.gridHasView(this.activitybarPartView); + let statusBarInGrid = this.gridHasView(this.statusbarPartView); + let titlebarInGrid = this.gridHasView(this.titlebarPartView); - // Remove hidden parts - if (this.panelHidden && panelInGrid) { - this.saveLastPanelDimension(); - this.workbenchGrid.removeView(this.panelPart); - panelInGrid = false; + // Hide parts + if (this.panelHidden) { + this.panelPartView.hide(); } - if (this.statusBarHidden && statusBarInGrid) { - this.workbenchGrid.removeView(this.statusbarPart); - statusBarInGrid = false; + if (this.statusBarHidden) { + this.statusbarPartView.hide(); } - if (!this.isVisible(Parts.TITLEBAR_PART) && titlebarInGrid) { - this.workbenchGrid.removeView(this.titlebarPart); - titlebarInGrid = false; + if (!this.isVisible(Parts.TITLEBAR_PART)) { + this.titlebarPartView.hide(); } - if (this.activityBarHidden && activityBarInGrid) { - this.workbenchGrid.removeView(this.activitybarPart); - activityBarInGrid = false; + if (this.activityBarHidden) { + this.activitybarPartView.hide(); } - if (this.sideBarHidden && sidebarInGrid) { - this.uiState.lastSidebarDimension = this.workbenchGrid.getViewSize(this.sidebarPart); - this.workbenchGrid.removeView(this.sidebarPart); - sidebarInGrid = false; + if (this.sideBarHidden) { + this.sidebarPartView.hide(); } - - // Add visible parts - if (!this.statusBarHidden && !statusBarInGrid) { - if (sidebarInGrid) { - this.uiState.lastSidebarDimension = this.workbenchGrid.getViewSize(this.sidebarPart); - this.workbenchGrid.removeView(this.sidebarPart); - sidebarInGrid = false; - } - - if (activityBarInGrid) { - this.workbenchGrid.removeView(this.activitybarPart); - activityBarInGrid = false; - } - - if (panelInGrid) { - this.saveLastPanelDimension(); - this.workbenchGrid.removeView(this.panelPart); - panelInGrid = false; - } - - this.workbenchGrid.addView(this.statusbarPart, Sizing.Split, this.editorPart, Direction.Down); + // Add parts to grid + if (!statusBarInGrid) { + this.workbenchGrid.addView(this.statusbarPartView, Sizing.Split, this.editorPartView, Direction.Down); statusBarInGrid = true; } - if (this.isVisible(Parts.TITLEBAR_PART) && !titlebarInGrid) { - if (sidebarInGrid) { - this.uiState.lastSidebarDimension = this.workbenchGrid.getViewSize(this.sidebarPart); - this.workbenchGrid.removeView(this.sidebarPart); - sidebarInGrid = false; - } - - if (activityBarInGrid) { - this.workbenchGrid.removeView(this.activitybarPart); - activityBarInGrid = false; - } - - if (panelInGrid && this.panelPosition !== Position.BOTTOM) { - this.saveLastPanelDimension(); - this.workbenchGrid.removeView(this.panelPart); - panelInGrid = false; - } - - this.workbenchGrid.addView(this.titlebarPart, Sizing.Split, this.editorPart, Direction.Up); + if (!titlebarInGrid) { + this.workbenchGrid.addView(this.titlebarPartView, Sizing.Split, this.editorPartView, Direction.Up); titlebarInGrid = true; } - if (!this.activityBarHidden && !activityBarInGrid) { - if (sidebarInGrid) { - this.uiState.lastSidebarDimension = this.workbenchGrid.getViewSize(this.sidebarPart); - this.workbenchGrid.removeView(this.sidebarPart); - sidebarInGrid = false; - } - - this.workbenchGrid.addView(this.activitybarPart, Sizing.Split, this.editorPart, this.sideBarPosition === Position.RIGHT ? Direction.Right : Direction.Left); + if (!activityBarInGrid) { + this.workbenchGrid.addView(this.activitybarPartView, Sizing.Split, panelInGrid && this.sideBarPosition === this.panelPosition ? this.panelPartView : this.editorPartView, this.sideBarPosition === Position.RIGHT ? Direction.Right : Direction.Left); activityBarInGrid = true; } - if (!this.sideBarHidden && !sidebarInGrid) { - if (panelInGrid && this.panelPosition === Position.BOTTOM) { - this.saveLastPanelDimension(); - this.workbenchGrid.removeView(this.panelPart); - panelInGrid = false; - } - - this.workbenchGrid.addView(this.sidebarPart, this.uiState.lastSidebarDimension !== undefined ? this.uiState.lastSidebarDimension : Sizing.Split, this.editorPart, this.sideBarPosition === Position.RIGHT ? Direction.Right : Direction.Left); + if (!sidebarInGrid) { + this.workbenchGrid.addView(this.sidebarPartView, this.uiState.lastSidebarDimension !== undefined ? this.uiState.lastSidebarDimension : Sizing.Split, this.activitybarPartView, this.sideBarPosition === Position.LEFT ? Direction.Right : Direction.Left); sidebarInGrid = true; } - if (!this.panelHidden && !panelInGrid) { - this.workbenchGrid.addView(this.panelPart, this.getLastPanelDimension(this.panelPosition) !== undefined ? this.getLastPanelDimension(this.panelPosition) : Sizing.Split, this.editorPart, this.panelPosition === Position.BOTTOM ? Direction.Down : Direction.Right); + if (!panelInGrid) { + this.workbenchGrid.addView(this.panelPartView, this.getLastPanelDimension(this.panelPosition) !== undefined ? this.getLastPanelDimension(this.panelPosition) : Sizing.Split, this.editorPartView, this.panelPosition === Position.BOTTOM ? Direction.Down : Direction.Right); panelInGrid = true; } + + // Show visible parts + if (!this.statusBarHidden) { + this.statusbarPartView.show(); + } + + if (this.isVisible(Parts.TITLEBAR_PART)) { + this.titlebarPartView.show(); + } + + if (!this.activityBarHidden) { + this.activitybarPartView.show(); + } + + if (!this.sideBarHidden) { + this.sidebarPartView.show(); + } + + if (!this.panelHidden) { + this.panelPartView.show(); + } } layout(options?: ILayoutOptions): void { this.contextViewService.layout(); if (this.workbenchStarted && !this.workbenchShutdown) { - if (this.workbenchGrid instanceof SerializableGrid) { + if (this.workbenchGrid instanceof Grid) { const dimensions = DOM.getClientArea(this.container); DOM.position(this.workbench, 0, 0, 0, 0, 'relative'); DOM.size(this.workbench, dimensions.width, dimensions.height); @@ -1565,15 +1521,15 @@ export class Workbench extends Disposable implements IPartService { } resizePart(part: Parts, sizeChange: number): void { - let view: WorkbenchView; + let view: View; switch (part) { case Parts.SIDEBAR_PART: - view = this.sidebarPart; + view = this.sidebarPartView; case Parts.PANEL_PART: - view = this.panelPart; + view = this.panelPartView; case Parts.EDITOR_PART: - view = this.editorPart; - if (this.workbenchGrid instanceof SerializableGrid) { + view = this.editorPartView; + if (this.workbenchGrid instanceof Grid) { this.workbenchGrid.resizeView(view, this.workbenchGrid.getViewSize(view) + sizeChange); } else { this.workbenchGrid.resizePart(part, sizeChange); @@ -1589,7 +1545,7 @@ export class Workbench extends Disposable implements IPartService { // Layout if (!skipLayout) { - if (this.workbenchGrid instanceof SerializableGrid) { + if (this.workbenchGrid instanceof Grid) { this.layout(); } else { this.workbenchGrid.layout(); @@ -1643,7 +1599,7 @@ export class Workbench extends Disposable implements IPartService { // Layout if (!skipLayout) { - if (this.workbenchGrid instanceof SerializableGrid) { + if (this.workbenchGrid instanceof Grid) { this.layout(); } else { this.workbenchGrid.layout(); @@ -1685,7 +1641,7 @@ export class Workbench extends Disposable implements IPartService { // Layout if (!skipLayout) { - if (this.workbenchGrid instanceof SerializableGrid) { + if (this.workbenchGrid instanceof Grid) { this.layout(); } else { this.workbenchGrid.layout(); @@ -1694,17 +1650,17 @@ export class Workbench extends Disposable implements IPartService { } toggleMaximizedPanel(): void { - if (this.workbenchGrid instanceof SerializableGrid) { - this.workbenchGrid.maximizeViewSize(this.panelPart); + if (this.workbenchGrid instanceof Grid) { + this.workbenchGrid.maximizeViewSize(this.panelPartView); } else { this.workbenchGrid.layout({ toggleMaximizedPanel: true, source: Parts.PANEL_PART }); } } isPanelMaximized(): boolean { - if (this.workbenchGrid instanceof SerializableGrid) { + if (this.workbenchGrid instanceof Grid) { try { - return this.workbenchGrid.getViewSize2(this.panelPart).height === this.panelPart.maximumHeight; + return this.workbenchGrid.getViewSize2(this.panelPartView).height === this.panelPart.maximumHeight; } catch (e) { return false; } @@ -1739,15 +1695,19 @@ export class Workbench extends Disposable implements IPartService { this.sidebarPart.updateStyles(); // Layout - if (this.workbenchGrid instanceof SerializableGrid) { + if (this.workbenchGrid instanceof Grid) { if (!wasHidden) { - this.uiState.lastSidebarDimension = this.workbenchGrid.getViewSize(this.sidebarPart); - this.workbenchGrid.removeView(this.sidebarPart); + this.uiState.lastSidebarDimension = this.workbenchGrid.getViewSize(this.sidebarPartView); + this.workbenchGrid.removeView(this.sidebarPartView); } if (!this.activityBarHidden) { - this.workbenchGrid.removeView(this.activitybarPart); + this.workbenchGrid.removeView(this.activitybarPartView); + } + + if (!this.panelHidden && this.panelPosition === Position.BOTTOM) { + this.workbenchGrid.removeView(this.panelPartView); } this.layout(); @@ -1762,7 +1722,7 @@ export class Workbench extends Disposable implements IPartService { // Layout if (!skipLayout) { - if (this.workbenchGrid instanceof SerializableGrid) { + if (this.workbenchGrid instanceof Grid) { const dimensions = DOM.getClientArea(this.container); this.workbenchGrid.layout(dimensions.width, dimensions.height); } else { @@ -1802,15 +1762,13 @@ export class Workbench extends Disposable implements IPartService { this.panelPart.updateStyles(); // Layout - if (this.workbenchGrid instanceof SerializableGrid) { - if (wasHidden) { - this.workbenchGrid.addView(this.panelPart, this.getLastPanelDimension(position) !== undefined ? this.getLastPanelDimension(position) : Sizing.Split, this.editorPart, position === Position.BOTTOM ? Direction.Down : Direction.Right); - } else { - this.workbenchGrid.moveView(this.panelPart, this.getLastPanelDimension(position) !== undefined ? this.getLastPanelDimension(position) : Sizing.Split, this.editorPart, position === Position.BOTTOM ? Direction.Down : Direction.Right); + if (this.workbenchGrid instanceof Grid) { + if (!wasHidden) { + this.saveLastPanelDimension(); + this.workbenchGrid.removeView(this.panelPartView); } - const dimensions = DOM.getClientArea(this.container); - this.workbenchGrid.layout(dimensions.width, dimensions.height); + this.layout(); } else { this.workbenchGrid.layout(); } From f93786c3d5832797003f979780b3b2d209950092 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 28 Jan 2019 14:30:20 -0800 Subject: [PATCH 247/274] Prevent double dispatch of copy/paste commands in webviews on platforms where we don't need workaround --- .../electron-browser/webview.contribution.ts | 88 ++++++++++--------- 1 file changed, 46 insertions(+), 42 deletions(-) diff --git a/src/vs/workbench/parts/webview/electron-browser/webview.contribution.ts b/src/vs/workbench/parts/webview/electron-browser/webview.contribution.ts index 7a686405379..033bd98d007 100644 --- a/src/vs/workbench/parts/webview/electron-browser/webview.contribution.ts +++ b/src/vs/workbench/parts/webview/electron-browser/webview.contribution.ts @@ -21,6 +21,7 @@ import { WebviewEditor } from './webviewEditor'; import { WebviewEditorInput } from './webviewEditorInput'; import { IWebviewEditorService, WebviewEditorService } from './webviewEditorService'; import { InputFocusedContextKey } from 'vs/platform/workbench/common/contextkeys'; +import { isMacintosh } from 'vs/base/common/platform'; (Registry.as(EditorExtensions.Editors)).registerEditor(new EditorDescriptor( WebviewEditor, @@ -72,53 +73,56 @@ export function registerWebViewCommands(editorId: string): void { } })).register(); - (new CopyWebviewEditorCommand({ - id: CopyWebviewEditorCommand.ID, - precondition: ContextKeyExpr.and(contextKeyExpr, ContextKeyExpr.not(InputFocusedContextKey)), - kbOpts: { - primary: KeyMod.CtrlCmd | KeyCode.KEY_C, - weight: KeybindingWeight.EditorContrib - } - })).register(); + // These commands are only needed on MacOS where we have to disable the menu bar commands + if (isMacintosh) { + (new CopyWebviewEditorCommand({ + id: CopyWebviewEditorCommand.ID, + precondition: ContextKeyExpr.and(contextKeyExpr, ContextKeyExpr.not(InputFocusedContextKey)), + kbOpts: { + primary: KeyMod.CtrlCmd | KeyCode.KEY_C, + weight: KeybindingWeight.EditorContrib + } + })).register(); - (new PasteWebviewEditorCommand({ - id: PasteWebviewEditorCommand.ID, - precondition: ContextKeyExpr.and(contextKeyExpr, ContextKeyExpr.not(InputFocusedContextKey)), - kbOpts: { - primary: KeyMod.CtrlCmd | KeyCode.KEY_V, - weight: KeybindingWeight.EditorContrib - } - })).register(); + (new PasteWebviewEditorCommand({ + id: PasteWebviewEditorCommand.ID, + precondition: ContextKeyExpr.and(contextKeyExpr, ContextKeyExpr.not(InputFocusedContextKey)), + kbOpts: { + primary: KeyMod.CtrlCmd | KeyCode.KEY_V, + weight: KeybindingWeight.EditorContrib + } + })).register(); - (new CutWebviewEditorCommand({ - id: CutWebviewEditorCommand.ID, - precondition: ContextKeyExpr.and(contextKeyExpr, ContextKeyExpr.not(InputFocusedContextKey)), - kbOpts: { - primary: KeyMod.CtrlCmd | KeyCode.KEY_X, - weight: KeybindingWeight.EditorContrib - } - })).register(); + (new CutWebviewEditorCommand({ + id: CutWebviewEditorCommand.ID, + precondition: ContextKeyExpr.and(contextKeyExpr, ContextKeyExpr.not(InputFocusedContextKey)), + kbOpts: { + primary: KeyMod.CtrlCmd | KeyCode.KEY_X, + weight: KeybindingWeight.EditorContrib + } + })).register(); - (new UndoWebviewEditorCommand({ - id: UndoWebviewEditorCommand.ID, - precondition: ContextKeyExpr.and(contextKeyExpr, ContextKeyExpr.not(InputFocusedContextKey)), - kbOpts: { - primary: KeyMod.CtrlCmd | KeyCode.KEY_Z, - weight: KeybindingWeight.EditorContrib - } - })).register(); + (new UndoWebviewEditorCommand({ + id: UndoWebviewEditorCommand.ID, + precondition: ContextKeyExpr.and(contextKeyExpr, ContextKeyExpr.not(InputFocusedContextKey)), + kbOpts: { + primary: KeyMod.CtrlCmd | KeyCode.KEY_Z, + weight: KeybindingWeight.EditorContrib + } + })).register(); - (new RedoWebviewEditorCommand({ - id: RedoWebviewEditorCommand.ID, - precondition: ContextKeyExpr.and(contextKeyExpr, ContextKeyExpr.not(InputFocusedContextKey)), - kbOpts: { - primary: KeyMod.CtrlCmd | KeyCode.KEY_Y, - secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Z], - mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Z }, - weight: KeybindingWeight.EditorContrib - } - })).register(); + (new RedoWebviewEditorCommand({ + id: RedoWebviewEditorCommand.ID, + precondition: ContextKeyExpr.and(contextKeyExpr, ContextKeyExpr.not(InputFocusedContextKey)), + kbOpts: { + primary: KeyMod.CtrlCmd | KeyCode.KEY_Y, + secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Z], + mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Z }, + weight: KeybindingWeight.EditorContrib + } + })).register(); + } } registerWebViewCommands(WebviewEditor.ID); From 58d3dade0e03d0a1b6687725168e8b5aaf50908d Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 28 Jan 2019 23:36:03 +0100 Subject: [PATCH 248/274] [html] update service (new formatter) --- extensions/html-language-features/server/package.json | 2 +- extensions/html-language-features/server/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/html-language-features/server/package.json b/extensions/html-language-features/server/package.json index 170deb801b2..b57df13fe19 100644 --- a/extensions/html-language-features/server/package.json +++ b/extensions/html-language-features/server/package.json @@ -10,7 +10,7 @@ "main": "./out/htmlServerMain", "dependencies": { "vscode-css-languageservice": "^3.0.13-next.10", - "vscode-html-languageservice": "^2.1.11-next.7", + "vscode-html-languageservice": "^2.1.11-next.8", "vscode-languageserver": "^5.1.0", "vscode-languageserver-types": "^3.13.0", "vscode-nls": "^4.0.0", diff --git a/extensions/html-language-features/server/yarn.lock b/extensions/html-language-features/server/yarn.lock index bb1b921c1c6..4918bf833a4 100644 --- a/extensions/html-language-features/server/yarn.lock +++ b/extensions/html-language-features/server/yarn.lock @@ -237,10 +237,10 @@ vscode-css-languageservice@^3.0.13-next.10: vscode-languageserver-types "^3.13.0" vscode-nls "^4.0.0" -vscode-html-languageservice@^2.1.11-next.7: - version "2.1.11-next.7" - resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-2.1.11-next.7.tgz#4de88828ffa8b133b94a00de31512856f1cd1998" - integrity sha512-k6xFtjw7mk7MIokNxPV+BTLLUTK9OwLxeIgJPibDi2v5b078FNgpD7Yb/h1sZ3ZHVTbd2ARfe4EY6QOTSQ1eaw== +vscode-html-languageservice@^2.1.11-next.8: + version "2.1.11-next.8" + resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-2.1.11-next.8.tgz#060c69163ad65ad50988841011b86e423fd0a6d3" + integrity sha512-K6jCKx0xPQHQ/poicbMvCKwWc+XxBzZlT74C5SLbDfS6llkVIBThoIl6qrJhDuAqPNmyFmaFbT4+YvfqBUdpAA== dependencies: vscode-languageserver-types "^3.13.0" vscode-nls "^4.0.0" From 807759ecff332b94bb17a6955d7a4d44f3e06706 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 28 Jan 2019 14:03:20 -0800 Subject: [PATCH 249/274] Rename `jsDocCompletion.enabled` to `javascript.suggest.completeJsDocs` and `typescript.suggest.completeJsDocs` Fixes #67146 --- .../typescript-language-features/package.json | 13 +++++++++++++ .../typescript-language-features/package.nls.json | 4 +++- .../src/features/jsDocCompletions.ts | 3 ++- .../src/languageProvider.ts | 2 +- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index 63fcf31bad9..dc8b9dd2e34 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -362,6 +362,7 @@ "type": "boolean", "default": true, "description": "%jsDocCompletion.enabled%", + "deprecationMessage": "jsDocCompletion.deprecationMessage", "scope": "resource" }, "javascript.implicitProjectConfig.checkJs": { @@ -424,6 +425,18 @@ "description": "%configuration.suggest.autoImports%", "scope": "resource" }, + "javascript.suggest.completeJSDocs": { + "type": "boolean", + "default": true, + "description": "%configuration.suggest.completeJSDocs%", + "scope": "resource" + }, + "typescript.suggest.completeJSDocs": { + "type": "boolean", + "default": true, + "description": "%configuration.suggest.completeJSDocs%", + "scope": "resource" + }, "typescript.locale": { "type": [ "string", diff --git a/extensions/typescript-language-features/package.nls.json b/extensions/typescript-language-features/package.nls.json index 2cd347a012a..d945ca0876b 100644 --- a/extensions/typescript-language-features/package.nls.json +++ b/extensions/typescript-language-features/package.nls.json @@ -38,6 +38,7 @@ "typescript.selectTypeScriptVersion.title": "Select TypeScript Version.", "typescript.reportStyleChecksAsWarnings": "Report style checks as warnings.", "jsDocCompletion.enabled": "Enable/disable auto JSDoc comments.", + "jsDocCompletion.deprecationMessage": "This setting has renamed to `javascript.suggest.completeJSDocs` and `typescript.suggest.completeJSDocs` and will be removed.", "javascript.implicitProjectConfig.checkJs": "Enable/disable semantic checking of JavaScript files. Existing jsconfig.json or tsconfig.json files override this setting. Requires using TypeScript 2.3.1 or newer in the workspace.", "typescript.npm": "Specifies the path to the NPM executable used for Automatic Type Acquisition. Requires using TypeScript 2.3.4 or newer in the workspace.", "typescript.check.npmIsInstalled": "Check if NPM is installed for Automatic Type Acquisition.", @@ -67,5 +68,6 @@ "typescript.updateImportsOnFileMove.enabled.never": "Never rename paths and don't prompt.", "typescript.autoClosingTags": "Enable/disable automatic closing of JSX tags. Requires using TypeScript 3.0 or newer in the workspace.", "typescript.suggest.enabled": "Enabled/disable autocomplete suggestions.", - "configuration.surveys.enabled": "Enabled/disable occasional surveys that help us improve VS Code's JavaScript and TypeScript support." + "configuration.surveys.enabled": "Enabled/disable occasional surveys that help us improve VS Code's JavaScript and TypeScript support.", + "configuration.suggest.completeJSDocs": "Enable/disable suggestion to complete JSDoc comments." } \ No newline at end of file diff --git a/extensions/typescript-language-features/src/features/jsDocCompletions.ts b/extensions/typescript-language-features/src/features/jsDocCompletions.ts index e84b27ed2b8..36f7a751522 100644 --- a/extensions/typescript-language-features/src/features/jsDocCompletions.ts +++ b/extensions/typescript-language-features/src/features/jsDocCompletions.ts @@ -112,9 +112,10 @@ export function templateToSnippet(template: string): vscode.SnippetString { export function register( selector: vscode.DocumentSelector, + modeId: string, client: ITypeScriptServiceClient, ): vscode.Disposable { - return new ConfigurationDependentRegistration('jsDocCompletion', 'enabled', () => { + return new ConfigurationDependentRegistration(modeId, 'suggest.completeJSDocs', () => { return vscode.languages.registerCompletionItemProvider(selector, new JsDocCompletionProvider(client), '*'); diff --git a/extensions/typescript-language-features/src/languageProvider.ts b/extensions/typescript-language-features/src/languageProvider.ts index 3a927cf777d..6f122fcb633 100644 --- a/extensions/typescript-language-features/src/languageProvider.ts +++ b/extensions/typescript-language-features/src/languageProvider.ts @@ -65,7 +65,7 @@ export default class LanguageProvider extends Disposable { this._register((await import('./features/hover')).register(selector, this.client)); this._register((await import('./features/implementations')).register(selector, this.client)); this._register((await import('./features/implementationsCodeLens')).register(selector, this.description.id, this.client, cachedResponse)); - this._register((await import('./features/jsDocCompletions')).register(selector, this.client)); + this._register((await import('./features/jsDocCompletions')).register(selector, this.description.id, this.client)); this._register((await import('./features/organizeImports')).register(selector, this.client, this.commandManager, this.fileConfigurationManager, this.telemetryReporter)); this._register((await import('./features/quickFix')).register(selector, this.client, this.fileConfigurationManager, this.commandManager, this.client.diagnosticsManager, this.telemetryReporter)); this._register((await import('./features/fixAll')).register(selector, this.client, this.fileConfigurationManager, this.client.diagnosticsManager)); From b15877def6af62a9488f8157baa012d0b3e157f6 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 28 Jan 2019 14:23:26 -0800 Subject: [PATCH 250/274] Handle some edge cases of #18131 --- .../src/features/completions.ts | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/extensions/typescript-language-features/src/features/completions.ts b/extensions/typescript-language-features/src/features/completions.ts index f07e335daeb..c0020fdfef0 100644 --- a/extensions/typescript-language-features/src/features/completions.ts +++ b/extensions/typescript-language-features/src/features/completions.ts @@ -638,25 +638,23 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider try { const args: Proto.FileLocationRequestArgs = typeConverters.Position.toFileLocationRequestArgs(filepath, position); const response = await this.client.execute('quickinfo', args, token); - if (response.type !== 'response' || !response.body) { - return true; + if (response.type === 'response' && response.body) { + switch (response.body.kind) { + case 'var': + case 'let': + case 'const': + case 'alias': + return false; + } } - - switch (response.body.kind) { - case 'var': - case 'let': - case 'const': - case 'alias': - return false; - } - } catch (e) { - return true; + } catch { + // Noop } - // Don't complete function call if there is already something that looks like a funciton call + // Don't complete function call if there is already something that looks like a function call // https://github.com/Microsoft/vscode/issues/18131 const after = document.lineAt(position.line).text.slice(position.character); - return after.match(/^\s*\(/g) === null; + return after.match(/^[a-z_$0-9]*\s*\(/gi) === null; } } From 71ee61ef2d96865b563c517aa25ba5791447eede Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 28 Jan 2019 14:43:49 -0800 Subject: [PATCH 251/274] Fix active signature not being propagated properly #33413 --- src/vs/workbench/api/node/extHostLanguageFeatures.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/api/node/extHostLanguageFeatures.ts b/src/vs/workbench/api/node/extHostLanguageFeatures.ts index 8379fa3ad79..76b8b1fbe00 100644 --- a/src/vs/workbench/api/node/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/node/extHostLanguageFeatures.ts @@ -756,11 +756,14 @@ class SignatureHelpAdapter { private reviveContext(context: modes.SignatureHelpContext): vscode.SignatureHelpContext { let activeSignatureHelp: vscode.SignatureHelp | undefined = undefined; if (context.activeSignatureHelp) { + const revivedSignatureHelp = typeConvert.SignatureHelp.to(context.activeSignatureHelp); const saved = this._heap.get(ObjectIdentifier.of(context.activeSignatureHelp)); if (saved) { activeSignatureHelp = saved; + activeSignatureHelp.activeSignature = revivedSignatureHelp.activeSignature; + activeSignatureHelp.activeParameter = revivedSignatureHelp.activeParameter; } else { - activeSignatureHelp = typeConvert.SignatureHelp.to(context.activeSignatureHelp); + activeSignatureHelp = revivedSignatureHelp; } } return { ...context, activeSignatureHelp }; From 89275bcf594eb2602b9efe5e6e28f7fc9d20bee0 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 28 Jan 2019 23:49:54 +0100 Subject: [PATCH 252/274] Add preserve-aligned to the UI settings for html.format.wrapAttributes. Fixes #66644 --- extensions/html-language-features/package.json | 8 ++++++-- extensions/html-language-features/package.nls.json | 2 ++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/extensions/html-language-features/package.json b/extensions/html-language-features/package.json index ebcde114c3f..0fc7ed3ce11 100644 --- a/extensions/html-language-features/package.json +++ b/extensions/html-language-features/package.json @@ -117,14 +117,18 @@ "force", "force-aligned", "force-expand-multiline", - "aligned-multiple" + "aligned-multiple", + "preserve", + "preserve-aligned" ], "enumDescriptions": [ "%html.format.wrapAttributes.auto%", "%html.format.wrapAttributes.force%", "%html.format.wrapAttributes.forcealign%", "%html.format.wrapAttributes.forcemultiline%", - "%html.format.wrapAttributes.alignedmultiple%" + "%html.format.wrapAttributes.alignedmultiple%", + "%html.format.wrapAttributes.preserve%", + "%html.format.wrapAttributes.preservealigned%" ], "description": "%html.format.wrapAttributes.desc%" }, diff --git a/extensions/html-language-features/package.nls.json b/extensions/html-language-features/package.nls.json index c6923ce881e..343a5a7e2d9 100644 --- a/extensions/html-language-features/package.nls.json +++ b/extensions/html-language-features/package.nls.json @@ -17,6 +17,8 @@ "html.format.wrapAttributes.forcealign": "Wrap each attribute except first and keep aligned.", "html.format.wrapAttributes.forcemultiline": "Wrap each attribute.", "html.format.wrapAttributes.alignedmultiple": "Wrap when line length is exceeded, align attributes vertically.", + "html.format.wrapAttributes.preserve": "Preserve wrapping of attributes", + "html.format.wrapAttributes.preservealigned": "Preserve wrapping of attributes but align.", "html.suggest.angular1.desc": "Controls whether the built-in HTML language support suggests Angular V1 tags and properties.", "html.suggest.ionic.desc": "Controls whether the built-in HTML language support suggests Ionic tags, properties and values.", "html.suggest.html5.desc": "Controls whether the built-in HTML language support suggests HTML5 tags, properties and values.", From 3af611c7590383f34756ab5cbbe063cdd2f8ebe2 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 28 Jan 2019 23:56:43 +0100 Subject: [PATCH 253/274] make jsonValidation dynamic (for #66574) --- extensions/json-language-features/client/src/jsonMain.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/extensions/json-language-features/client/src/jsonMain.ts b/extensions/json-language-features/client/src/jsonMain.ts index 585a908afe7..0ea7b420e3a 100644 --- a/extensions/json-language-features/client/src/jsonMain.ts +++ b/extensions/json-language-features/client/src/jsonMain.ts @@ -194,6 +194,10 @@ export function activate(context: ExtensionContext) { client.sendNotification(SchemaAssociationNotification.type, getSchemaAssociation(context)); + extensions.onDidChange(_ => { + client.sendNotification(SchemaAssociationNotification.type, getSchemaAssociation(context)); + }); + documentSelector.forEach(selector => { toDispose.push(languages.registerSelectionRangeProvider(selector, { async provideSelectionRanges(document: TextDocument, position: Position): Promise { From b79f3a68999c8f80ffd0c8ab43ea859d804e678c Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Tue, 29 Jan 2019 00:02:06 +0100 Subject: [PATCH 254/274] cli server: open folders in separate windows --- src/vs/workbench/api/node/extHostTerminalService.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts index 884bd047a33..1136debe9a1 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -603,8 +603,11 @@ class CLIServer { req.setEncoding('utf8'); req.on('data', (d: string) => chunks.push(d)); req.on('end', () => { - const { fileURIs, folderURIs, forceNewWindow, diffMode, addMode, forceReuseWindow } = JSON.parse(chunks.join('')); + let { fileURIs, folderURIs, forceNewWindow, diffMode, addMode, forceReuseWindow } = JSON.parse(chunks.join('')); if (folderURIs && folderURIs.length || fileURIs && fileURIs.length) { + if (folderURIs && folderURIs.length && !forceReuseWindow) { + forceNewWindow = true; + } this._commands.executeCommand('_files.windowOpen', { folderURIs: this.toURIs(folderURIs), fileURIs: this.toURIs(fileURIs), forceNewWindow, diffMode, addMode, forceReuseWindow }); } res.writeHead(200); From ab8a277c9c12da1d47c5cae49ded77c670f8905d Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Mon, 28 Jan 2019 15:13:26 -0800 Subject: [PATCH 255/274] don't add titlebar to the grid when using native --- src/vs/workbench/electron-browser/workbench.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts index 7cc7eab56b1..df339344737 100644 --- a/src/vs/workbench/electron-browser/workbench.ts +++ b/src/vs/workbench/electron-browser/workbench.ts @@ -1430,7 +1430,7 @@ export class Workbench extends Disposable implements IPartService { statusBarInGrid = true; } - if (!titlebarInGrid) { + if (!titlebarInGrid && this.useCustomTitleBarStyle()) { this.workbenchGrid.addView(this.titlebarPartView, Sizing.Split, this.editorPartView, Direction.Up); titlebarInGrid = true; } From 6a7000f2cc27e0fccbe84e2d866d5448292742ff Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 28 Jan 2019 14:49:51 -0800 Subject: [PATCH 256/274] Better description of activeSignatureHelp --- src/vs/vscode.proposed.d.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index a32810ebda4..7419b80e47d 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1112,7 +1112,8 @@ declare module 'vscode' { /** * The currently active [`SignatureHelp`](#SignatureHelp). * - * Will have the [`SignatureHelp.activeSignature`] field updated based on user arrowing through sig help + * The `activeSignatureHelp` has its [`SignatureHelp.activeSignature`] field updated based on + * the user arrowing through available signatures. */ readonly activeSignatureHelp?: SignatureHelp; } From 951d795023ccae1c9f44e4d3e185c346dba03d31 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 28 Jan 2019 15:24:25 -0800 Subject: [PATCH 257/274] Improving documentation --- src/vs/vscode.proposed.d.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 7419b80e47d..43a627b4c93 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1122,7 +1122,7 @@ declare module 'vscode' { //#region CodeAction.isPreferred - mjbvz export interface CodeAction { /** - * If the action is a preferred action or fix to take. + * Marks this as a preferred action. Preferred actions are used by the `auto fix` command. * * A quick fix should be marked preferred if it properly addresses the underlying error. * A refactoring should be marked preferred if it is the most reasonable choice of actions to take. @@ -1143,9 +1143,9 @@ declare module 'vscode' { //#region Autofix - mjbvz export namespace CodeActionKind { /** - * Base kind for an auto fix source action: `source.fixAll`. + * Base kind for auto-fix source actions: `source.fixAll`. * - * Fix all actions automatically fix errors in the code that have a clear fix that does not require user input. + * Fix all actions automatically fix errors that have a clear fix that do not require user input. * They should not suppress errors or perform unsafe fixes such as generating new types or classes. */ export const SourceFixAll: CodeActionKind; From c1c3e5eab0f2fb9e04a32b4fc6473023a9c25697 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 28 Jan 2019 15:45:30 -0800 Subject: [PATCH 258/274] Properly encode markdown file path for open links Fixes #59523 Double encode the path so that `Uri.parse`'s decoding only strips out the first level of encoding --- .../markdown-language-features/src/commands/openDocumentLink.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/markdown-language-features/src/commands/openDocumentLink.ts b/extensions/markdown-language-features/src/commands/openDocumentLink.ts index 42dfe67df25..ea929f484f6 100644 --- a/extensions/markdown-language-features/src/commands/openDocumentLink.ts +++ b/extensions/markdown-language-features/src/commands/openDocumentLink.ts @@ -25,7 +25,7 @@ export class OpenDocumentLinkCommand implements Command { path: string, fragment: string ): vscode.Uri { - return vscode.Uri.parse(`command:${OpenDocumentLinkCommand.id}?${encodeURIComponent(JSON.stringify({ path, fragment }))}`); + return vscode.Uri.parse(`command:${OpenDocumentLinkCommand.id}?${encodeURIComponent(JSON.stringify({ path: encodeURIComponent(path), fragment }))}`); } public constructor( From 62468cb3562d97141424eb84f863cc0d8701ba2d Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 28 Jan 2019 23:48:53 +0000 Subject: [PATCH 259/274] Sync settings tree scroll position to TOC - #64749 --- .../parts/preferences/browser/settingsTree.ts | 7 ++ .../electron-browser/settingsEditor2.ts | 103 +++++++++++++----- 2 files changed, 80 insertions(+), 30 deletions(-) diff --git a/src/vs/workbench/parts/preferences/browser/settingsTree.ts b/src/vs/workbench/parts/preferences/browser/settingsTree.ts index 0b9ccb098bc..ab40cc8e798 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsTree.ts @@ -257,6 +257,7 @@ export abstract class AbstractSettingRenderer implements ITreeRenderer(); readonly onDidClickOverrideElement: Event = this._onDidClickOverrideElement.event; @@ -385,6 +386,7 @@ export abstract class AbstractSettingRenderer implements ITreeRenderer; + private tocFocusedElement: SettingsTreeGroupElement; + private settingsTreeScrollTop = 0; + constructor( @ITelemetryService telemetryService: ITelemetryService, @IConfigurationService private readonly configurationService: IConfigurationService, @@ -542,21 +545,22 @@ export class SettingsEditor2 extends BaseEditor { this.viewState)); this._register(this.tocTree.onDidChangeFocus(e => { - this.tocTree.setSelection(e.elements); const element: SettingsTreeGroupElement = e.elements[0]; + if (this.tocFocusedElement === e.elements[0]) { + return; + } + + this.tocFocusedElement = element; + this.tocTree.setSelection(e.elements); if (this.searchResultModel) { if (this.viewState.filterToCategory !== element) { this.viewState.filterToCategory = element; this.renderTree(); this.settingsTree.scrollTop = 0; } - } else if (element) { + } else if (element && (!e.browserEvent || !(e.browserEvent).fromScroll)) { this.settingsTree.reveal(element, 0); } - - // else if (element && (!e.payload || !e.payload.fromScroll)) { - // this.settingsTree.reveal(element, 0); - // } })); this._register(this.tocTree.onDidFocus(() => { @@ -606,7 +610,17 @@ export class SettingsEditor2 extends BaseEditor { this.settingsTree.getHTMLElement().attributes.removeNamedItem('tabindex'); this._register(this.settingsTree.onDidScroll(() => { - this.updateTreeScrollSync(); + if (this.settingsTree.scrollTop === this.settingsTreeScrollTop) { + return; + } + + this.settingsTreeScrollTop = this.settingsTree.scrollTop; + + // setTimeout because calling setChildren on the settingsTree can trigger onDidScroll, so it fires when + // setChildren has called on the settings tree but not the toc tree yet, so their rendered elements are out of sync + setTimeout(() => { + this.updateTreeScrollSync(); + }, 0); })); } @@ -642,29 +656,56 @@ export class SettingsEditor2 extends BaseEditor { return; } - // this.updateTreePagingByScroll(); + // Hack, see https://github.com/Microsoft/vscode/issues/64749 + const settingItems = this.settingsTree.getHTMLElement().querySelectorAll('.setting-item'); + const firstEl = settingItems[1] || settingItems[0]; + if (!firstEl) { + return; + } - // const element = this.tocTreeModel.children[0]; - // const elementToSync = this.settingsTree.getFirstVisibleElement(); - // const element = elementToSync instanceof SettingsTreeSettingElement ? elementToSync.parent : - // elementToSync instanceof SettingsTreeGroupElement ? elementToSync : - // null; + const firstSettingId = this.settingRenderers.getIdForDOMElementInSetting(firstEl); + const elementToSync = this.settingsTreeModel.getElementById(firstSettingId); + const element = elementToSync instanceof SettingsTreeSettingElement ? elementToSync.parent : + elementToSync instanceof SettingsTreeGroupElement ? elementToSync : + null; - // if (element && this.tocTree.getSelection()[0] !== element) { - // this.tocTree.reveal(element); - // const elementTop = this.tocTree.getRelativeTop(element); - // collapseAll(this.tocTree, element); - // if (elementTop < 0 || elementTop > 1) { - // this.tocTree.reveal(element); - // } else { - // this.tocTree.reveal(element, elementTop); - // } + if (element && this.tocTree.getSelection()[0] !== element) { + const ancestors = this.getAncestors(element); + ancestors.forEach(e => this.tocTree.expand(e)); - // this.tocTree.expand(element); + this.tocTree.reveal(element); + const elementTop = this.tocTree.getRelativeTop(element); + this.tocTree.collapseAll(); - // this.tocTree.setSelection([element]); - // this.tocTree.setFocus(element, { fromScroll: true }); - // } + ancestors.forEach(e => this.tocTree.expand(e)); + if (elementTop < 0 || elementTop > 1) { + this.tocTree.reveal(element); + } else { + this.tocTree.reveal(element, elementTop); + } + + this.tocTree.expand(element); + + this.tocTree.setSelection([element]); + + const fakeKeyboardEvent = new KeyboardEvent('keydown'); + (fakeKeyboardEvent).fromScroll = true; + this.tocTree.setFocus([element], fakeKeyboardEvent); + } + } + + private getAncestors(element: SettingsTreeElement): SettingsTreeElement[] { + const ancestors: any[] = []; + + while (element.parent) { + if (element.parent.id !== 'root') { + ancestors.push(element.parent); + } + + element = element.parent; + } + + return ancestors.reverse(); } private updateChangedSetting(key: string, value: any): Promise { @@ -843,10 +884,11 @@ export class SettingsEditor2 extends BaseEditor { } else { this.settingsTreeModel = this.instantiationService.createInstance(SettingsTreeModel, this.viewState); this.settingsTreeModel.update(resolvedSettingsRoot); + this.tocTreeModel.settingsTreeRoot = this.settingsTreeModel.root as SettingsTreeGroupElement; + + this.refreshTOCTree(); this.refreshTree(); - this.tocTreeModel.settingsTreeRoot = this.settingsTreeModel.root as SettingsTreeGroupElement; - this.refreshTOCTree(); this.tocTree.collapseAll(); } @@ -980,7 +1022,6 @@ export class SettingsEditor2 extends BaseEditor { this.viewState.filterToCategory = null; this.tocTreeModel.currentSearchModel = this.searchResultModel; - this.refreshTOCTree(); this.onSearchModeToggled(); if (this.searchResultModel) { @@ -995,6 +1036,8 @@ export class SettingsEditor2 extends BaseEditor { this.refreshTree(); this.renderResultCountMessages(); } + + this.refreshTOCTree(); } return Promise.resolve(null); From 0abf8dcbd0185277e305b9e09c01ff4ebc2168eb Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Mon, 28 Jan 2019 15:45:53 -0800 Subject: [PATCH 260/274] Fix typo --- extensions/html-language-features/.vscode/launch.json | 6 ++---- extensions/html-language-features/server/src/customData.ts | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/extensions/html-language-features/.vscode/launch.json b/extensions/html-language-features/.vscode/launch.json index 28ec3d3dc89..032dc46bb43 100644 --- a/extensions/html-language-features/.vscode/launch.json +++ b/extensions/html-language-features/.vscode/launch.json @@ -17,8 +17,7 @@ ], "stopOnEntry": false, "sourceMaps": true, - "outFiles": ["${workspaceFolder}/client/out/**/*.js"], - "preLaunchTask": "npm" + "outFiles": ["${workspaceFolder}/client/out/**/*.js"] }, { "name": "Launch Tests", @@ -28,8 +27,7 @@ "args": ["--extensionDevelopmentPath=${workspaceFolder}", "--extensionTestsPath=${workspaceFolder}/client/out/test" ], "stopOnEntry": false, "sourceMaps": true, - "outFiles": ["${workspaceFolder}/client/out/test/**/*.js"], - "preLaunchTask": "npm" + "outFiles": ["${workspaceFolder}/client/out/test/**/*.js"] }, { "name": "Attach Language Server", diff --git a/extensions/html-language-features/server/src/customData.ts b/extensions/html-language-features/server/src/customData.ts index 8aa57038288..673e4a4ab9d 100644 --- a/extensions/html-language-features/server/src/customData.ts +++ b/extensions/html-language-features/server/src/customData.ts @@ -21,7 +21,7 @@ export function getDataProviders(dataPaths?: string[]): IHTMLDataProvider[] { providers.push(new HTMLDataProvider(`customProvider${i}`, htmlData)); } } catch (err) { - console.log(`Failed to laod tag from ${path}`); + console.log(`Failed to load tag from ${path}`); } }); From a608c35e0d9c7a0761fd7c0b5574ebf010caabfb Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 28 Jan 2019 15:54:55 -0800 Subject: [PATCH 261/274] Only update setIgnoreMenuShortcuts on keydown --- .../parts/webview/electron-browser/webviewElement.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/webview/electron-browser/webviewElement.ts b/src/vs/workbench/parts/webview/electron-browser/webviewElement.ts index 57af438d221..5d9110300fb 100644 --- a/src/vs/workbench/parts/webview/electron-browser/webviewElement.ts +++ b/src/vs/workbench/parts/webview/electron-browser/webviewElement.ts @@ -157,7 +157,9 @@ class WebviewKeyboardHandler extends Disposable { const contents = this.getWebContents(); if (contents) { contents.on('before-input-event', (_event, input) => { - this.setIgnoreMenuShortcuts(input.control || input.meta); + if (input.type === 'keyDown') { + this.setIgnoreMenuShortcuts(input.control || input.meta); + } }); } })); From a7ad8306dd7d18b1e6e03da6689ba7b0dbf1bb3d Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Mon, 28 Jan 2019 16:47:25 -0800 Subject: [PATCH 262/274] adding toggle editor --- .../browser/actions/layoutActions.ts | 26 +++++++ .../workbench/electron-browser/workbench.ts | 76 ++++++++++++++----- .../services/part/common/partService.ts | 6 ++ .../workbench/test/workbenchTestServices.ts | 2 + 4 files changed, 89 insertions(+), 21 deletions(-) diff --git a/src/vs/workbench/browser/actions/layoutActions.ts b/src/vs/workbench/browser/actions/layoutActions.ts index fa168b918d7..14585858c91 100644 --- a/src/vs/workbench/browser/actions/layoutActions.ts +++ b/src/vs/workbench/browser/actions/layoutActions.ts @@ -210,6 +210,32 @@ MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { // --- Toggle Sidebar Visibility +export class ToggleEditorVisibilityAction extends Action { + static readonly ID = 'workbench.action.toggleEditorVisibility'; + static readonly LABEL = nls.localize('toggleEditor', "Toggle Editor Area"); + + constructor( + id: string, + label: string, + @IPartService private readonly partService: IPartService + ) { + super(id, label); + + this.enabled = !!this.partService; + } + + run(): Promise { + const hideEditor = this.partService.isVisible(Parts.EDITOR_PART); + this.partService.setEditorHidden(hideEditor); + + return Promise.resolve(null); + } + +} + +registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleEditorVisibilityAction, ToggleEditorVisibilityAction.ID, ToggleEditorVisibilityAction.LABEL), 'View: Toggle Editor Area Visibility', viewCategory); + + export class ToggleSidebarVisibilityAction extends Action { static readonly ID = 'workbench.action.toggleSidebarVisibility'; diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts index df339344737..f20aff0ed3b 100644 --- a/src/vs/workbench/electron-browser/workbench.ts +++ b/src/vs/workbench/electron-browser/workbench.ts @@ -228,6 +228,7 @@ export class Workbench extends Disposable implements IPartService { private notificationsCenter: NotificationsCenter; private notificationsToasts: NotificationsToasts; + private editorHidden: boolean; private sideBarHidden: boolean; private statusBarHidden: boolean; private activityBarHidden: boolean; @@ -911,6 +912,9 @@ export class Workbench extends Disposable implements IPartService { private initSettings(): void { + // Editor visiblity + this.editorHidden = false; + // Sidebar visibility this.sideBarHidden = this.storageService.getBoolean(Workbench.sidebarHiddenStorageKey, StorageScope.WORKSPACE, this.contextService.getWorkbenchState() === WorkbenchState.EMPTY); @@ -1403,27 +1407,6 @@ export class Workbench extends Disposable implements IPartService { let statusBarInGrid = this.gridHasView(this.statusbarPartView); let titlebarInGrid = this.gridHasView(this.titlebarPartView); - // Hide parts - if (this.panelHidden) { - this.panelPartView.hide(); - } - - if (this.statusBarHidden) { - this.statusbarPartView.hide(); - } - - if (!this.isVisible(Parts.TITLEBAR_PART)) { - this.titlebarPartView.hide(); - } - - if (this.activityBarHidden) { - this.activitybarPartView.hide(); - } - - if (this.sideBarHidden) { - this.sidebarPartView.hide(); - } - // Add parts to grid if (!statusBarInGrid) { this.workbenchGrid.addView(this.statusbarPartView, Sizing.Split, this.editorPartView, Direction.Down); @@ -1450,7 +1433,36 @@ export class Workbench extends Disposable implements IPartService { panelInGrid = true; } + // Hide parts + if (this.panelHidden) { + this.panelPartView.hide(); + } + + if (this.statusBarHidden) { + this.statusbarPartView.hide(); + } + + if (!this.isVisible(Parts.TITLEBAR_PART)) { + this.titlebarPartView.hide(); + } + + if (this.activityBarHidden) { + this.activitybarPartView.hide(); + } + + if (this.sideBarHidden) { + this.sidebarPartView.hide(); + } + + if (this.editorHidden) { + this.editorPartView.hide(); + } + // Show visible parts + if (!this.editorHidden) { + this.editorPartView.show(); + } + if (!this.statusBarHidden) { this.statusbarPartView.show(); } @@ -1553,6 +1565,23 @@ export class Workbench extends Disposable implements IPartService { } } + setEditorHidden(hidden: boolean, skipLayout?: boolean): void { + if (!(this.workbenchGrid instanceof Grid)) { + return; + } + + this.editorHidden = hidden; + + // The editor and the panel cannot be hidden at the same time + if (this.editorHidden && this.panelHidden) { + this.setPanelHidden(false, true); + } + + if (!skipLayout) { + this.layout(); + } + } + setSideBarHidden(hidden: boolean, skipLayout?: boolean): void { this.sideBarHidden = hidden; this.sideBarVisibleContext.set(!hidden); @@ -1639,6 +1668,11 @@ export class Workbench extends Disposable implements IPartService { this.storageService.remove(Workbench.panelHiddenStorageKey, StorageScope.WORKSPACE); } + // The editor and panel cannot be hiddne at the same time + if (hidden && this.editorHidden) { + this.setEditorHidden(false, true); + } + // Layout if (!skipLayout) { if (this.workbenchGrid instanceof Grid) { diff --git a/src/vs/workbench/services/part/common/partService.ts b/src/vs/workbench/services/part/common/partService.ts index 23d75ea37e1..6c63edf6559 100644 --- a/src/vs/workbench/services/part/common/partService.ts +++ b/src/vs/workbench/services/part/common/partService.ts @@ -86,6 +86,12 @@ export interface IPartService { */ getTitleBarOffset(): number; + /** + * + * Set editor area hidden or not + */ + setEditorHidden(hidden: boolean): void; + /** * Set sidebar hidden or not */ diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index 6096db99a7c..3de18af03d2 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -492,6 +492,8 @@ export class TestPartService implements IPartService { return false; } + public setEditorHidden(_hidden: boolean): Promise { return Promise.resolve(null); } + public setSideBarHidden(_hidden: boolean): Promise { return Promise.resolve(null); } public isPanelHidden(): boolean { From 6f457c75ab1fda07f682f5e7659dbbd44ab03933 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 28 Jan 2019 17:05:50 -0800 Subject: [PATCH 263/274] Turn conpty off for next stable release Fixes #66158 --- .../parts/terminal/electron-browser/terminal.contribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts b/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts index 47070e0350d..fb600958c8b 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts @@ -285,7 +285,7 @@ configurationRegistry.registerConfiguration({ 'terminal.integrated.windowsEnableConpty': { description: nls.localize('terminal.integrated.windowsEnableConpty', "Whether to use ConPTY for Windows terminal process communication (requires Windows 10 build number 18309+). Winpty will be used if this is false."), type: 'boolean', - default: true + default: false } } }); From 81b9b09cd3e8c4d2e602b6e6375bd2863e79554a Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Mon, 28 Jan 2019 17:12:24 -0800 Subject: [PATCH 264/274] Update HTML language service for NPE --- extensions/html-language-features/server/package.json | 2 +- extensions/html-language-features/server/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/html-language-features/server/package.json b/extensions/html-language-features/server/package.json index b57df13fe19..e631b8e2db0 100644 --- a/extensions/html-language-features/server/package.json +++ b/extensions/html-language-features/server/package.json @@ -10,7 +10,7 @@ "main": "./out/htmlServerMain", "dependencies": { "vscode-css-languageservice": "^3.0.13-next.10", - "vscode-html-languageservice": "^2.1.11-next.8", + "vscode-html-languageservice": "^2.1.11-next.9", "vscode-languageserver": "^5.1.0", "vscode-languageserver-types": "^3.13.0", "vscode-nls": "^4.0.0", diff --git a/extensions/html-language-features/server/yarn.lock b/extensions/html-language-features/server/yarn.lock index 4918bf833a4..e0a65914a11 100644 --- a/extensions/html-language-features/server/yarn.lock +++ b/extensions/html-language-features/server/yarn.lock @@ -237,10 +237,10 @@ vscode-css-languageservice@^3.0.13-next.10: vscode-languageserver-types "^3.13.0" vscode-nls "^4.0.0" -vscode-html-languageservice@^2.1.11-next.8: - version "2.1.11-next.8" - resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-2.1.11-next.8.tgz#060c69163ad65ad50988841011b86e423fd0a6d3" - integrity sha512-K6jCKx0xPQHQ/poicbMvCKwWc+XxBzZlT74C5SLbDfS6llkVIBThoIl6qrJhDuAqPNmyFmaFbT4+YvfqBUdpAA== +vscode-html-languageservice@^2.1.11-next.9: + version "2.1.11-next.9" + resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-2.1.11-next.9.tgz#1471b945351a4b179bc9b995e9617f07ab12d647" + integrity sha512-iSQPafYHDv62dDpwXVmaJzuS3y8yuehPwtWEBC9QysNBm5LxrRWy293pC7b9CehVly6mDnrLVXSRfgwAtg9xzA== dependencies: vscode-languageserver-types "^3.13.0" vscode-nls "^4.0.0" From e9708716f24c01d6caec596c960556ec5b034d2e Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Mon, 28 Jan 2019 17:14:28 -0800 Subject: [PATCH 265/274] Move html/css custom data contribution to experimental --- extensions/css-language-features/client/src/customData.ts | 2 +- extensions/html-language-features/client/src/customData.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/css-language-features/client/src/customData.ts b/extensions/css-language-features/client/src/customData.ts index c9954de8c51..8ba0770235a 100644 --- a/extensions/css-language-features/client/src/customData.ts +++ b/extensions/css-language-features/client/src/customData.ts @@ -43,7 +43,7 @@ export function getCustomDataPathsFromAllExtensions(): string[] { for (const extension of extensions.all) { const contributes = extension.packageJSON && extension.packageJSON.contributes; - if (contributes && contributes.css && contributes.css.customData && Array.isArray(contributes.css.customData)) { + if (contributes && contributes.css && contributes.css.experimental.customData && Array.isArray(contributes.css.experimental.customData)) { const relativePaths: string[] = contributes.css.customData; relativePaths.forEach(rp => { dataPaths.push(path.resolve(extension.extensionPath, rp)); diff --git a/extensions/html-language-features/client/src/customData.ts b/extensions/html-language-features/client/src/customData.ts index 467940bc736..8e9bf2863b6 100644 --- a/extensions/html-language-features/client/src/customData.ts +++ b/extensions/html-language-features/client/src/customData.ts @@ -45,7 +45,7 @@ export function getCustomDataPathsFromAllExtensions(): string[] { for (const extension of extensions.all) { const contributes = extension.packageJSON && extension.packageJSON.contributes; - if (contributes && contributes.html && contributes.html.customData && Array.isArray(contributes.html.customData)) { + if (contributes && contributes.html && contributes.html.experimental.customData && Array.isArray(contributes.html.experimental.customData)) { const relativePaths: string[] = contributes.html.customData; relativePaths.forEach(rp => { dataPaths.push(path.resolve(extension.extensionPath, rp)); From 13aff1eff21f71b29e8daf83eb7e07516a56f3d3 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 28 Jan 2019 17:12:05 -0800 Subject: [PATCH 266/274] Add workaround for TS not supporting providePrefixAndSuffixTextForRename not being supported per-file Workaround for https://github.com/Microsoft/TypeScript/issues/29585 --- .../src/features/fileConfigurationManager.ts | 2 +- .../src/typescriptServiceClient.ts | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/extensions/typescript-language-features/src/features/fileConfigurationManager.ts b/extensions/typescript-language-features/src/features/fileConfigurationManager.ts index 0091aa1dac5..676e80bafd1 100644 --- a/extensions/typescript-language-features/src/features/fileConfigurationManager.ts +++ b/extensions/typescript-language-features/src/features/fileConfigurationManager.ts @@ -183,7 +183,7 @@ export default class FileConfigurationManager extends Disposable { allowTextChangesInNewFiles: document.uri.scheme === 'file', providePrefixAndSuffixTextForRename: true, allowRenameOfImportPath: true, - } as Proto.UserPreferences; + }; } } diff --git a/extensions/typescript-language-features/src/typescriptServiceClient.ts b/extensions/typescript-language-features/src/typescriptServiceClient.ts index 4328948b23e..134942e4696 100644 --- a/extensions/typescript-language-features/src/typescriptServiceClient.ts +++ b/extensions/typescript-language-features/src/typescriptServiceClient.ts @@ -423,6 +423,10 @@ export default class TypeScriptServiceClient extends Disposable implements IType private serviceStarted(resendModels: boolean): void { const configureOptions: Proto.ConfigureRequestArguments = { hostInfo: 'vscode', + preferences: { + providePrefixAndSuffixTextForRename: true, + allowRenameOfImportPath: true, + } }; this.executeWithoutWaitingForResponse('configure', configureOptions); this.setCompilerOptionsForInferredProjects(this._configuration); From 248aea7fd51a703a7ab94ae5a582c85c11fbff33 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 28 Jan 2019 17:16:35 -0800 Subject: [PATCH 267/274] Fix some spelling errors in api docs --- src/vs/vscode.d.ts | 22 +++++++++++----------- src/vs/vscode.proposed.d.ts | 8 ++++---- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 6bfa75c148c..b2d1771d638 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -70,7 +70,7 @@ declare module 'vscode' { /** * The offset of the first character which is not a whitespace character as defined - * by `/\s/`. **Note** that if a line is all whitespaces the length of the line is returned. + * by `/\s/`. **Note** that if a line is all whitespace the length of the line is returned. */ readonly firstNonWhitespaceCharacterIndex: number; @@ -3218,7 +3218,7 @@ declare module 'vscode' { /** * List of characters that re-trigger signature help. * - * These trigger characters are only active when signature help is alread showing. All trigger characters + * These trigger characters are only active when signature help is already showing. All trigger characters * are also counted as re-trigger characters. */ readonly retriggerCharacters: ReadonlyArray; @@ -4681,7 +4681,7 @@ declare module 'vscode' { storagePath: string | undefined; /** - * An absolute file path in which the extension can store gloabal state. + * An absolute file path in which the extension can store global state. * The directory might not exist on disk and creation is * up to the extension. However, the parent directory is guaranteed to be existent. * @@ -5633,7 +5633,7 @@ declare module 'vscode' { * * @param source The existing file. * @param destination The destination location. - * @param options Defines if existing files should be overwriten. + * @param options Defines if existing files should be overwritten. * @throws [`FileNotFound`](#FileSystemError.FileNotFound) when `source` doesn't exist. * @throws [`FileNotFound`](#FileSystemError.FileNotFound) when parent of `destination` doesn't exist, e.g. no mkdirp-logic required. * @throws [`FileExists`](#FileSystemError.FileExists) when `destination` exists and when the `overwrite` option is not `true`. @@ -5855,7 +5855,7 @@ 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. * @@ -5863,7 +5863,7 @@ declare module 'vscode' { * serializer must restore the webview's `.html` and hook up all webview events. * @param state Persisted state from the webview content. * - * @return Thanble indicating that the webview has been fully restored. + * @return Thenble indicating that the webview has been fully restored. */ deserializeWebviewPanel(webviewPanel: WebviewPanel, state: any): Thenable; } @@ -6926,13 +6926,13 @@ declare module 'vscode' { } /** - * A light-weight user input UI that is intially not visible. After + * A light-weight user input UI that is initially not visible. After * configuring it through its properties the extension can make it * visible by calling [QuickInput.show](#QuickInput.show). * * There are several reasons why this UI might have to be hidden and * the extension will be notified through [QuickInput.onDidHide](#QuickInput.onDidHide). - * (Examples include: an explict call to [QuickInput.hide](#QuickInput.hide), + * (Examples include: an explicit call to [QuickInput.hide](#QuickInput.hide), * the user pressing Esc, some other input UI opening, etc.) * * A user pressing Enter or some other gesture implying acceptance @@ -7001,7 +7001,7 @@ declare module 'vscode' { * * There are several reasons why this UI might have to be hidden and * the extension will be notified through [QuickInput.onDidHide](#QuickInput.onDidHide). - * (Examples include: an explict call to [QuickInput.hide](#QuickInput.hide), + * (Examples include: an explicit call to [QuickInput.hide](#QuickInput.hide), * the user pressing Esc, some other input UI opening, etc.) */ onDidHide: Event; @@ -7495,7 +7495,7 @@ declare module 'vscode' { * cause failure of the operation. * * When applying a workspace edit that consists only of text edits an 'all-or-nothing'-strategy is used. - * A workspace edit with resource creations or deletions aborts the operation, e.g. consective edits will + * A workspace edit with resource creations or deletions aborts the operation, e.g. consecutive edits will * not be attempted, when a single edit fails. * * @param edit A workspace edit. @@ -8534,7 +8534,7 @@ declare module 'vscode' { */ onWillStopSession?(): void; /** - * An error with the debug adapter has occured. + * An error with the debug adapter has occurred. */ onError?(error: Error): void; /** diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 43a627b4c93..62aabce3dba 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -26,7 +26,7 @@ declare module 'vscode' { static readonly Empty: SelectionRangeKind; /** - * The statment kind, its value is `statement`, possible extensions can be + * The statement kind, its value is `statement`, possible extensions can be * `statement.if` etc */ static readonly Statement: SelectionRangeKind; @@ -915,7 +915,7 @@ declare module 'vscode' { /** * Represents a terminal without a process where all interaction and output in the terminal is * controlled by an extension. This is similar to an output window but has the same VT sequence - * compatility as the regular terminal. + * compatibility as the regular terminal. * * Note that an instance of [Terminal](#Terminal) will be created when a TerminalRenderer is * created with all its APIs available for use by extensions. When using the Terminal object @@ -963,7 +963,7 @@ declare module 'vscode' { readonly maximumDimensions: TerminalDimensions | undefined; /** - * The corressponding [Terminal](#Terminal) for this TerminalRenderer. + * The corresponding [Terminal](#Terminal) for this TerminalRenderer. */ readonly terminal: Terminal; @@ -995,7 +995,7 @@ declare module 'vscode' { * ```typescript * const terminalRenderer = window.createTerminalRenderer('test'); * terminalRenderer.onDidAcceptInput(data => { - * cosole.log(data); // 'Hello world' + * console.log(data); // 'Hello world' * }); * terminalRenderer.terminal.then(t => t.sendText('Hello world')); * ``` From c37d497f2b8df58aab6ac6555cda3540a1925b61 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 28 Jan 2019 18:12:32 -0800 Subject: [PATCH 268/274] Mark add missing async as auto fixable --- extensions/typescript-language-features/src/features/quickFix.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions/typescript-language-features/src/features/quickFix.ts b/extensions/typescript-language-features/src/features/quickFix.ts index 24866ac9403..91c3f3b3265 100644 --- a/extensions/typescript-language-features/src/features/quickFix.ts +++ b/extensions/typescript-language-features/src/features/quickFix.ts @@ -307,6 +307,7 @@ const preferredFixes = new Set([ 'annotateWithTypeFromJSDoc', 'constructorForDerivedNeedSuperCall', 'extendsInterfaceBecomesImplements', + 'fixAwaitInSyncFunction', 'fixClassIncorrectlyImplementsInterface', 'fixUnreachableCode', 'forgottenThisPropertyAccess', From ccb98c2c9bb6aa3af3cb4a27be80cea88ecee37f Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 28 Jan 2019 21:52:52 -0800 Subject: [PATCH 269/274] Fix TOC items expanding on second settings editor open --- .../workbench/parts/preferences/browser/tocTree.ts | 12 ++++++++++-- .../preferences/electron-browser/settingsEditor2.ts | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/parts/preferences/browser/tocTree.ts b/src/vs/workbench/parts/preferences/browser/tocTree.ts index a30929be24b..72acc8e1417 100644 --- a/src/vs/workbench/parts/preferences/browser/tocTree.ts +++ b/src/vs/workbench/parts/preferences/browser/tocTree.ts @@ -126,15 +126,23 @@ class TOCTreeDelegate implements IListVirtualDelegate { } } -export function createTOCIterator(model: TOCTreeModel | SettingsTreeGroupElement): Iterator> { +export function createTOCIterator(model: TOCTreeModel | SettingsTreeGroupElement, tree: TOCTree): Iterator> { const groupChildren = model.children.filter(c => c instanceof SettingsTreeGroupElement); const groupsIt = Iterator.fromArray(groupChildren); + return Iterator.map(groupsIt, g => { + let nodeExists = true; + try { tree.getNode(g); } catch (e) { nodeExists = false; } + + const hasGroupChildren = g.children.some(c => c instanceof SettingsTreeGroupElement); + return { element: g, + collapsed: nodeExists ? undefined : true, + collapsible: hasGroupChildren, children: g instanceof SettingsTreeGroupElement ? - createTOCIterator(g) : + createTOCIterator(g, tree) : undefined }; }); diff --git a/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts b/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts index 1d084f3521f..9f52f2b6459 100644 --- a/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts +++ b/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts @@ -967,7 +967,7 @@ export class SettingsEditor2 extends BaseEditor { } private refreshTOCTree(): void { - this.tocTree.setChildren(null, createTOCIterator(this.tocTreeModel)); + this.tocTree.setChildren(null, createTOCIterator(this.tocTreeModel, this.tocTree)); } private updateModifiedLabelForKey(key: string): void { From acc7365e41f9cb380e79bf78d7d102184f1ffe0d Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 29 Jan 2019 05:53:32 +0000 Subject: [PATCH 270/274] Enable type to find in search view --- .../parts/search/browser/media/searchview.css | 1 + .../parts/search/browser/searchResultsView.ts | 9 +++++++-- .../workbench/parts/search/browser/searchView.ts | 15 ++++++++++++++- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/parts/search/browser/media/searchview.css b/src/vs/workbench/parts/search/browser/media/searchview.css index fd591c75b55..9538b2816c4 100644 --- a/src/vs/workbench/parts/search/browser/media/searchview.css +++ b/src/vs/workbench/parts/search/browser/media/searchview.css @@ -177,6 +177,7 @@ line-height: 22px; display: flex; overflow: hidden; + padding-left: 22px; } .search-view .linematch > .match { diff --git a/src/vs/workbench/parts/search/browser/searchResultsView.ts b/src/vs/workbench/parts/search/browser/searchResultsView.ts index 18a8d159b78..499495c2387 100644 --- a/src/vs/workbench/parts/search/browser/searchResultsView.ts +++ b/src/vs/workbench/parts/search/browser/searchResultsView.ts @@ -26,6 +26,7 @@ import { IResourceLabel, ResourceLabels } from 'vs/workbench/browser/labels'; import { RemoveAction, ReplaceAction, ReplaceAllAction, ReplaceAllInFolderAction } from 'vs/workbench/parts/search/browser/searchActions'; import { SearchView } from 'vs/workbench/parts/search/browser/searchView'; import { FileMatch, FolderMatch, Match, RenderableMatch, SearchModel, BaseFolderMatch } from 'vs/workbench/parts/search/common/searchModel'; +import { createMatches } from 'vs/base/common/filters'; interface IFolderMatchTemplate { label: IResourceLabel; @@ -162,7 +163,7 @@ export class FileMatchRenderer extends Disposable implements ITreeRenderer, index: number, templateData: IFileMatchTemplate): void { const fileMatch = node.element; templateData.el.setAttribute('data-resource', fileMatch.resource().toString()); - templateData.label.setFile(fileMatch.resource(), { hideIcon: false }); + templateData.label.setFile( + fileMatch.resource(), { + hideIcon: false, + matches: createMatches(node.filterData) + }); const count = fileMatch.count(); templateData.badge.setCount(count); templateData.badge.setTitleFormat(count > 1 ? nls.localize('searchMatches', "{0} matches found", count) : nls.localize('searchMatch', "{0} match found", count)); diff --git a/src/vs/workbench/parts/search/browser/searchView.ts b/src/vs/workbench/parts/search/browser/searchView.ts index 31f506bd990..3260f6cee2e 100644 --- a/src/vs/workbench/parts/search/browser/searchView.ts +++ b/src/vs/workbench/parts/search/browser/searchView.ts @@ -62,6 +62,8 @@ import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorG import { IPartService } from 'vs/workbench/services/part/common/partService'; import { IPreferencesService, ISettingsEditorOptions } from 'vs/workbench/services/preferences/common/preferences'; import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; +import { createFileIconThemableTreeContainerScope } from 'vs/workbench/browser/parts/views/views'; +import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; const $ = dom.$; @@ -143,10 +145,11 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { @IUntitledEditorService private readonly untitledEditorService: IUntitledEditorService, @IPreferencesService private readonly preferencesService: IPreferencesService, @IThemeService protected themeService: IThemeService, + @IWorkbenchThemeService protected workbenchThemeService: IWorkbenchThemeService, @ISearchHistoryService private readonly searchHistoryService: ISearchHistoryService, @IEditorGroupsService private readonly editorGroupsService: IEditorGroupsService, @IContextMenuService private readonly contextMenuService: IContextMenuService, - @IMenuService private readonly menuService: IMenuService + @IMenuService private readonly menuService: IMenuService, ) { super(VIEW_ID, configurationService, partService, telemetryService, themeService, storageService); @@ -615,6 +618,7 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { private createSearchResultsView(container: HTMLElement): void { this.resultsElement = dom.append(container, $('.results.show-file-icons')); const delegate = this.instantiationService.createInstance(SearchDelegate); + this._register(createFileIconThemableTreeContainerScope(this.resultsElement, this.workbenchThemeService)); const identityProvider: IIdentityProvider = { getId(element: RenderableMatch) { @@ -632,6 +636,15 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { this._register(this.instantiationService.createInstance(MatchRenderer, this.viewModel, this)), ], { + keyboardNavigationLabelProvider: { + getKeyboardNavigationLabel: (element: RenderableMatch) => { + if (element instanceof FolderMatch || element instanceof FileMatch) { + return element.name(); + } + + return ''; + } + }, identityProvider, accessibilityProvider: this.instantiationService.createInstance(SearchAccessibilityProvider, this.viewModel) })); From b21209d92c4934dc0a3664a71a36c1acc9a2f20a Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 29 Jan 2019 07:45:09 +0100 Subject: [PATCH 271/274] fix grid view bug --- src/vs/base/browser/ui/grid/gridview.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/browser/ui/grid/gridview.ts b/src/vs/base/browser/ui/grid/gridview.ts index a3f483632d0..05fdd8ce033 100644 --- a/src/vs/base/browser/ui/grid/gridview.ts +++ b/src/vs/base/browser/ui/grid/gridview.ts @@ -414,7 +414,7 @@ class LeafNode implements ISplitView, IDisposable { ) { this._orthogonalSize = orthogonalSize; - this._onDidViewChange = Event.map(this.view.onDidChange, this.orientation === Orientation.HORIZONTAL ? e => e && e.width : e => e && e.height); + this._onDidViewChange = Event.map(this.view.onDidChange, this.orientation === Orientation.VERTICAL ? e => e && e.width : e => e && e.height); this.onDidChange = Event.any(this._onDidViewChange, this._onDidSetLinkedNode.event, this._onDidLinkedWidthNodeChange.event, this._onDidLinkedHeightNodeChange.event); } From af608864808988ed5c6ef7259514d9386864f2b6 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 29 Jan 2019 09:02:24 +0100 Subject: [PATCH 272/274] grid: should check orientation at the time of the event --- src/vs/base/browser/ui/grid/gridview.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/browser/ui/grid/gridview.ts b/src/vs/base/browser/ui/grid/gridview.ts index 05fdd8ce033..9011a578e51 100644 --- a/src/vs/base/browser/ui/grid/gridview.ts +++ b/src/vs/base/browser/ui/grid/gridview.ts @@ -414,7 +414,7 @@ class LeafNode implements ISplitView, IDisposable { ) { this._orthogonalSize = orthogonalSize; - this._onDidViewChange = Event.map(this.view.onDidChange, this.orientation === Orientation.VERTICAL ? e => e && e.width : e => e && e.height); + this._onDidViewChange = Event.map(this.view.onDidChange, e => e && (this.orientation === Orientation.VERTICAL ? e.width : e.height)); this.onDidChange = Event.any(this._onDidViewChange, this._onDidSetLinkedNode.event, this._onDidLinkedWidthNodeChange.event, this._onDidLinkedHeightNodeChange.event); } From c2ee16954d9a31997c000a154ea102cda984422c Mon Sep 17 00:00:00 2001 From: Dirk Baeumer Date: Tue, 29 Jan 2019 11:39:01 +0100 Subject: [PATCH 273/274] Ran OSS Tool --- ThirdPartyNotices.txt | 212 ++++++++++++++++++------------------------ 1 file changed, 88 insertions(+), 124 deletions(-) diff --git a/ThirdPartyNotices.txt b/ThirdPartyNotices.txt index 4541dbbb24d..91876a89245 100644 --- a/ThirdPartyNotices.txt +++ b/ThirdPartyNotices.txt @@ -5,47 +5,47 @@ Do Not Translate or Localize This project incorporates components from the projects listed below. The original copyright notices and the licenses under which Microsoft received such components are set forth below. Microsoft reserves all rights not expressly granted herein, whether by implication, estoppel or otherwise. -1. atom/language-c (https://github.com/atom/language-c) -2. atom/language-clojure (https://github.com/atom/language-clojure) -3. atom/language-coffee-script (https://github.com/atom/language-coffee-script) +1. atom/language-c version 0.58.1 (https://github.com/atom/language-c) +2. atom/language-clojure version 0.22.6 (https://github.com/atom/language-clojure) +3. atom/language-coffee-script version 0.49.3 (https://github.com/atom/language-coffee-script) 4. atom/language-css (https://github.com/atom/language-css) -5. atom/language-java (https://github.com/atom/language-java) -6. atom/language-objective-c (https://github.com/atom/language-objective-c) -7. atom/language-sass version 0.52.0 (https://github.com/atom/language-sass) -8. atom/language-shellscript (https://github.com/atom/language-shellscript) -9. atom/language-xml (https://github.com/atom/language-xml) +5. atom/language-java version 0.31.1 (https://github.com/atom/language-java) +6. atom/language-objective-c version 0.15.0 (https://github.com/atom/language-objective-c) +7. atom/language-sass version 0.61.4 (https://github.com/atom/language-sass) +8. atom/language-shellscript version 0.26.0 (https://github.com/atom/language-shellscript) +9. atom/language-xml version 0.35.2 (https://github.com/atom/language-xml) 10. chriskempson/tomorrow-theme (https://github.com/chriskempson/tomorrow-theme) 11. Colorsublime-Themes version 0.1.0 (https://github.com/Colorsublime/Colorsublime-Themes) -12. daaain/Handlebars (https://github.com/daaain/Handlebars) +12. daaain/Handlebars version 1.7.1 (https://github.com/daaain/Handlebars) 13. davidrios/pug-tmbundle (https://github.com/davidrios/pug-tmbundle) 14. definitelytyped (https://github.com/DefinitelyTyped/DefinitelyTyped) -15. demyte/language-cshtml (https://github.com/demyte/language-cshtml) +15. demyte/language-cshtml version 0.3.0 (https://github.com/demyte/language-cshtml) 16. Document Object Model version 4.0.0 (https://www.w3.org/DOM/) 17. dotnet/csharp-tmLanguage version 0.1.0 (https://github.com/dotnet/csharp-tmLanguage) 18. expand-abbreviation version 0.5.8 (https://github.com/emmetio/expand-abbreviation) 19. fadeevab/make.tmbundle (https://github.com/fadeevab/make.tmbundle) 20. freebroccolo/atom-language-swift (https://github.com/freebroccolo/atom-language-swift) 21. HTML 5.1 W3C Working Draft version 08 October 2015 (http://www.w3.org/TR/2015/WD-html51-20151008/) -22. Ikuyadeu/vscode-R (https://github.com/Ikuyadeu/vscode-R) +22. Ikuyadeu/vscode-R version 0.5.5 (https://github.com/Ikuyadeu/vscode-R) 23. Ionic documentation version 1.2.4 (https://github.com/ionic-team/ionic-site) 24. ionide/ionide-fsgrammar (https://github.com/ionide/ionide-fsgrammar) 25. js-beautify version 1.6.8 (https://github.com/beautify-web/js-beautify) 26. Jxck/assert version 1.0.0 (https://github.com/Jxck/assert) 27. language-docker (https://github.com/moby/moby) -28. language-go version 0.39.0 (https://github.com/atom/language-go) -29. language-less (https://github.com/atom/language-less) -30. language-php (https://github.com/atom/language-php) -31. language-rust version 0.4.9 (https://github.com/zargony/atom-language-rust) -32. MagicStack/MagicPython (https://github.com/MagicStack/MagicPython) +28. language-go version 0.44.3 (https://github.com/atom/language-go) +29. language-less version 0.34.2 (https://github.com/atom/language-less) +30. language-php version 0.43.2 (https://github.com/atom/language-php) +31. language-rust version 0.4.12 (https://github.com/zargony/atom-language-rust) +32. MagicStack/MagicPython version 1.1.1 (https://github.com/MagicStack/MagicPython) 33. marked version 0.5.0 (https://github.com/markedjs/marked) 34. mdn-data version 1.1.12 (https://github.com/mdn/data) 35. Microsoft/TypeScript-TmLanguage version 0.0.1 (https://github.com/Microsoft/TypeScript-TmLanguage) 36. Microsoft/vscode-JSON.tmLanguage (https://github.com/Microsoft/vscode-JSON.tmLanguage) -37. Microsoft/vscode-mssql (https://github.com/Microsoft/vscode-mssql) -38. mmims/language-batchfile (https://github.com/mmims/language-batchfile) -39. octicons-code version 3.1.0 (https://registry.npmjs.org/octicons/-/octicons-3.1.0.tgz) -40. octicons-font version 3.1.0 (https://registry.npmjs.org/octicons/-/octicons-3.1.0.tgz) -41. PowerShell/EditorSyntax (https://github.com/powershell/editorsyntax) +37. Microsoft/vscode-mssql version 1.4.0 (https://github.com/Microsoft/vscode-mssql) +38. mmims/language-batchfile version 0.7.4 (https://github.com/mmims/language-batchfile) +39. octicons version 8.3.0 (https://github.com/primer/octicons) +40. PowerShell/EditorSyntax (https://github.com/powershell/editorsyntax) +41. promise-polyfill version 8.0.0 (https://github.com/taylorhakes/promise-polyfill) 42. seti-ui version 0.1.0 (https://github.com/jesseweed/seti-ui) 43. shaders-tmLanguage version 0.1.0 (https://github.com/tgjones/shaders-tmLanguage) 44. textmate/asp.vb.net.tmbundle (https://github.com/textmate/asp.vb.net.tmbundle) @@ -62,10 +62,12 @@ This project incorporates components from the projects listed below. The origina 55. textmate/ruby.tmbundle (https://github.com/textmate/ruby.tmbundle) 56. textmate/yaml.tmbundle (https://github.com/textmate/yaml.tmbundle) 57. TypeScript-TmLanguage version 0.1.8 (https://github.com/Microsoft/TypeScript-TmLanguage) -58. Unicode version 12.0.0 (http://www.unicode.org/) -59. vscode-logfile-highlighter version 1.2.0 (https://github.com/emilast/vscode-logfile-highlighter) -60. vscode-swift version 0.0.1 (https://github.com/owensd/vscode-swift) -61. Web Background Synchronization (https://github.com/WICG/BackgroundSync) +58. TypeScript-TmLanguage version 1.0.0 (https://github.com/Microsoft/TypeScript-TmLanguage) +59. Unicode version 12.0.0 (http://www.unicode.org/) +60. vscode-logfile-highlighter version 2.4.1 (https://github.com/emilast/vscode-logfile-highlighter) +61. vscode-octicons-font version 1.0.0 (https://github.com/Microsoft/vscode-octicons-font) +62. vscode-swift version 0.0.1 (https://github.com/owensd/vscode-swift) +63. Web Background Synchronization (https://github.com/WICG/BackgroundSync) %% atom/language-c NOTICES AND INFORMATION BEGIN HERE @@ -1800,11 +1802,11 @@ THE SOFTWARE. ========================================= END OF mmims/language-batchfile NOTICES AND INFORMATION -%% octicons-code NOTICES AND INFORMATION BEGIN HERE +%% octicons NOTICES AND INFORMATION BEGIN HERE ========================================= -The MIT License (MIT) +MIT License -(c) 2012-2015 GitHub +Copyright (c) 2019 GitHub Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -1813,109 +1815,18 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. ========================================= -END OF octicons-code NOTICES AND INFORMATION - -%% octicons-font NOTICES AND INFORMATION BEGIN HERE -========================================= -(c) 2012-2015 GitHub - -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. -========================================= -END OF octicons-font NOTICES AND INFORMATION +END OF octicons NOTICES AND INFORMATION %% PowerShell/EditorSyntax NOTICES AND INFORMATION BEGIN HERE ========================================= @@ -1945,6 +1856,33 @@ SOFTWARE. ========================================= END OF PowerShell/EditorSyntax NOTICES AND INFORMATION +%% promise-polyfill NOTICES AND INFORMATION BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) 2014 Taylor Hakes +Copyright (c) 2014 Forbes Lindesay + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF promise-polyfill NOTICES AND INFORMATION + %% seti-ui NOTICES AND INFORMATION BEGIN HERE ========================================= Copyright (c) 2014 Jesse Weed @@ -2357,6 +2295,32 @@ SOFTWARE. ========================================= END OF vscode-logfile-highlighter NOTICES AND INFORMATION +%% vscode-octicons-font NOTICES AND INFORMATION BEGIN HERE +========================================= +MIT License + + Copyright (c) Microsoft Corporation. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE +========================================= +END OF vscode-octicons-font NOTICES AND INFORMATION + %% vscode-swift NOTICES AND INFORMATION BEGIN HERE ========================================= The MIT License (MIT) From 9c510825d6a7f8b9d428c35378dc4adb1900dd6e Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 29 Jan 2019 12:13:56 +0100 Subject: [PATCH 274/274] fix #67368 --- src/vs/workbench/browser/actions/layoutActions.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/workbench/browser/actions/layoutActions.ts b/src/vs/workbench/browser/actions/layoutActions.ts index 14585858c91..70bafe35ada 100644 --- a/src/vs/workbench/browser/actions/layoutActions.ts +++ b/src/vs/workbench/browser/actions/layoutActions.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import 'vs/css!./media/actions'; + import * as nls from 'vs/nls'; import { Registry } from 'vs/platform/registry/common/platform'; import { Action } from 'vs/base/common/actions';